Loading core/java/android/app/CameraCompatTaskInfo.java +33 −2 Original line number Diff line number Diff line Loading @@ -18,6 +18,8 @@ package android.app; import static android.app.WindowConfiguration.ROTATION_UNDEFINED; import static android.view.Surface.ROTATION_0; import static android.view.Surface.ROTATION_180; import static android.view.Surface.ROTATION_270; import static android.view.Surface.ROTATION_90; import android.annotation.IntDef; Loading Loading @@ -87,9 +89,19 @@ public class CameraCompatTaskInfo implements Parcelable { * <p>This field is used by the WM and the camera framework, to coordinate camera compat mode * setup. */ // TODO(b/414347702): Revisit data structure. @FreeformCameraCompatMode public int freeformCameraCompatMode; /** * Real display rotation, never affected by camera compat sandboxing. * * <p>This value is used by the Camera Framework to calculate rotate-and-crop rotation degrees. */ // TODO(b/414347702): Revisit data structure. @Surface.Rotation public int displayRotation; private CameraCompatTaskInfo() { // Do nothing } Loading Loading @@ -126,6 +138,7 @@ public class CameraCompatTaskInfo implements Parcelable { */ void readFromParcel(Parcel source) { freeformCameraCompatMode = source.readInt(); displayRotation = source.readInt(); } /** Loading @@ -134,6 +147,7 @@ public class CameraCompatTaskInfo implements Parcelable { @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(freeformCameraCompatMode); dest.writeInt(displayRotation); } /** Loading @@ -144,7 +158,8 @@ public class CameraCompatTaskInfo implements Parcelable { if (that == null) { return false; } return freeformCameraCompatMode == that.freeformCameraCompatMode; return freeformCameraCompatMode == that.freeformCameraCompatMode && displayRotation == that.displayRotation; } /** Loading @@ -154,13 +169,15 @@ public class CameraCompatTaskInfo implements Parcelable { if (that == null) { return false; } return freeformCameraCompatMode == that.freeformCameraCompatMode; return freeformCameraCompatMode == that.freeformCameraCompatMode && displayRotation == that.displayRotation; } @Override public String toString() { return "CameraCompatTaskInfo { freeformCameraCompatMode=" + freeformCameraCompatModeToString(freeformCameraCompatMode) + displayRotationToString(displayRotation) + "}"; } Loading Loading @@ -204,4 +221,18 @@ public class CameraCompatTaskInfo implements Parcelable { "Unexpected camera compat mode: " + freeformCameraCompatMode); }; } /** Human readable version of the freeform camera compat mode. */ @NonNull public static String displayRotationToString(@Surface.Rotation int displayRotation) { return switch (displayRotation) { case ROTATION_UNDEFINED -> "undefined"; case ROTATION_0 -> "0"; case ROTATION_90 -> "90"; case ROTATION_180 -> "180"; case ROTATION_270 -> "270"; default -> throw new AssertionError( "Unexpected display rotation: " + displayRotation); }; } } core/java/android/hardware/camera2/CameraManager.java +40 −3 Original line number Diff line number Diff line Loading @@ -20,6 +20,8 @@ import static android.companion.virtual.VirtualDeviceParams.DEVICE_POLICY_DEFAUL import static android.companion.virtual.VirtualDeviceParams.POLICY_TYPE_CAMERA; import static android.content.Context.DEVICE_ID_DEFAULT; import static android.content.Context.DEVICE_ID_INVALID; import static android.view.Surface.ROTATION_270; import static android.view.Surface.ROTATION_90; import android.annotation.CallbackExecutor; import android.annotation.FlaggedApi; Loading Loading @@ -79,6 +81,7 @@ import android.util.Log; import android.util.Pair; import android.util.Size; import android.view.Display; import android.view.Surface; import android.window.DesktopModeFlags; import com.android.internal.camera.flags.Flags; Loading Loading @@ -211,6 +214,16 @@ public final class CameraManager { public static final int ROTATION_OVERRIDE_ROTATION_ONLY = ICameraService.ROTATION_OVERRIDE_ROTATION_ONLY; /** * Crops and rotates landscape camera feed to portrait counterclockwise, but doesn't change * sensor orientation. * * @hide */ // TODO(b/414347702): Revisit data structure. static final int ROTATION_OVERRIDE_ROTATION_ONLY_REVERSE = ICameraService.ROTATION_OVERRIDE_ROTATION_ONLY_REVERSE; /** * Enable physical camera availability callbacks when the logical camera is unavailable * Loading Loading @@ -1719,7 +1732,8 @@ public final class CameraManager { && taskInfo.topActivity != null && taskInfo.topActivity.getPackageName().equals(packageName)) { // WindowManager has requested rotation override. return getRotationOverrideForCompatFreeform(freeformCameraCompatMode); return getRotationOverrideForCompatFreeform(freeformCameraCompatMode, taskInfo.appCompatTaskInfo.cameraCompatTaskInfo.displayRotation); } } } Loading Loading @@ -1749,13 +1763,36 @@ public final class CameraManager { } private static int getRotationOverrideForCompatFreeform( @CameraCompatTaskInfo.FreeformCameraCompatMode int freeformCameraCompatMode) { @CameraCompatTaskInfo.FreeformCameraCompatMode int freeformCameraCompatMode, @Surface.Rotation int displayRotation) { // Only rotate-and-crop if the app and device orientations do not match. if (freeformCameraCompatMode == CameraCompatTaskInfo.CAMERA_COMPAT_FREEFORM_LANDSCAPE_DEVICE_IN_PORTRAIT || freeformCameraCompatMode == CameraCompatTaskInfo.CAMERA_COMPAT_FREEFORM_PORTRAIT_DEVICE_IN_LANDSCAPE) { // Rotate-and-crop compensates for changes in camera preview calculations (sandboxing). // Recommended calculation of camera preview is: // rotation = (sensorOrientationDegrees - deviceOrientationDegrees * sign + 360) % 360 // (camera-facing - sign - is accounted for later). // If any of the parameters above are changed, rotate-and-crop should be applied to // equal the changed amount. // For example, with real display rotation 90 sandboxed to 0, rotate-and-crop by 270 // degrees (-90) for back camera, and 90 for front camera. // Use `displayRotation` param, sent by WindowManager, as the display rotation in the // app process might be sandboxed. if (displayRotation == ROTATION_90 && com.android.window.flags.Flags .enableCameraCompatCheckDeviceRotationBugfix()) { // back camera: 270 degrees, front camera: 90 degrees return ICameraService.ROTATION_OVERRIDE_ROTATION_ONLY_REVERSE; } else if (displayRotation == ROTATION_270) { // back camera: 90 degrees, front camera: 270 degrees return ICameraService.ROTATION_OVERRIDE_ROTATION_ONLY; } else { // TODO(b/390183440): differentiate between LANDSCAPE and REVERSE_LANDSCAPE // requested orientation for landscape apps. 'displayRotation` is 0 or 180 (rare) // in either case. return ICameraService.ROTATION_OVERRIDE_ROTATION_ONLY; } } else { return ICameraService.ROTATION_OVERRIDE_NONE; } Loading services/core/java/com/android/server/wm/AppCompatCameraPolicy.java +10 −0 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package com.android.server.wm; import static android.app.WindowConfiguration.ROTATION_UNDEFINED; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; import static com.android.server.wm.AppCompatConfiguration.MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO; Loading @@ -25,6 +26,7 @@ import android.annotation.Nullable; import android.app.CameraCompatTaskInfo; import android.content.pm.ActivityInfo.ScreenOrientation; import android.content.res.Configuration; import android.view.Surface; import android.widget.Toast; import android.window.DesktopModeFlags; Loading Loading @@ -271,6 +273,14 @@ class AppCompatCameraPolicy { : CameraCompatTaskInfo.CAMERA_COMPAT_FREEFORM_NONE; } @Surface.Rotation static int getCameraDeviceRotation(@NonNull ActivityRecord activity) { final AppCompatCameraPolicy cameraPolicy = getAppCompatCameraPolicy(activity); return cameraPolicy != null && cameraPolicy.mCameraCompatFreeformPolicy != null ? cameraPolicy.mCameraCompatFreeformPolicy.getCameraDeviceRotation() : ROTATION_UNDEFINED; } /** * Whether we should apply the min aspect ratio per-app override only when an app is connected * to the camera. Loading services/core/java/com/android/server/wm/AppCompatUtils.java +4 −0 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package com.android.server.wm; import static android.app.WindowConfiguration.ROTATION_UNDEFINED; import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; import static android.content.res.Configuration.ORIENTATION_PORTRAIT; Loading Loading @@ -235,6 +236,9 @@ final class AppCompatUtils { appCompatTaskInfo.setEligibleForUserAspectRatioButton(eligibleForAspectRatioButton); appCompatTaskInfo.cameraCompatTaskInfo.freeformCameraCompatMode = AppCompatCameraPolicy.getCameraCompatFreeformMode(top); appCompatTaskInfo.cameraCompatTaskInfo.displayRotation = Flags.enableCameraCompatCheckDeviceRotationBugfix() ? AppCompatCameraPolicy.getCameraDeviceRotation(top) : ROTATION_UNDEFINED; appCompatTaskInfo.setHasMinAspectRatioOverride(top.mAppCompatController .getDesktopAspectRatioPolicy().hasMinAspectRatioOverride(task)); appCompatTaskInfo.setOptOutEdgeToEdge(top.mOptOutEdgeToEdge); Loading services/core/java/com/android/server/wm/CameraCompatFreeformPolicy.java +5 −0 Original line number Diff line number Diff line Loading @@ -93,6 +93,11 @@ final class CameraCompatFreeformPolicy implements CameraStateMonitor.CameraCompa mIsRunning = true; } int getCameraDeviceRotation() { // TODO(b/276432441): Check device orientation when running on an external display. return mDisplayContent.getRotation(); } /** Releases camera callback listener. */ void dispose() { mCameraStateMonitor.removeCameraStateListener(this); Loading Loading
core/java/android/app/CameraCompatTaskInfo.java +33 −2 Original line number Diff line number Diff line Loading @@ -18,6 +18,8 @@ package android.app; import static android.app.WindowConfiguration.ROTATION_UNDEFINED; import static android.view.Surface.ROTATION_0; import static android.view.Surface.ROTATION_180; import static android.view.Surface.ROTATION_270; import static android.view.Surface.ROTATION_90; import android.annotation.IntDef; Loading Loading @@ -87,9 +89,19 @@ public class CameraCompatTaskInfo implements Parcelable { * <p>This field is used by the WM and the camera framework, to coordinate camera compat mode * setup. */ // TODO(b/414347702): Revisit data structure. @FreeformCameraCompatMode public int freeformCameraCompatMode; /** * Real display rotation, never affected by camera compat sandboxing. * * <p>This value is used by the Camera Framework to calculate rotate-and-crop rotation degrees. */ // TODO(b/414347702): Revisit data structure. @Surface.Rotation public int displayRotation; private CameraCompatTaskInfo() { // Do nothing } Loading Loading @@ -126,6 +138,7 @@ public class CameraCompatTaskInfo implements Parcelable { */ void readFromParcel(Parcel source) { freeformCameraCompatMode = source.readInt(); displayRotation = source.readInt(); } /** Loading @@ -134,6 +147,7 @@ public class CameraCompatTaskInfo implements Parcelable { @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(freeformCameraCompatMode); dest.writeInt(displayRotation); } /** Loading @@ -144,7 +158,8 @@ public class CameraCompatTaskInfo implements Parcelable { if (that == null) { return false; } return freeformCameraCompatMode == that.freeformCameraCompatMode; return freeformCameraCompatMode == that.freeformCameraCompatMode && displayRotation == that.displayRotation; } /** Loading @@ -154,13 +169,15 @@ public class CameraCompatTaskInfo implements Parcelable { if (that == null) { return false; } return freeformCameraCompatMode == that.freeformCameraCompatMode; return freeformCameraCompatMode == that.freeformCameraCompatMode && displayRotation == that.displayRotation; } @Override public String toString() { return "CameraCompatTaskInfo { freeformCameraCompatMode=" + freeformCameraCompatModeToString(freeformCameraCompatMode) + displayRotationToString(displayRotation) + "}"; } Loading Loading @@ -204,4 +221,18 @@ public class CameraCompatTaskInfo implements Parcelable { "Unexpected camera compat mode: " + freeformCameraCompatMode); }; } /** Human readable version of the freeform camera compat mode. */ @NonNull public static String displayRotationToString(@Surface.Rotation int displayRotation) { return switch (displayRotation) { case ROTATION_UNDEFINED -> "undefined"; case ROTATION_0 -> "0"; case ROTATION_90 -> "90"; case ROTATION_180 -> "180"; case ROTATION_270 -> "270"; default -> throw new AssertionError( "Unexpected display rotation: " + displayRotation); }; } }
core/java/android/hardware/camera2/CameraManager.java +40 −3 Original line number Diff line number Diff line Loading @@ -20,6 +20,8 @@ import static android.companion.virtual.VirtualDeviceParams.DEVICE_POLICY_DEFAUL import static android.companion.virtual.VirtualDeviceParams.POLICY_TYPE_CAMERA; import static android.content.Context.DEVICE_ID_DEFAULT; import static android.content.Context.DEVICE_ID_INVALID; import static android.view.Surface.ROTATION_270; import static android.view.Surface.ROTATION_90; import android.annotation.CallbackExecutor; import android.annotation.FlaggedApi; Loading Loading @@ -79,6 +81,7 @@ import android.util.Log; import android.util.Pair; import android.util.Size; import android.view.Display; import android.view.Surface; import android.window.DesktopModeFlags; import com.android.internal.camera.flags.Flags; Loading Loading @@ -211,6 +214,16 @@ public final class CameraManager { public static final int ROTATION_OVERRIDE_ROTATION_ONLY = ICameraService.ROTATION_OVERRIDE_ROTATION_ONLY; /** * Crops and rotates landscape camera feed to portrait counterclockwise, but doesn't change * sensor orientation. * * @hide */ // TODO(b/414347702): Revisit data structure. static final int ROTATION_OVERRIDE_ROTATION_ONLY_REVERSE = ICameraService.ROTATION_OVERRIDE_ROTATION_ONLY_REVERSE; /** * Enable physical camera availability callbacks when the logical camera is unavailable * Loading Loading @@ -1719,7 +1732,8 @@ public final class CameraManager { && taskInfo.topActivity != null && taskInfo.topActivity.getPackageName().equals(packageName)) { // WindowManager has requested rotation override. return getRotationOverrideForCompatFreeform(freeformCameraCompatMode); return getRotationOverrideForCompatFreeform(freeformCameraCompatMode, taskInfo.appCompatTaskInfo.cameraCompatTaskInfo.displayRotation); } } } Loading Loading @@ -1749,13 +1763,36 @@ public final class CameraManager { } private static int getRotationOverrideForCompatFreeform( @CameraCompatTaskInfo.FreeformCameraCompatMode int freeformCameraCompatMode) { @CameraCompatTaskInfo.FreeformCameraCompatMode int freeformCameraCompatMode, @Surface.Rotation int displayRotation) { // Only rotate-and-crop if the app and device orientations do not match. if (freeformCameraCompatMode == CameraCompatTaskInfo.CAMERA_COMPAT_FREEFORM_LANDSCAPE_DEVICE_IN_PORTRAIT || freeformCameraCompatMode == CameraCompatTaskInfo.CAMERA_COMPAT_FREEFORM_PORTRAIT_DEVICE_IN_LANDSCAPE) { // Rotate-and-crop compensates for changes in camera preview calculations (sandboxing). // Recommended calculation of camera preview is: // rotation = (sensorOrientationDegrees - deviceOrientationDegrees * sign + 360) % 360 // (camera-facing - sign - is accounted for later). // If any of the parameters above are changed, rotate-and-crop should be applied to // equal the changed amount. // For example, with real display rotation 90 sandboxed to 0, rotate-and-crop by 270 // degrees (-90) for back camera, and 90 for front camera. // Use `displayRotation` param, sent by WindowManager, as the display rotation in the // app process might be sandboxed. if (displayRotation == ROTATION_90 && com.android.window.flags.Flags .enableCameraCompatCheckDeviceRotationBugfix()) { // back camera: 270 degrees, front camera: 90 degrees return ICameraService.ROTATION_OVERRIDE_ROTATION_ONLY_REVERSE; } else if (displayRotation == ROTATION_270) { // back camera: 90 degrees, front camera: 270 degrees return ICameraService.ROTATION_OVERRIDE_ROTATION_ONLY; } else { // TODO(b/390183440): differentiate between LANDSCAPE and REVERSE_LANDSCAPE // requested orientation for landscape apps. 'displayRotation` is 0 or 180 (rare) // in either case. return ICameraService.ROTATION_OVERRIDE_ROTATION_ONLY; } } else { return ICameraService.ROTATION_OVERRIDE_NONE; } Loading
services/core/java/com/android/server/wm/AppCompatCameraPolicy.java +10 −0 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package com.android.server.wm; import static android.app.WindowConfiguration.ROTATION_UNDEFINED; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; import static com.android.server.wm.AppCompatConfiguration.MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO; Loading @@ -25,6 +26,7 @@ import android.annotation.Nullable; import android.app.CameraCompatTaskInfo; import android.content.pm.ActivityInfo.ScreenOrientation; import android.content.res.Configuration; import android.view.Surface; import android.widget.Toast; import android.window.DesktopModeFlags; Loading Loading @@ -271,6 +273,14 @@ class AppCompatCameraPolicy { : CameraCompatTaskInfo.CAMERA_COMPAT_FREEFORM_NONE; } @Surface.Rotation static int getCameraDeviceRotation(@NonNull ActivityRecord activity) { final AppCompatCameraPolicy cameraPolicy = getAppCompatCameraPolicy(activity); return cameraPolicy != null && cameraPolicy.mCameraCompatFreeformPolicy != null ? cameraPolicy.mCameraCompatFreeformPolicy.getCameraDeviceRotation() : ROTATION_UNDEFINED; } /** * Whether we should apply the min aspect ratio per-app override only when an app is connected * to the camera. Loading
services/core/java/com/android/server/wm/AppCompatUtils.java +4 −0 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package com.android.server.wm; import static android.app.WindowConfiguration.ROTATION_UNDEFINED; import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; import static android.content.res.Configuration.ORIENTATION_PORTRAIT; Loading Loading @@ -235,6 +236,9 @@ final class AppCompatUtils { appCompatTaskInfo.setEligibleForUserAspectRatioButton(eligibleForAspectRatioButton); appCompatTaskInfo.cameraCompatTaskInfo.freeformCameraCompatMode = AppCompatCameraPolicy.getCameraCompatFreeformMode(top); appCompatTaskInfo.cameraCompatTaskInfo.displayRotation = Flags.enableCameraCompatCheckDeviceRotationBugfix() ? AppCompatCameraPolicy.getCameraDeviceRotation(top) : ROTATION_UNDEFINED; appCompatTaskInfo.setHasMinAspectRatioOverride(top.mAppCompatController .getDesktopAspectRatioPolicy().hasMinAspectRatioOverride(task)); appCompatTaskInfo.setOptOutEdgeToEdge(top.mOptOutEdgeToEdge); Loading
services/core/java/com/android/server/wm/CameraCompatFreeformPolicy.java +5 −0 Original line number Diff line number Diff line Loading @@ -93,6 +93,11 @@ final class CameraCompatFreeformPolicy implements CameraStateMonitor.CameraCompa mIsRunning = true; } int getCameraDeviceRotation() { // TODO(b/276432441): Check device orientation when running on an external display. return mDisplayContent.getRotation(); } /** Releases camera callback listener. */ void dispose() { mCameraStateMonitor.removeCameraStateListener(this); Loading