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:
*
*
*
@@ -1290,6 +1316,33 @@ public final class CameraManager {
*
*
*
+ * If {@link android.content.pm.ApplicationInfo#targetSdkVersion targetSdkVersion}
+ * ≥ {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE}:
+ *
+ *
+ *
+ * - 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.
+ *
+ * - 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.
+ *
+ *
+ *
+ * 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:
*
*
*
@@ -1326,6 +1380,33 @@ public final class CameraManager {
*
*
*
+ * If {@link android.content.pm.ApplicationInfo#targetSdkVersion targetSdkVersion}
+ * ≥ {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE}:
+ *
+ *
+ *
+ * - 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.
+ *
+ * - 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.
+ *
+ *
+ *
+ * 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;