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;