Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f3b34910 authored by Mina Granic's avatar Mina Granic
Browse files

Check display rotation for rotateAndCrop.

As display and sensor orientation are set to 0 and 90
respectively, rotateAndCrop needs to align with the
current rotation of the device to result in a correct
calculation:
rotation = (sensorOrientationDegrees - deviceOrientationDegrees * sign + 360) % 360

Flag: com.android.window.flags.enable_camera_compat_check_device_rotation_bugfix
Fixes: 395047103
Test: manual
Change-Id: I490bec90f2b5130265d68fec2a01aa1e9f25d4c8
parent 2e071263
Loading
Loading
Loading
Loading
+33 −2
Original line number Diff line number Diff line
@@ -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;
@@ -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
    }
@@ -126,6 +138,7 @@ public class CameraCompatTaskInfo implements Parcelable {
     */
    void readFromParcel(Parcel source) {
        freeformCameraCompatMode = source.readInt();
        displayRotation = source.readInt();
    }

    /**
@@ -134,6 +147,7 @@ public class CameraCompatTaskInfo implements Parcelable {
    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeInt(freeformCameraCompatMode);
        dest.writeInt(displayRotation);
    }

    /**
@@ -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;
    }

    /**
@@ -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)
                + "}";
    }

@@ -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);
        };
    }
}
+40 −3
Original line number Diff line number Diff line
@@ -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;
@@ -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;
@@ -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
     *
@@ -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);
                    }
                }
            }
@@ -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;
        }
+10 −0
Original line number Diff line number Diff line
@@ -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;
@@ -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;

@@ -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.
+4 −0
Original line number Diff line number Diff line
@@ -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;
@@ -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);
+5 −0
Original line number Diff line number Diff line
@@ -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