From a781e86467e5ddba225aff37cd2ab687e1ccff29 Mon Sep 17 00:00:00 2001 From: Shuzhen Wang Date: Sat, 18 Feb 2023 02:12:04 +0000 Subject: [PATCH] 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 --- .../hardware/camera2/CameraManager.java | 99 +++++++++++++++++-- 1 file changed, 91 insertions(+), 8 deletions(-) diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java index fbc018444dc5c..39117a29d565c 100644 --- a/core/java/android/hardware/camera2/CameraManager.java +++ b/core/java/android/hardware/camera2/CameraManager.java @@ -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 + * + *

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.

+ * + *

With this change, the {@link #onPhysicalCameraAvailable} and {@link + * #onPhysicalCameraUnavailable} can still be called while the logical camera is unavailable. + *

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

* - *

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:

+ *

If {@link android.content.pm.ApplicationInfo#targetSdkVersion targetSdkVersion} + * < {@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:

* * * + *

If {@link android.content.pm.ApplicationInfo#targetSdkVersion targetSdkVersion} + * ≥ {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE}:

+ * + * + * + *

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.

+ * *

The default implementation of this method does nothing.

* * @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.

* - *

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:

+ *

If {@link android.content.pm.ApplicationInfo#targetSdkVersion targetSdkVersion} + * < {@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:

* * * + *

If {@link android.content.pm.ApplicationInfo#targetSdkVersion targetSdkVersion} + * ≥ {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE}:

+ * + * + * + *

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.

+ * *

The default implementation of this method does nothing.

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