[6/n] Camera Compat: Orientation override for camera only

Adds OVERRIDE_ORIENTATION_ONLY_FOR_CAMERA that acts as a modifier for the existing orientation overrides allowing to limit them to when the app is connected to the camera.

Fix: 226051012
Test: atest WmTests:LetterboxUiControllerTest
Change-Id: Iac7ea582dcdb996fc416576d886ab92c2d48c225
This commit is contained in:
Mariia Sandrikova
2023-01-30 17:09:55 +00:00
parent 46133ca915
commit ccc0c90b11
6 changed files with 112 additions and 11 deletions

View File

@@ -1237,6 +1237,18 @@ public class ActivityInfo extends ComponentInfo implements Parcelable {
@Overridable
public static final long OVERRIDE_ANY_ORIENTATION = 265464455L;
/**
* When enabled, activates OVERRIDE_LANDSCAPE_ORIENTATION_TO_REVERSE_LANDSCAPE,
* OVERRIDE_UNDEFINED_ORIENTATION_TO_NOSENSOR and OVERRIDE_UNDEFINED_ORIENTATION_TO_PORTRAIT
* only when an app is connected to the camera. See
* com.android.server.wm.DisplayRotationCompatPolicy for more context.
* @hide
*/
@ChangeId
@Disabled
@Overridable
public static final long OVERRIDE_ORIENTATION_ONLY_FOR_CAMERA = 265456536L;
/**
* This override fixes display orientation to landscape natural orientation when a task is
* fullscreen. While display rotation is fixed to landscape, the orientation requested by the

View File

@@ -308,6 +308,10 @@ final class DisplayRotationCompatPolicy {
&& mDisplayContent.getDisplay().getType() == TYPE_INTERNAL;
}
boolean isActivityEligibleForOrientationOverride(@NonNull ActivityRecord activity) {
return isTreatmentEnabledForDisplay() && isCameraActiveInFullscreen(activity);
}
/**
* Whether camera compat treatment is applicable for the given activity.
*
@@ -319,12 +323,16 @@ final class DisplayRotationCompatPolicy {
* </ul>
*/
boolean isTreatmentEnabledForActivity(@Nullable ActivityRecord activity) {
return activity != null && !activity.inMultiWindowMode()
return activity != null && isCameraActiveInFullscreen(activity)
&& activity.getRequestedConfigurationOrientation() != ORIENTATION_UNDEFINED
// "locked" and "nosensor" values are often used by camera apps that can't
// handle dynamic changes so we shouldn't force rotate them.
&& activity.getOverrideOrientation() != SCREEN_ORIENTATION_NOSENSOR
&& activity.getOverrideOrientation() != SCREEN_ORIENTATION_LOCKED
&& activity.getOverrideOrientation() != SCREEN_ORIENTATION_LOCKED;
}
private boolean isCameraActiveInFullscreen(@NonNull ActivityRecord activity) {
return !activity.inMultiWindowMode()
&& mCameraIdPackageBiMap.containsPackageName(activity.packageName)
&& activity.mLetterboxUiController.shouldForceRotateForCameraCompat();
}
@@ -369,6 +377,9 @@ final class DisplayRotationCompatPolicy {
// Checking whether an activity in fullscreen rather than the task as this camera compat
// treatment doesn't cover activity embedding.
if (topActivity.getWindowingMode() == WINDOWING_MODE_FULLSCREEN) {
if (topActivity.mLetterboxUiController.isOverrideOrientationOnlyForCameraEnabled()) {
topActivity.recomputeConfiguration();
}
updateOrientationWithWmLock();
return;
}
@@ -423,6 +434,17 @@ final class DisplayRotationCompatPolicy {
ProtoLog.v(WM_DEBUG_ORIENTATION,
"Display id=%d is notified that Camera %s is closed, updating rotation.",
mDisplayContent.mDisplayId, cameraId);
ActivityRecord topActivity = mDisplayContent.topRunningActivity(
/* considerKeyguardState= */ true);
if (topActivity == null
// Checking whether an activity in fullscreen rather than the task as this camera
// compat treatment doesn't cover activity embedding.
|| topActivity.getWindowingMode() != WINDOWING_MODE_FULLSCREEN) {
return;
}
if (topActivity.mLetterboxUiController.isOverrideOrientationOnlyForCameraEnabled()) {
topActivity.recomputeConfiguration();
}
updateOrientationWithWmLock();
}

View File

@@ -19,6 +19,7 @@ package com.android.server.wm;
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.LetterboxConfigurationDeviceConfig.KEY_ALLOW_IGNORE_ORIENTATION_REQUEST;
import static com.android.server.wm.LetterboxConfigurationDeviceConfig.KEY_ENABLE_CAMERA_COMPAT_TREATMENT;
import static com.android.server.wm.LetterboxConfigurationDeviceConfig.KEY_ENABLE_DISPLAY_ROTATION_IMMERSIVE_APP_COMPAT_POLICY;
import android.annotation.IntDef;
@@ -309,6 +310,9 @@ final class LetterboxConfiguration {
mIsDisplayRotationImmersiveAppCompatPolicyEnabled = mContext.getResources().getBoolean(
R.bool.config_letterboxIsDisplayRotationImmersiveAppCompatPolicyEnabled);
mDeviceConfig.updateFlagActiveStatus(
/* isActive */ mIsCameraCompatTreatmentEnabled,
/* key */ KEY_ENABLE_CAMERA_COMPAT_TREATMENT);
mDeviceConfig.updateFlagActiveStatus(
/* isActive */ mIsDisplayRotationImmersiveAppCompatPolicyEnabled,
/* key */ KEY_ENABLE_DISPLAY_ROTATION_IMMERSIVE_APP_COMPAT_POLICY);
@@ -1118,15 +1122,8 @@ final class LetterboxConfiguration {
/** Whether camera compatibility treatment is enabled. */
boolean isCameraCompatTreatmentEnabled(boolean checkDeviceConfig) {
return mIsCameraCompatTreatmentEnabled
&& (!checkDeviceConfig || isCameraCompatTreatmentAllowed());
}
// TODO(b/262977416): Cache a runtime flag and implement
// DeviceConfig.OnPropertiesChangedListener
private static boolean isCameraCompatTreatmentAllowed() {
return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_WINDOW_MANAGER,
"enable_compat_camera_treatment", true);
return mIsCameraCompatTreatmentEnabled && (!checkDeviceConfig
|| mDeviceConfig.getFlag(KEY_ENABLE_CAMERA_COMPAT_TREATMENT));
}
/** Whether camera compatibility refresh is enabled. */

View File

@@ -33,6 +33,9 @@ import java.util.concurrent.Executor;
final class LetterboxConfigurationDeviceConfig
implements DeviceConfig.OnPropertiesChangedListener {
static final String KEY_ENABLE_CAMERA_COMPAT_TREATMENT = "enable_compat_camera_treatment";
private static final boolean DEFAULT_VALUE_ENABLE_CAMERA_COMPAT_TREATMENT = true;
static final String KEY_ENABLE_DISPLAY_ROTATION_IMMERSIVE_APP_COMPAT_POLICY =
"enable_display_rotation_immersive_app_compat_policy";
private static final boolean DEFAULT_VALUE_ENABLE_DISPLAY_ROTATION_IMMERSIVE_APP_COMPAT_POLICY =
@@ -44,12 +47,19 @@ final class LetterboxConfigurationDeviceConfig
@VisibleForTesting
static final Map<String, Boolean> sKeyToDefaultValueMap = Map.of(
KEY_ENABLE_CAMERA_COMPAT_TREATMENT,
DEFAULT_VALUE_ENABLE_CAMERA_COMPAT_TREATMENT,
KEY_ENABLE_DISPLAY_ROTATION_IMMERSIVE_APP_COMPAT_POLICY,
DEFAULT_VALUE_ENABLE_DISPLAY_ROTATION_IMMERSIVE_APP_COMPAT_POLICY,
KEY_ALLOW_IGNORE_ORIENTATION_REQUEST,
DEFAULT_VALUE_ALLOW_IGNORE_ORIENTATION_REQUEST
);
// Whether camera compatibility treatment is enabled.
// See DisplayRotationCompatPolicy for context.
private boolean mIsCameraCompatTreatmentEnabled =
DEFAULT_VALUE_ENABLE_CAMERA_COMPAT_TREATMENT;
// Whether enabling rotation compat policy for immersive apps that prevents auto rotation
// into non-optimal screen orientation while in fullscreen. This is needed because immersive
// apps, such as games, are often not optimized for all orientations and can have a poor UX
@@ -101,6 +111,8 @@ final class LetterboxConfigurationDeviceConfig
*/
boolean getFlag(String key) {
switch (key) {
case KEY_ENABLE_CAMERA_COMPAT_TREATMENT:
return mIsCameraCompatTreatmentEnabled;
case KEY_ENABLE_DISPLAY_ROTATION_IMMERSIVE_APP_COMPAT_POLICY:
return mIsDisplayRotationImmersiveAppCompatPolicyEnabled;
case KEY_ALLOW_IGNORE_ORIENTATION_REQUEST:
@@ -116,6 +128,10 @@ final class LetterboxConfigurationDeviceConfig
throw new AssertionError("Haven't found default value for flag: " + key);
}
switch (key) {
case KEY_ENABLE_CAMERA_COMPAT_TREATMENT:
mIsCameraCompatTreatmentEnabled =
getDeviceConfig(key, defaultValue);
break;
case KEY_ENABLE_DISPLAY_ROTATION_IMMERSIVE_APP_COMPAT_POLICY:
mIsDisplayRotationImmersiveAppCompatPolicyEnabled =
getDeviceConfig(key, defaultValue);

View File

@@ -23,6 +23,7 @@ import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_DISABLE_REF
import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE;
import static android.content.pm.ActivityInfo.OVERRIDE_ENABLE_COMPAT_IGNORE_REQUESTED_ORIENTATION;
import static android.content.pm.ActivityInfo.OVERRIDE_LANDSCAPE_ORIENTATION_TO_REVERSE_LANDSCAPE;
import static android.content.pm.ActivityInfo.OVERRIDE_ORIENTATION_ONLY_FOR_CAMERA;
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.OVERRIDE_USE_DISPLAY_LANDSCAPE_NATURAL_ORIENTATION;
@@ -136,6 +137,8 @@ final class LetterboxUiController {
private final boolean mIsOverrideToNosensorOrientationEnabled;
// Corresponds to OVERRIDE_LANDSCAPE_ORIENTATION_TO_REVERSE_LANDSCAPE
private final boolean mIsOverrideToReverseLandscapeOrientationEnabled;
// Corresponds to OVERRIDE_ORIENTATION_ONLY_FOR_CAMERA
private final boolean mIsOverrideOrientationOnlyForCameraEnabled;
// Corresponds to OVERRIDE_USE_DISPLAY_LANDSCAPE_NATURAL_ORIENTATION
private final boolean mIsOverrideUseDisplayLandscapeNaturalOrientationEnabled;
@@ -253,6 +256,8 @@ final class LetterboxUiController {
isCompatChangeEnabled(OVERRIDE_LANDSCAPE_ORIENTATION_TO_REVERSE_LANDSCAPE);
mIsOverrideToNosensorOrientationEnabled =
isCompatChangeEnabled(OVERRIDE_UNDEFINED_ORIENTATION_TO_NOSENSOR);
mIsOverrideOrientationOnlyForCameraEnabled =
isCompatChangeEnabled(OVERRIDE_ORIENTATION_ONLY_FOR_CAMERA);
mIsOverrideUseDisplayLandscapeNaturalOrientationEnabled =
isCompatChangeEnabled(OVERRIDE_USE_DISPLAY_LANDSCAPE_NATURAL_ORIENTATION);
@@ -410,6 +415,14 @@ final class LetterboxUiController {
return candidate;
}
DisplayContent displayContent = mActivityRecord.mDisplayContent;
if (mIsOverrideOrientationOnlyForCameraEnabled && displayContent != null
&& (displayContent.mDisplayRotationCompatPolicy == null
|| !displayContent.mDisplayRotationCompatPolicy
.isActivityEligibleForOrientationOverride(mActivityRecord))) {
return candidate;
}
if (mIsOverrideToReverseLandscapeOrientationEnabled
&& (isFixedOrientationLandscape(candidate) || mIsOverrideAnyOrientationEnabled)) {
Slog.w(TAG, "Requested orientation " + screenOrientationToString(candidate) + " for "
@@ -439,6 +452,10 @@ final class LetterboxUiController {
return candidate;
}
boolean isOverrideOrientationOnlyForCameraEnabled() {
return mIsOverrideOrientationOnlyForCameraEnabled;
}
/**
* Whether activity is eligible for activity "refresh" after camera compat force rotation
* treatment. See {@link DisplayRotationCompatPolicy} for context.

View File

@@ -22,6 +22,7 @@ import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_DISABLE_REF
import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE;
import static android.content.pm.ActivityInfo.OVERRIDE_ENABLE_COMPAT_IGNORE_REQUESTED_ORIENTATION;
import static android.content.pm.ActivityInfo.OVERRIDE_LANDSCAPE_ORIENTATION_TO_REVERSE_LANDSCAPE;
import static android.content.pm.ActivityInfo.OVERRIDE_ORIENTATION_ONLY_FOR_CAMERA;
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.OVERRIDE_USE_DISPLAY_LANDSCAPE_NATURAL_ORIENTATION;
@@ -576,6 +577,42 @@ public class LetterboxUiControllerTest extends WindowTestsBase {
/* candidate */ SCREEN_ORIENTATION_UNSPECIFIED), SCREEN_ORIENTATION_UNSPECIFIED);
}
@Test
@EnableCompatChanges({OVERRIDE_UNDEFINED_ORIENTATION_TO_PORTRAIT,
OVERRIDE_ORIENTATION_ONLY_FOR_CAMERA})
public void testOverrideOrientationIfNeeded_whenCameraNotActive_returnsUnchanged() {
doReturn(true).when(mLetterboxConfiguration).isCameraCompatTreatmentEnabled(anyBoolean());
// Recreate DisplayContent with DisplayRotationCompatPolicy
mActivity = setUpActivityWithComponent();
mController = new LetterboxUiController(mWm, mActivity);
spyOn(mDisplayContent.mDisplayRotationCompatPolicy);
doReturn(false).when(mDisplayContent.mDisplayRotationCompatPolicy)
.isActivityEligibleForOrientationOverride(eq(mActivity));
assertEquals(mController.overrideOrientationIfNeeded(
/* candidate */ SCREEN_ORIENTATION_UNSPECIFIED), SCREEN_ORIENTATION_UNSPECIFIED);
}
@Test
@EnableCompatChanges({OVERRIDE_UNDEFINED_ORIENTATION_TO_PORTRAIT,
OVERRIDE_ORIENTATION_ONLY_FOR_CAMERA})
public void testOverrideOrientationIfNeeded_whenCameraActive_returnsPortrait() {
doReturn(true).when(mLetterboxConfiguration).isCameraCompatTreatmentEnabled(anyBoolean());
// Recreate DisplayContent with DisplayRotationCompatPolicy
mActivity = setUpActivityWithComponent();
mController = new LetterboxUiController(mWm, mActivity);
spyOn(mDisplayContent.mDisplayRotationCompatPolicy);
doReturn(true).when(mDisplayContent.mDisplayRotationCompatPolicy)
.isActivityEligibleForOrientationOverride(eq(mActivity));
assertEquals(mController.overrideOrientationIfNeeded(
/* candidate */ SCREEN_ORIENTATION_UNSPECIFIED), SCREEN_ORIENTATION_PORTRAIT);
}
// shouldUseDisplayLandscapeNaturalOrientation
@Test