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

Commit a781e864 authored by Shuzhen Wang's avatar Shuzhen Wang
Browse files

Camera: Physical camera avail callbacks can happen for unavail camera

Allow onPhysicalCameraAvailable and onPhysicalCameraUnavailable be
called when the logical camera is unavailable.

This makes it possible for the application using the logical camera to
listen to the physical camera callback.

Gate the behavior change based on targetSdkVersion and app compat
framework.

Test: Camera CTS
Bug: 244358506
Bug: 266649122
Change-Id: I37bf6b721c3a13a67f08a573d688cfd5256e7afd
parent 144c5157
Loading
Loading
Loading
Loading
+91 −8
Original line number Diff line number Diff line
@@ -131,6 +131,23 @@ public final class CameraManager {
    public static final String LANDSCAPE_TO_PORTRAIT_PROP =
            "camera.enable_landscape_to_portrait";

    /**
     * Enable physical camera availability callbacks when the logical camera is unavailable
     *
     * <p>Previously once a logical camera becomes unavailable, no {@link
     * #onPhysicalCameraAvailable} or {@link #onPhysicalCameraUnavailable} will be called until
     * the logical camera becomes available again. The results in the app opening the logical
     * camera not able to receive physical camera availability change.</p>
     *
     * <p>With this change, the {@link #onPhysicalCameraAvailable} and {@link
     * #onPhysicalCameraUnavailable} can still be called while the logical camera is unavailable.
     * </p>
     */
    @ChangeId
    @EnabledSince(targetSdkVersion = android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
    private static final long ENABLE_PHYSICAL_CAMERA_CALLBACK_FOR_UNAVAILABLE_LOGICAL_CAMERA =
            244358506L;

    /**
     * @hide
     */
@@ -1199,6 +1216,14 @@ public final class CameraManager {
        return CompatChanges.isChangeEnabled(OVERRIDE_CAMERA_LANDSCAPE_TO_PORTRAIT);
    }

    /**
     * @hide
     */
    public static boolean physicalCallbacksAreEnabledForUnavailableCamera() {
        return CompatChanges.isChangeEnabled(
                ENABLE_PHYSICAL_CAMERA_CALLBACK_FOR_UNAVAILABLE_LOGICAL_CAMERA);
    }

    /**
     * A callback for camera devices becoming available or unavailable to open.
     *
@@ -1276,9 +1301,10 @@ public final class CameraManager {
         * to begin with, {@link #onPhysicalCameraUnavailable} may be invoked after
         * {@link #onCameraAvailable}.</p>
         *
         * <p>Limitation: Opening a logical camera disables the {@link #onPhysicalCameraAvailable}
         * and {@link #onPhysicalCameraUnavailable} callbacks for its physical cameras. For example,
         * if app A opens the camera device:</p>
         * <p>If {@link android.content.pm.ApplicationInfo#targetSdkVersion targetSdkVersion}
         * &lt; {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE}, opening a logical camera
         * disables the {@link #onPhysicalCameraAvailable} and {@link #onPhysicalCameraUnavailable}
         * callbacks for its physical cameras. For example, if app A opens the camera device:</p>
         *
         * <ul>
         *
@@ -1290,6 +1316,33 @@ public final class CameraManager {
         *
         * </ul>
         *
         * <p>If {@link android.content.pm.ApplicationInfo#targetSdkVersion targetSdkVersion}
         * &ge; {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE}:</p>
         *
         * <ul>
         *
         * <li>A physical camera status change will trigger {@link #onPhysicalCameraAvailable}
         * or {@link #onPhysicalCameraUnavailable} even after the logical camera becomes
         * unavailable. A {@link #onCameraUnavailable} call for a logical camera doesn't reset the
         * physical cameras' availability status. This makes it possible for an application opening
         * the logical camera device to know which physical camera becomes unavailable or available
         * to use.</li>
         *
         * <li>Similar to {@link android.os.Build.VERSION_CODES#TIRAMISU Android 13} and earlier,
         * the logical camera's {@link #onCameraAvailable} callback implies all of its physical
         * cameras' status become available. {@link #onPhysicalCameraUnavailable} will be called
         * for any unavailable physical cameras upon the logical camera becoming available.</li>
         *
         * </ul>
         *
         * <p>Given the pipeline nature of the camera capture through {@link
         * android.hardware.camera2.CaptureRequest}, there may be frame drops if the application
         * requests images from a physical camera of a logical multi-camera and that physical camera
         * becomes unavailable. The application should stop requesting directly from an unavailable
         * physical camera as soon as {@link #onPhysicalCameraUnavailable} is received, and also be
         * ready to robustly handle frame drop errors for requests targeting physical cameras,
         * since those errors may arrive before the unavailability callback.</p>
         *
         * <p>The default implementation of this method does nothing.</p>
         *
         * @param cameraId The unique identifier of the logical multi-camera.
@@ -1312,9 +1365,10 @@ public final class CameraManager {
         * cameras of its parent logical multi-camera, when {@link #onCameraUnavailable} for
         * the logical multi-camera is invoked.</p>
         *
         * <p>Limitation: Opening a logical camera disables the {@link #onPhysicalCameraAvailable}
         * and {@link #onPhysicalCameraUnavailable} callbacks for its physical cameras. For example,
         * if app A opens the camera device:</p>
         * <p>If {@link android.content.pm.ApplicationInfo#targetSdkVersion targetSdkVersion}
         * &lt; {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE}, opening a logical camera
         * disables the {@link #onPhysicalCameraAvailable} and {@link #onPhysicalCameraUnavailable}
         * callbacks for its physical cameras. For example, if app A opens the camera device:</p>
         *
         * <ul>
         *
@@ -1326,6 +1380,33 @@ public final class CameraManager {
         *
         * </ul>
         *
         * <p>If {@link android.content.pm.ApplicationInfo#targetSdkVersion targetSdkVersion}
         * &ge; {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE}:</p>
         *
         * <ul>
         *
         * <li>A physical camera status change will trigger {@link #onPhysicalCameraAvailable}
         * or {@link #onPhysicalCameraUnavailable} even after the logical camera becomes
         * unavailable. A {@link #onCameraUnavailable} call for a logical camera doesn't reset the
         * physical cameras' availability status. This makes it possible for an application opening
         * the logical camera device to know which physical camera becomes unavailable or available
         * to use.</li>
         *
         * <li>Similar to {@link android.os.Build.VERSION_CODES#TIRAMISU Android 13} and earlier,
         * the logical camera's {@link #onCameraAvailable} callback implies all of its physical
         * cameras' status become available. {@link #onPhysicalCameraUnavailable} will be called
         * for any unavailable physical cameras upon the logical camera becoming available.</li>
         *
         * </ul>
         *
         * <p>Given the pipeline nature of the camera capture through {@link
         * android.hardware.camera2.CaptureRequest}, there may be frame drops if the application
         * requests images from a physical camera of a logical multi-camera and that physical camera
         * becomes unavailable. The application should stop requesting directly from an unavailable
         * physical camera as soon as {@link #onPhysicalCameraUnavailable} is received, and also be
         * ready to robustly handle frame drop errors for requests targeting physical cameras,
         * since those errors may arrive before the unavailability callback.</p>
         *
         * <p>The default implementation of this method does nothing.</p>
         *
         * @param cameraId The unique identifier of the logical multi-camera.
@@ -2289,7 +2370,8 @@ public final class CameraManager {
                postSingleUpdate(callback, executor, id, null /*physicalId*/, status);

                // Send the NOT_PRESENT state for unavailable physical cameras
                if (isAvailable(status) && mUnavailablePhysicalDevices.containsKey(id)) {
                if ((isAvailable(status) || physicalCallbacksAreEnabledForUnavailableCamera())
                        && mUnavailablePhysicalDevices.containsKey(id)) {
                    ArrayList<String> unavailableIds = mUnavailablePhysicalDevices.get(id);
                    for (String unavailableId : unavailableIds) {
                        postSingleUpdate(callback, executor, id, unavailableId,
@@ -2422,7 +2504,8 @@ public final class CameraManager {
                return;
            }

            if (!isAvailable(mDeviceStatus.get(id))) {
            if (!physicalCallbacksAreEnabledForUnavailableCamera()
                    && !isAvailable(mDeviceStatus.get(id))) {
                Log.i(TAG, String.format("Camera %s is not available. Ignore physical camera "
                        + "status change callback(s)", id));
                return;