Merge "camera2: Add CameraDeviceSetup" into main
diff --git a/core/api/current.txt b/core/api/current.txt
index 97f078b..7052fd1 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -19227,7 +19227,7 @@
method public int getCameraAudioRestriction() throws android.hardware.camera2.CameraAccessException;
method @NonNull public abstract String getId();
method @FlaggedApi("com.android.internal.camera.flags.feature_combination_query") @NonNull public android.hardware.camera2.CameraCharacteristics getSessionCharacteristics(@NonNull android.hardware.camera2.params.SessionConfiguration) throws android.hardware.camera2.CameraAccessException;
- method @Deprecated public boolean isSessionConfigurationSupported(@NonNull android.hardware.camera2.params.SessionConfiguration) throws android.hardware.camera2.CameraAccessException;
+ method public boolean isSessionConfigurationSupported(@NonNull android.hardware.camera2.params.SessionConfiguration) throws android.hardware.camera2.CameraAccessException;
method public void setCameraAudioRestriction(int) throws android.hardware.camera2.CameraAccessException;
field public static final int AUDIO_RESTRICTION_NONE = 0; // 0x0
field public static final int AUDIO_RESTRICTION_VIBRATION = 1; // 0x1
@@ -19240,6 +19240,13 @@
field public static final int TEMPLATE_ZERO_SHUTTER_LAG = 5; // 0x5
}
+ @FlaggedApi("com.android.internal.camera.flags.camera_device_setup") public abstract static class CameraDevice.CameraDeviceSetup {
+ method @FlaggedApi("com.android.internal.camera.flags.camera_device_setup") @NonNull public abstract android.hardware.camera2.CaptureRequest.Builder createCaptureRequest(int) throws android.hardware.camera2.CameraAccessException;
+ method @FlaggedApi("com.android.internal.camera.flags.camera_device_setup") @NonNull public abstract String getId();
+ method @FlaggedApi("com.android.internal.camera.flags.camera_device_setup") public abstract boolean isSessionConfigurationSupported(@NonNull android.hardware.camera2.params.SessionConfiguration) throws android.hardware.camera2.CameraAccessException;
+ method @FlaggedApi("com.android.internal.camera.flags.camera_device_setup") @RequiresPermission(android.Manifest.permission.CAMERA) public abstract void openCamera(@NonNull java.util.concurrent.Executor, @NonNull android.hardware.camera2.CameraDevice.StateCallback) throws android.hardware.camera2.CameraAccessException;
+ }
+
public abstract static class CameraDevice.StateCallback {
ctor public CameraDevice.StateCallback();
method public void onClosed(@NonNull android.hardware.camera2.CameraDevice);
@@ -19308,14 +19315,14 @@
}
public final class CameraManager {
- method @FlaggedApi("com.android.internal.camera.flags.feature_combination_query") @NonNull @RequiresPermission(android.Manifest.permission.CAMERA) public android.hardware.camera2.CaptureRequest.Builder createCaptureRequest(@NonNull String, int) throws android.hardware.camera2.CameraAccessException;
method @NonNull public android.hardware.camera2.CameraCharacteristics getCameraCharacteristics(@NonNull String) throws android.hardware.camera2.CameraAccessException;
+ method @FlaggedApi("com.android.internal.camera.flags.camera_device_setup") @NonNull public android.hardware.camera2.CameraDevice.CameraDeviceSetup getCameraDeviceSetup(@NonNull String) throws android.hardware.camera2.CameraAccessException;
method @NonNull public android.hardware.camera2.CameraExtensionCharacteristics getCameraExtensionCharacteristics(@NonNull String) throws android.hardware.camera2.CameraAccessException;
method @NonNull public String[] getCameraIdList() throws android.hardware.camera2.CameraAccessException;
method @NonNull public java.util.Set<java.util.Set<java.lang.String>> getConcurrentCameraIds() throws android.hardware.camera2.CameraAccessException;
method public int getTorchStrengthLevel(@NonNull String) throws android.hardware.camera2.CameraAccessException;
+ method @FlaggedApi("com.android.internal.camera.flags.camera_device_setup") public boolean isCameraDeviceSetupSupported(@NonNull String) throws android.hardware.camera2.CameraAccessException;
method @RequiresPermission(android.Manifest.permission.CAMERA) public boolean isConcurrentSessionConfigurationSupported(@NonNull java.util.Map<java.lang.String,android.hardware.camera2.params.SessionConfiguration>) throws android.hardware.camera2.CameraAccessException;
- method @FlaggedApi("com.android.internal.camera.flags.feature_combination_query") @RequiresPermission(android.Manifest.permission.CAMERA) public boolean isSessionConfigurationWithParametersSupported(@NonNull String, @NonNull android.hardware.camera2.params.SessionConfiguration) throws android.hardware.camera2.CameraAccessException;
method @RequiresPermission(android.Manifest.permission.CAMERA) public void openCamera(@NonNull String, @NonNull android.hardware.camera2.CameraDevice.StateCallback, @Nullable android.os.Handler) throws android.hardware.camera2.CameraAccessException;
method @RequiresPermission(android.Manifest.permission.CAMERA) public void openCamera(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.hardware.camera2.CameraDevice.StateCallback) throws android.hardware.camera2.CameraAccessException;
method public void registerAvailabilityCallback(@NonNull android.hardware.camera2.CameraManager.AvailabilityCallback, @Nullable android.os.Handler);
diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java
index 3835c52..1d14169 100644
--- a/core/java/android/hardware/camera2/CameraDevice.java
+++ b/core/java/android/hardware/camera2/CameraDevice.java
@@ -16,10 +16,12 @@
package android.hardware.camera2;
+import android.annotation.CallbackExecutor;
import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.hardware.camera2.params.ExtensionSessionConfiguration;
import android.hardware.camera2.params.InputConfiguration;
@@ -35,6 +37,7 @@
import java.lang.annotation.RetentionPolicy;
import java.util.List;
import java.util.Set;
+import java.util.concurrent.Executor;
/**
* <p>The CameraDevice class is a representation of a single camera connected to an
@@ -897,7 +900,7 @@
* supported sizes.
* Camera clients that register a Jpeg/R output within a stream combination that doesn't fit
* in the mandatory stream table above can call
- * {@link CameraManager#isSessionConfigurationWithParametersSupported} to ensure that this particular
+ * {@link #isSessionConfigurationSupported} to ensure that this particular
* configuration is supported.</p>
*
* <h5>STREAM_USE_CASE capability additional guaranteed configurations</h5>
@@ -970,7 +973,7 @@
*
* <p>Since the capabilities of camera devices vary greatly, a given camera device may support
* target combinations with sizes outside of these guarantees, but this can only be tested for
- * by calling {@link CameraManager#isSessionConfigurationWithParametersSupported} or attempting
+ * by calling {@link #isSessionConfigurationSupported} or attempting
* to create a session with such targets.</p>
*
* <p>Exception on 176x144 (QCIF) resolution:
@@ -1395,8 +1398,12 @@
* {@link android.hardware.camera2.params.MandatoryStreamCombination} are better suited for this
* purpose.</p>
*
- * <p>Note that session parameters will be ignored and calls to
- * {@link SessionConfiguration#setSessionParameters} are not required.</p>
+ * <p><b>NOTE:</b>
+ * For apps targeting {@link android.os.Build.VERSION_CODES#VANILLA_ICE_CREAM} and above,
+ * this method will ensure session parameters set through calls to
+ * {@link SessionConfiguration#setSessionParameters} are also supported if the Camera Device
+ * supports it. For apps targeting {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE} and
+ * below, session parameters will be ignored.</p>
*
* @return {@code true} if the given session configuration is supported by the camera device
* {@code false} otherwise.
@@ -1406,10 +1413,8 @@
* @throws CameraAccessException if the camera device is no longer connected or has
* encountered a fatal error
* @throws IllegalStateException if the camera device has been closed
- * @deprecated Please use {@link CameraManager#isSessionConfigurationWithParametersSupported}
- * to check whether a SessionConfiguration is supported by the device.
+ *
*/
- @Deprecated
public boolean isSessionConfigurationSupported(
@NonNull SessionConfiguration sessionConfig) throws CameraAccessException {
throw new UnsupportedOperationException("Subclasses must override this method");
@@ -1627,6 +1632,155 @@
}
/**
+ * CameraDeviceSetup is a limited representation of {@link CameraDevice} that can be used to
+ * query device specific information which would otherwise need a CameraDevice instance.
+ * This class can be constructed without calling {@link CameraManager#openCamera} and paying
+ * the latency cost of CameraDevice creation. Use {@link CameraManager#getCameraDeviceSetup}
+ * to get an instance of this class.
+ *
+ * <p>Can only be instantiated for camera devices for which
+ * {@link CameraManager#isCameraDeviceSetupSupported} returns true.</p>
+ *
+ * @see CameraManager#isCameraDeviceSetupSupported(String)
+ * @see CameraManager#getCameraDeviceSetup(String)
+ */
+ @FlaggedApi(Flags.FLAG_CAMERA_DEVICE_SETUP)
+ public abstract static class CameraDeviceSetup {
+ /**
+ * Create a {@link CaptureRequest.Builder} for new capture requests,
+ * initialized with a template for target use case.
+ *
+ * <p>The settings are chosen to be the best options for the specific camera device,
+ * so it is not recommended to reuse the same request for a different camera device;
+ * create a builder specific for that device and template and override the
+ * settings as desired, instead.</p>
+ *
+ * <p>Supported if {@link CameraCharacteristics#INFO_SESSION_CONFIGURATION_QUERY_VERSION}
+ * is at least {@link android.os.Build.VERSION_CODES#VANILLA_ICE_CREAM}. If less or equal to
+ * {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE}, this function throws an
+ * {@link UnsupportedOperationException}.</p>
+ *
+ * @param templateType An enumeration selecting the use case for this request. Not all
+ * template types are supported on every device. See the documentation
+ * for each template type for details.
+ *
+ * @return a builder for a capture request, initialized with default settings for that
+ * template, and no output streams
+ *
+ * @throws CameraAccessException if the querying the camera device failed or there has been
+ * a fatal error
+ * @throws IllegalArgumentException if the templateType is not supported by this device
+ */
+ @NonNull
+ @FlaggedApi(Flags.FLAG_CAMERA_DEVICE_SETUP)
+ public abstract CaptureRequest.Builder createCaptureRequest(
+ @RequestTemplate int templateType) throws CameraAccessException;
+
+ /**
+ * Checks whether a particular {@link SessionConfiguration} is supported by the camera
+ * device.
+ *
+ * <p>This method performs a runtime check of a given {@link SessionConfiguration}. The
+ * result confirms whether or not the {@code SessionConfiguration}, <b>including the
+ * parameters specified via {@link SessionConfiguration#setSessionParameters}</b>, can
+ * be used to create a camera capture session using
+ * {@link CameraDevice#createCaptureSession(SessionConfiguration)}.</p>
+ *
+ * <p>This method is supported if the
+ * {@link CameraCharacteristics#INFO_SESSION_CONFIGURATION_QUERY_VERSION}
+ * is at least {@link android.os.Build.VERSION_CODES#VANILLA_ICE_CREAM}. If less or equal
+ * to {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE}, this function throws
+ * {@link UnsupportedOperationException}.</p>
+ *
+ * <p>Although this method is much faster than creating a new capture session, it can still
+ * take a few milliseconds per call. Applications should therefore not use this method to
+ * explore the entire space of supported session combinations.</p>
+ *
+ * <p>Instead, applications should use this method to query whether combinations of
+ * certain features are supported. {@link
+ * CameraCharacteristics#INFO_SESSION_CONFIGURATION_QUERY_VERSION} provides the list of
+ * feature combinations the camera device will reliably report.</p>
+ *
+ * <p><b>IMPORTANT:</b></p>
+ * <ul>
+ * <li>If a feature support can be queried via
+ * {@link CameraCharacteristics#SCALER_MANDATORY_STREAM_COMBINATIONS} or
+ * {@link CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP}, applications should
+ * directly use it rather than calling this function as: (1) using
+ * {@code CameraCharacteristics} is more efficient, and (2) calling this function with on
+ * non-supported devices will throw a {@link UnsupportedOperationException}.
+ *
+ * <li>To minimize latency of {@link SessionConfiguration} creation, applications can
+ * use deferred surfaces for SurfaceView and SurfaceTexture to avoid waiting for UI
+ * creation before setting up the camera. For {@link android.media.MediaRecorder} and
+ * {@link android.media.MediaCodec} uses, applications can use {@code ImageReader} with
+ * {@link android.hardware.HardwareBuffer#USAGE_VIDEO_ENCODE}. The lightweight nature of
+ * {@code ImageReader} helps minimize the latency cost.
+ * </ul>
+ *
+ * @return {@code true} if the given session configuration is supported by the camera
+ * device, {@code false} otherwise.
+ *
+ * @throws CameraAccessException if the camera device is no longer connected or has
+ * encountered a fatal error
+ * @throws IllegalArgumentException if the session configuration is invalid
+ *
+ * @see CameraCharacteristics#INFO_SESSION_CONFIGURATION_QUERY_VERSION
+ * @see SessionConfiguration
+ * @see android.media.ImageReader
+ */
+ @FlaggedApi(Flags.FLAG_CAMERA_DEVICE_SETUP)
+ public abstract boolean isSessionConfigurationSupported(
+ @NonNull SessionConfiguration config) throws CameraAccessException;
+
+ /**
+ * Utility function to forward the call to
+ * {@link CameraManager#openCamera(String, Executor, StateCallback)}. This function simply
+ * calls {@code CameraManager.openCamera} for the cameraId for which this class was
+ * constructed. All semantics are consistent with {@code CameraManager.openCamera}.
+ *
+ * @param executor The executor which will be used when invoking the callback.
+ * @param callback The callback which is invoked once the camera is opened
+ *
+ * @throws CameraAccessException if the camera is disabled by device policy,
+ * has been disconnected, or is being used by a higher-priority camera API client.
+ *
+ * @throws IllegalArgumentException if cameraId, the callback or the executor was null,
+ * or the cameraId does not match any currently or previously available
+ * camera device.
+ *
+ * @throws SecurityException if the application does not have permission to
+ * access the camera
+ *
+ * @see CameraManager#openCamera(String, Executor, StateCallback)
+ */
+ @FlaggedApi(Flags.FLAG_CAMERA_DEVICE_SETUP)
+ @RequiresPermission(android.Manifest.permission.CAMERA)
+ public abstract void openCamera(@NonNull @CallbackExecutor Executor executor,
+ @NonNull StateCallback callback) throws CameraAccessException;
+
+ /**
+ * Get the ID of this camera device.
+ *
+ * <p>This matches the ID given to {@link CameraManager#getCameraDeviceSetup} to instantiate
+ * this object.</p>
+ *
+ * @return the ID for this camera device
+ *
+ * @see CameraManager#getCameraIdList
+ */
+ @NonNull
+ @FlaggedApi(Flags.FLAG_CAMERA_DEVICE_SETUP)
+ public abstract String getId();
+
+ /**
+ * To be implemented by camera2 classes only.
+ * @hide
+ */
+ public CameraDeviceSetup() {}
+ }
+
+ /**
* Set audio restriction mode when this CameraDevice is being used.
*
* <p>Some camera hardware (e.g. devices with optical image stabilization support)
diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java
index ada5e19..83b68a5 100644
--- a/core/java/android/hardware/camera2/CameraManager.java
+++ b/core/java/android/hardware/camera2/CameraManager.java
@@ -36,8 +36,9 @@
import android.hardware.CameraStatus;
import android.hardware.ICameraService;
import android.hardware.ICameraServiceListener;
-import android.hardware.camera2.CameraDevice.RequestTemplate;
+import android.hardware.camera2.CameraDevice.StateCallback;
import android.hardware.camera2.impl.CameraDeviceImpl;
+import android.hardware.camera2.impl.CameraDeviceSetupImpl;
import android.hardware.camera2.impl.CameraInjectionSessionImpl;
import android.hardware.camera2.impl.CameraMetadataNative;
import android.hardware.camera2.params.ExtensionSessionConfiguration;
@@ -352,71 +353,6 @@
}
/**
- * Checks whether a particular {@link SessionConfiguration} is supported by a camera device.
- *
- * <p>This method performs a runtime check of a given {@link SessionConfiguration}. The result
- * confirms whether or not the session configuration, including the
- * {@link SessionConfiguration#setSessionParameters specified session parameters}, can
- * be successfully used to create a camera capture session using
- * {@link CameraDevice#createCaptureSession(
- * android.hardware.camera2.params.SessionConfiguration)}.
- * </p>
- *
- * <p>Supported if the {@link CameraCharacteristics#INFO_SESSION_CONFIGURATION_QUERY_VERSION}
- * is at least {@code android.os.Build.VERSION_CODES.VANILLA_ICE_CREAM}. If less or equal to
- * {@code android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE}, this function throws
- * {@code UnsupportedOperationException}.</p>
- *
- * <p>Although this method is much faster than creating a new capture session, it is not
- * trivial cost: the latency is less than 5 milliseconds in most cases. As a result, the
- * app should not use this to explore the entire space of supported session combinations.</p>
- *
- * <p>Instead, the application should use this method to query whether the
- * combination of certain features are supported. See {@link
- * CameraCharacteristics#INFO_SESSION_CONFIGURATION_QUERY_VERSION} for the list of feature
- * combinations the camera device will reliably report.</p>
- *
- * <p>IMPORTANT:</p>
- *
- * <ul>
- *
- * <li>If a feature support can be queried with {@code CameraCharacteristics},
- * the application must directly use {@code CameraCharacteristics} rather than
- * calling this function. The reasons are: (1) using {@code CameraCharacteristics} is more
- * efficient, and (2) calling this function with a non-supported feature will throw a {@code
- * IllegalArgumentException}.</li>
- *
- * <li>To minimize latency for {@code SessionConfiguration} creation, the application should
- * use deferred surfaces for SurfaceView and SurfaceTexture to avoid delays. Alternatively,
- * the application can create {@code ImageReader} with {@code USAGE_COMPOSER_OVERLAY} and
- * {@code USAGE_GPU_SAMPLED_IMAGE} usage respectively. For {@code MediaRecorder} and {@code
- * MediaCodec}, the application can use {@code ImageReader} with {@code
- * USAGE_VIDEO_ENCODE}. The lightweight nature of {@code ImageReader} helps minimize the
- * latency cost.</li>
- *
- * </ul>
- *
- *
- * @return {@code true} if the given session configuration is supported by the camera device
- * {@code false} otherwise.
- * @throws CameraAccessException if the camera device is no longer connected or has
- * encountered a fatal error
- * @throws IllegalArgumentException if the session configuration is invalid
- * @throws UnsupportedOperationException if the query operation is not supported by the camera
- * device
- *
- * @see CameraCharacteristics#INFO_SESSION_CONFIGURATION_QUERY_VERSION
- */
- @RequiresPermission(android.Manifest.permission.CAMERA)
- @FlaggedApi(Flags.FLAG_FEATURE_COMBINATION_QUERY)
- public boolean isSessionConfigurationWithParametersSupported(@NonNull String cameraId,
- @NonNull SessionConfiguration sessionConfig) throws CameraAccessException {
- //TODO: b/298033056: restructure the OutputConfiguration API for better usability
- return CameraManagerGlobal.get().isSessionConfigurationWithParametersSupported(
- cameraId, sessionConfig);
- }
-
- /**
* Register a callback to be notified about camera device availability.
*
* <p>Registering the same callback again will replace the handler with the
@@ -785,6 +721,110 @@
}
/**
+ * Returns a {@link CameraDevice.CameraDeviceSetup} object for the given {@code cameraId},
+ * which provides limited access to CameraDevice setup and query functionality without
+ * requiring an {@link #openCamera} call. The {@link CameraDevice} can later be obtained either
+ * by calling {@link #openCamera}, or {@link CameraDevice.CameraDeviceSetup#openCamera}.
+ *
+ * <p>Support for {@link CameraDevice.CameraDeviceSetup} for a given {@code cameraId} must be
+ * checked with {@link #isCameraDeviceSetupSupported}. If {@code isCameraDeviceSetupSupported}
+ * returns {@code false} for a {@code cameraId}, this method will throw an
+ * {@link UnsupportedOperationException}</p>
+ *
+ * @param cameraId The unique identifier of the camera device for which
+ * {@link CameraDevice.CameraDeviceSetup} object must be constructed. This
+ * identifier must be present in {@link #getCameraIdList()}
+ *
+ * @return {@link CameraDevice.CameraDeviceSetup} object corresponding to the provided
+ * {@code cameraId}
+ *
+ * @throws IllegalArgumentException If {@code cameraId} is null, or if {@code cameraId} does not
+ * match any device in {@link #getCameraIdList()}.
+ * @throws CameraAccessException if the camera device is not accessible
+ * @throws UnsupportedOperationException if {@link CameraDevice.CameraDeviceSetup} instance
+ * cannot be constructed for the given {@code cameraId}, i.e.
+ * {@link #isCameraDeviceSetupSupported} returns false.
+ *
+ * @see CameraDevice.CameraDeviceSetup
+ * @see #getCameraIdList()
+ * @see #openCamera
+ */
+ @NonNull
+ @FlaggedApi(Flags.FLAG_CAMERA_DEVICE_SETUP)
+ public CameraDevice.CameraDeviceSetup getCameraDeviceSetup(@NonNull String cameraId)
+ throws CameraAccessException {
+ if (cameraId == null) {
+ throw new IllegalArgumentException("cameraId was null");
+ }
+
+ if (CameraManagerGlobal.sCameraServiceDisabled) {
+ throw new CameraAccessException(CameraAccessException.CAMERA_DISABLED,
+ "No cameras available on device");
+ }
+
+ if (!Arrays.asList(CameraManagerGlobal.get().getCameraIdList()).contains(cameraId)) {
+ throw new IllegalArgumentException(
+ "Camera ID '" + cameraId + "' not available on device.");
+ }
+
+ if (!isCameraDeviceSetupSupported(cameraId)) {
+ throw new UnsupportedOperationException(
+ "CameraDeviceSetup is not supported for Camera ID: " + cameraId);
+ }
+
+ return new CameraDeviceSetupImpl(cameraId, /*cameraManager=*/ this,
+ mContext.getApplicationInfo().targetSdkVersion);
+ }
+
+ /**
+ * Checks a Camera Device's characteristics to ensure that a
+ * {@link CameraDevice.CameraDeviceSetup} instance can be constructed for a given
+ * {@code cameraId}. If this method returns false for a {@code cameraId}, calling
+ * {@link #getCameraDeviceSetup} for that {@code cameraId} will throw an
+ * {@link UnsupportedOperationException}.
+ *
+ * <p>{@link CameraDevice.CameraDeviceSetup} is supported for all devices that report
+ * {@link CameraCharacteristics#INFO_SESSION_CONFIGURATION_QUERY_VERSION} >
+ * {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE}</p>
+ *
+ * @param cameraId The unique identifier of the camera device for which
+ * {@link CameraDevice.CameraDeviceSetup} support is being queried. This
+ * identifier must be present in {@link #getCameraIdList()}.
+ *
+ * @return {@code true} if {@link CameraDevice.CameraDeviceSetup} object can be constructed
+ * for the provided {@code cameraId}; {@code false} otherwise.
+ *
+ * @throws IllegalArgumentException If {@code cameraId} is null, or if {@code cameraId} does not
+ * match any device in {@link #getCameraIdList()}.
+ * @throws CameraAccessException if the camera device is not accessible
+ *
+ * @see CameraCharacteristics#INFO_SESSION_CONFIGURATION_QUERY_VERSION
+ * @see CameraDevice.CameraDeviceSetup
+ * @see #getCameraDeviceSetup(String)
+ * @see #getCameraIdList()
+ */
+ @FlaggedApi(Flags.FLAG_CAMERA_DEVICE_SETUP)
+ public boolean isCameraDeviceSetupSupported(@NonNull String cameraId)
+ throws CameraAccessException {
+ if (cameraId == null) {
+ throw new IllegalArgumentException("Camera ID was null");
+ }
+
+ if (CameraManagerGlobal.sCameraServiceDisabled) {
+ throw new CameraAccessException(CameraAccessException.CAMERA_DISABLED,
+ "No cameras available on device");
+ }
+
+ if (!Arrays.asList(CameraManagerGlobal.get().getCameraIdList()).contains(cameraId)) {
+ throw new IllegalArgumentException(
+ "Camera ID '" + cameraId + "' not available on device.");
+ }
+
+ CameraCharacteristics chars = getCameraCharacteristics(cameraId);
+ return CameraDeviceSetupImpl.isCameraDeviceSetupSupported(chars);
+ }
+
+ /**
* Helper for opening a connection to a camera with the given ID.
*
* @param cameraId The unique identifier of the camera device to open
@@ -817,6 +857,11 @@
synchronized (mLock) {
ICameraDeviceUser cameraUser = null;
+ CameraDevice.CameraDeviceSetup cameraDeviceSetup = null;
+ if (Flags.cameraDeviceSetup() && isCameraDeviceSetupSupported(cameraId)) {
+ cameraDeviceSetup = getCameraDeviceSetup(cameraId);
+ }
+
android.hardware.camera2.impl.CameraDeviceImpl deviceImpl =
new android.hardware.camera2.impl.CameraDeviceImpl(
cameraId,
@@ -825,8 +870,7 @@
characteristics,
physicalIdsToChars,
mContext.getApplicationInfo().targetSdkVersion,
- mContext);
-
+ mContext, cameraDeviceSetup);
ICameraDeviceCallbacks callbacks = deviceImpl.getCallbacks();
try {
@@ -1310,48 +1354,6 @@
}
/**
- * Create a {@link CaptureRequest.Builder} for new capture requests,
- * initialized with template for a target use case.
- *
- * <p>The settings are chosen to be the best options for the specific camera device,
- * so it is not recommended to reuse the same request for a different camera device;
- * create a builder specific for that device and template and override the
- * settings as desired, instead.</p>
- *
- * <p>Supported if the {@link CameraCharacteristics#INFO_SESSION_CONFIGURATION_QUERY_VERSION}
- * is at least {@code android.os.Build.VERSION_CODES.VANILLA_ICE_CREAM}. If less or equal to
- * {@code android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE}, this function throws a
- * {@code UnsupportedOperationException}.
- *
- * @param cameraId The camera ID to create capture request for.
- * @param templateType An enumeration selecting the use case for this request. Not all template
- * types are supported on every device. See the documentation for each template type for
- * details.
- * @return a builder for a capture request, initialized with default
- * settings for that template, and no output streams
- *
- * @throws CameraAccessException if the camera device is no longer connected or has
- * encountered a fatal error
- * @throws IllegalArgumentException if the cameraId is not valid, or the templateType is
- * not supported by this device.
- * @throws UnsupportedOperationException if this method is not supported by the camera device,
- * for example, if {@link CameraCharacteristics#INFO_SESSION_CONFIGURATION_QUERY_VERSION}
- * is less than {@code android.os.Build.VERSION_CODES.VANILLA_ICE_CREAM}.
- */
- @NonNull
- @RequiresPermission(android.Manifest.permission.CAMERA)
- @FlaggedApi(Flags.FLAG_FEATURE_COMBINATION_QUERY)
- public CaptureRequest.Builder createCaptureRequest(@NonNull String cameraId,
- @RequestTemplate int templateType) throws CameraAccessException {
- if (CameraManagerGlobal.sCameraServiceDisabled) {
- throw new IllegalArgumentException("No camera available on device.");
- }
-
- return CameraManagerGlobal.get().createCaptureRequest(cameraId, templateType,
- mContext.getApplicationInfo().targetSdkVersion);
- }
-
- /**
* @hide
*/
public static boolean shouldOverrideToPortrait(@Nullable Context context) {
@@ -1825,6 +1827,23 @@
}
/**
+ * Returns the current CameraService instance connected to Global
+ * @hide
+ */
+ public ICameraService getCameraService() {
+ return CameraManagerGlobal.get().getCameraService();
+ }
+
+ /**
+ * Returns true if cameraservice is currently disabled. If true, {@link #getCameraService()}
+ * will definitely return null.
+ * @hide
+ */
+ public boolean isCameraServiceDisabled() {
+ return CameraManagerGlobal.sCameraServiceDisabled;
+ }
+
+ /**
* Reports {@link CameraExtensionSessionStats} to the {@link ICameraService} to be logged for
* currently active session. Validation is done downstream.
*
@@ -2350,24 +2369,6 @@
}
}
- public boolean isSessionConfigurationWithParametersSupported(
- @NonNull String cameraId, @NonNull SessionConfiguration sessionConfiguration)
- throws CameraAccessException {
-
- synchronized (mLock) {
- try {
- return mCameraService.isSessionConfigurationWithParametersSupported(
- cameraId, sessionConfiguration);
- } catch (ServiceSpecificException e) {
- throw ExceptionUtils.throwAsPublicException(e);
- } catch (RemoteException e) {
- // Camera service died - act as if the camera was disconnected
- throw new CameraAccessException(CameraAccessException.CAMERA_DISCONNECTED,
- "Camera service is currently unavailable", e);
- }
- }
- }
-
/**
* Helper function to find out if a camera id is in the set of combinations returned by
* getConcurrentCameraIds()
@@ -2467,45 +2468,6 @@
return torchStrength;
}
- public CaptureRequest.Builder createCaptureRequest(@NonNull String cameraId,
- @RequestTemplate int templateType, int targetSdkVersion)
- throws CameraAccessException {
- CaptureRequest.Builder builder = null;
- synchronized (mLock) {
- if (cameraId == null) {
- throw new IllegalArgumentException("cameraId was null");
- }
-
- ICameraService cameraService = getCameraService();
- if (cameraService == null) {
- throw new CameraAccessException(CameraAccessException.CAMERA_DISCONNECTED,
- "Camera service is currently unavailable.");
- }
-
- try {
- CameraMetadataNative defaultRequest =
- cameraService.createDefaultRequest(cameraId, templateType);
-
- CameraDeviceImpl.disableZslIfNeeded(defaultRequest,
- targetSdkVersion, templateType);
-
- builder = new CaptureRequest.Builder(defaultRequest, /*reprocess*/false,
- CameraCaptureSession.SESSION_ID_NONE, cameraId,
- /*physicalCameraIdSet*/null);
- } catch (ServiceSpecificException e) {
- if (e.errorCode == ICameraService.ERROR_INVALID_OPERATION) {
- throw new UnsupportedOperationException(e.getMessage());
- }
-
- throw ExceptionUtils.throwAsPublicException(e);
- } catch (RemoteException e) {
- throw new CameraAccessException(CameraAccessException.CAMERA_DISCONNECTED,
- "Camera service is currently unavailable.");
- }
- }
- return builder;
- }
-
private void handleRecoverableSetupErrors(ServiceSpecificException e) {
switch (e.errorCode) {
case ICameraService.ERROR_DISCONNECTED:
diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
index f03876b..98a44ee 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
@@ -19,6 +19,10 @@
import static com.android.internal.util.function.pooled.PooledLambda.obtainRunnable;
import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.compat.CompatChanges;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledSince;
import android.content.Context;
import android.graphics.ImageFormat;
import android.hardware.ICameraService;
@@ -59,6 +63,8 @@
import android.util.SparseArray;
import android.view.Surface;
+import com.android.internal.camera.flags.Flags;
+
import java.util.AbstractMap.SimpleEntry;
import java.util.ArrayList;
import java.util.Arrays;
@@ -85,10 +91,23 @@
private static final int REQUEST_ID_NONE = -1;
+ /**
+ * Starting {@link Build.VERSION_CODES#VANILLA_ICE_CREAM},
+ * {@link #isSessionConfigurationSupported} also checks for compatibility of session parameters
+ * when supported by the HAL. This ChangeId guards enabling that functionality for apps
+ * that target {@link Build.VERSION_CODES#VANILLA_ICE_CREAM} and above.
+ */
+ @ChangeId
+ @EnabledSince(targetSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM)
+ static final long CHECK_PARAMS_IN_IS_SESSION_CONFIGURATION_SUPPORTED = 320741775;
+
// TODO: guard every function with if (!mRemoteDevice) check (if it was closed)
private ICameraDeviceUserWrapper mRemoteDevice;
private boolean mRemoteDeviceInit = false;
+ // CameraDeviceSetup object to delegate some of the newer calls to.
+ @Nullable private final CameraDeviceSetup mCameraDeviceSetup;
+
// Lock to synchronize cross-thread access to device public interface
final Object mInterfaceLock = new Object(); // access from this class and Session only!
private final CameraDeviceCallbacks mCallbacks = new CameraDeviceCallbacks();
@@ -275,7 +294,8 @@
CameraCharacteristics characteristics,
Map<String, CameraCharacteristics> physicalIdsToChars,
int appTargetSdkVersion,
- Context ctx) {
+ Context ctx,
+ @Nullable CameraDevice.CameraDeviceSetup cameraDeviceSetup) {
if (cameraId == null || callback == null || executor == null || characteristics == null) {
throw new IllegalArgumentException("Null argument given");
}
@@ -286,6 +306,7 @@
mPhysicalIdsToChars = physicalIdsToChars;
mAppTargetSdkVersion = appTargetSdkVersion;
mContext = ctx;
+ mCameraDeviceSetup = cameraDeviceSetup;
final int MAX_TAG_LEN = 23;
String tag = String.format("CameraDevice-JV-%s", mCameraId);
@@ -781,7 +802,11 @@
UnsupportedOperationException, IllegalArgumentException {
synchronized (mInterfaceLock) {
checkIfCameraClosedOrInError();
-
+ if (CompatChanges.isChangeEnabled(CHECK_PARAMS_IN_IS_SESSION_CONFIGURATION_SUPPORTED)
+ && Flags.cameraDeviceSetup()
+ && mCameraDeviceSetup != null) {
+ return mCameraDeviceSetup.isSessionConfigurationSupported(sessionConfig);
+ }
return mRemoteDevice.isSessionConfigurationSupported(sessionConfig);
}
}
diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceSetupImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceSetupImpl.java
new file mode 100644
index 0000000..fa2f519f
--- /dev/null
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceSetupImpl.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware.camera2.impl;
+
+import android.annotation.CallbackExecutor;
+import android.annotation.FlaggedApi;
+import android.hardware.ICameraService;
+import android.hardware.camera2.CameraAccessException;
+import android.hardware.camera2.CameraCaptureSession;
+import android.hardware.camera2.CameraCharacteristics;
+import android.hardware.camera2.CameraDevice;
+import android.hardware.camera2.CameraManager;
+import android.hardware.camera2.CaptureRequest;
+import android.hardware.camera2.params.SessionConfiguration;
+import android.hardware.camera2.utils.ExceptionUtils;
+import android.os.Build;
+import android.os.RemoteException;
+import android.os.ServiceSpecificException;
+
+import androidx.annotation.NonNull;
+
+import com.android.internal.camera.flags.Flags;
+
+import java.util.concurrent.Executor;
+
+@FlaggedApi(Flags.FLAG_CAMERA_DEVICE_SETUP)
+public class CameraDeviceSetupImpl extends CameraDevice.CameraDeviceSetup {
+ private final String mCameraId;
+ private final CameraManager mCameraManager;
+ private final int mTargetSdkVersion;
+
+ private final Object mInterfaceLock = new Object();
+
+ public CameraDeviceSetupImpl(@NonNull String cameraId, @NonNull CameraManager cameraManager,
+ int targetSdkVersion) {
+ mCameraId = cameraId;
+ mCameraManager = cameraManager;
+ mTargetSdkVersion = targetSdkVersion;
+ }
+
+ @NonNull
+ @Override
+ public CaptureRequest.Builder createCaptureRequest(int templateType)
+ throws CameraAccessException {
+ synchronized (mInterfaceLock) {
+ if (mCameraManager.isCameraServiceDisabled()) {
+ throw new IllegalArgumentException("No cameras available on device");
+ }
+
+ ICameraService cameraService = mCameraManager.getCameraService();
+ if (cameraService == null) {
+ throw new CameraAccessException(CameraAccessException.CAMERA_DISCONNECTED,
+ "Camera service is currently unavailable.");
+ }
+
+ try {
+ CameraMetadataNative defaultRequest = cameraService.createDefaultRequest(mCameraId,
+ templateType);
+ CameraDeviceImpl.disableZslIfNeeded(defaultRequest, mTargetSdkVersion,
+ templateType);
+
+ return new CaptureRequest.Builder(
+ defaultRequest, /*reprocess=*/ false,
+ CameraCaptureSession.SESSION_ID_NONE, mCameraId,
+ /*physicalCameraIdSet=*/ null);
+ } catch (ServiceSpecificException e) {
+ throw ExceptionUtils.throwAsPublicException(e);
+ } catch (RemoteException e) {
+ throw ExceptionUtils.throwAsPublicException(e);
+ }
+ }
+ }
+
+ @Override
+ public boolean isSessionConfigurationSupported(@NonNull SessionConfiguration config)
+ throws CameraAccessException {
+ // TODO(b/298033056): restructure the OutputConfiguration API for better usability
+ synchronized (mInterfaceLock) {
+ if (mCameraManager.isCameraServiceDisabled()) {
+ throw new IllegalArgumentException("No cameras available on device");
+ }
+
+ ICameraService cameraService = mCameraManager.getCameraService();
+ if (cameraService == null) {
+ throw new CameraAccessException(CameraAccessException.CAMERA_DISCONNECTED,
+ "Camera service is currently unavailable.");
+ }
+
+ try {
+ return cameraService.isSessionConfigurationWithParametersSupported(
+ mCameraId, config);
+ } catch (ServiceSpecificException e) {
+ throw ExceptionUtils.throwAsPublicException(e);
+ } catch (RemoteException e) {
+ throw ExceptionUtils.throwAsPublicException(e);
+ }
+ }
+ }
+
+ @Override
+ public void openCamera(@NonNull @CallbackExecutor Executor executor,
+ @NonNull CameraDevice.StateCallback callback) throws CameraAccessException {
+ mCameraManager.openCamera(mCameraId, executor, callback);
+ }
+
+ @NonNull
+ @Override
+ public String getId() {
+ return mCameraId;
+ }
+
+ @Override
+ public int hashCode() {
+ return mCameraId.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj instanceof CameraDeviceSetupImpl other) {
+ return mCameraId.equals(other.mCameraId);
+ }
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ return "CameraDeviceSetup(cameraId='" + mCameraId + "')";
+ }
+
+ /**
+ * Returns true if HAL supports calls to {@code isSessionConfigurationWithParametersSupported};
+ * false otherwise.
+ * <p>
+ * Suppressing AndroidFrameworkCompatChange because we are querying HAL support here
+ * and HAL's return value happens to follow the same scheme as SDK version.
+ * AndroidFrameworkCompatChange incorrectly flags this as an SDK version check.
+ * @hide
+ */
+ @SuppressWarnings("AndroidFrameworkCompatChange")
+ public static boolean isCameraDeviceSetupSupported(CameraCharacteristics chars) {
+ if (!Flags.featureCombinationQuery()) {
+ return false;
+ }
+
+ Integer queryVersion = chars.get(
+ CameraCharacteristics.INFO_SESSION_CONFIGURATION_QUERY_VERSION);
+ return queryVersion != null && queryVersion > Build.VERSION_CODES.UPSIDE_DOWN_CAKE;
+ }
+}