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;
+    }
+}