Add the initial version of ResolutionSelector and Builder class

- Add ResolutionSelector class
- Add Builder and functions to the ResolutionSelector class
- Add setResolutionSelector function to the use cases' builder classes

Bug: 227546858
Test: ./gradlew bOS

Change-Id: Ia1e318d0e6f4a06bb54f42604dcd24725b4578d2
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/ImageAnalysis.java b/camera/camera-core/src/main/java/androidx/camera/core/ImageAnalysis.java
index 9cd621b..0732044 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/ImageAnalysis.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/ImageAnalysis.java
@@ -23,6 +23,7 @@
 import static androidx.camera.core.impl.ImageAnalysisConfig.OPTION_OUTPUT_IMAGE_FORMAT;
 import static androidx.camera.core.impl.ImageAnalysisConfig.OPTION_OUTPUT_IMAGE_ROTATION_ENABLED;
 import static androidx.camera.core.impl.ImageOutputConfig.OPTION_MAX_RESOLUTION;
+import static androidx.camera.core.impl.ImageOutputConfig.OPTION_RESOLUTION_SELECTOR;
 import static androidx.camera.core.impl.ImageOutputConfig.OPTION_SUPPORTED_RESOLUTIONS;
 import static androidx.camera.core.impl.ImageOutputConfig.OPTION_TARGET_ASPECT_RATIO;
 import static androidx.camera.core.impl.ImageOutputConfig.OPTION_TARGET_RESOLUTION;
@@ -1317,6 +1318,55 @@
             return this;
         }
 
+        /**
+         * Sets the resolution selector to select the preferred supported resolution.
+         *
+         * <p>CameraX will select a supported resolution for the {@link ImageAnalysis} according to
+         * the target resolution, aspect ratio, max resolution or high resolution enabled
+         * settings of the resolution selector. When using the <code>camera-camera2</code> CameraX
+         * implementation, which resolution will be finally selected will depend on the camera
+         * device's hardware level and the bound use cases combination. The device hardware level
+         * information can be retrieved by
+         * {@link android.hardware.camera2.CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL}
+         * from the interop class
+         * {@link androidx.camera.camera2.interop.Camera2CameraInfo#getCameraCharacteristic(CameraCharacteristics.Key)}.
+         *
+         * <p>A <code>LIMITED-level</code> above device can support a <code>RECORD</code> size
+         * resolution for {@link ImageAnalysis} when it is bound together with {@link Preview}
+         * and {@link ImageCapture}. The trade-off is the selected resolution for the
+         * {@link ImageCapture} will also be restricted by the <code>RECORD</code> size. To
+         * successfully select a <code>RECORD</code> size resolution for {@link ImageAnalysis}, a
+         * <code>RECORD</code> size target resolution should be set on both {@link ImageCapture}
+         * and {@link ImageAnalysis}. This indicates that the application clearly understand the
+         * trade-off and prefer the {@link ImageAnalysis} to have a larger resolution rather than
+         * the {@link ImageCapture} to have a <code>MAXIMUM</code> size resolution. For the
+         * definitions of <code>RECORD</code>, <code>MAXIMUM</code> sizes and more details see the
+         * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraDevice#regular-capture">Regular capture</a>
+         * section in {@link android.hardware.camera2.CameraDevice}'s. The <code>RECORD</code>
+         * size refers to the camera device's maximum supported recording resolution, as
+         * determined by {@link CamcorderProfile}. The <code>MAXIMUM</code> size refers to the
+         * camera device's maximum output resolution for that format or target from
+         * {@link android.hardware.camera2.params.StreamConfigurationMap#getOutputSizes}.
+         *
+         * <p>If no resolution selector is set, resolution of 640x480 will be selected to use in
+         * priority.
+         *
+         * <p>The existing {@link #setTargetResolution(Size)} and
+         * {@link #setTargetAspectRatio(int)} APIs are deprecated and are not compatible with
+         * {@link ResolutionSelector}. Calling any of these APIs together with
+         * {@link ResolutionSelector} will throw an {@link IllegalArgumentException} while
+         * {@link #build()} is called to create the {@link ImageAnalysis} instance.
+         *
+         * @hide
+         **/
+        @RestrictTo(Scope.LIBRARY_GROUP)
+        @Override
+        @NonNull
+        public Builder setResolutionSelector(@NonNull ResolutionSelector resolutionSelector) {
+            getMutableConfig().insertOption(OPTION_RESOLUTION_SELECTOR, resolutionSelector);
+            return this;
+        }
+
         // Implementations of ThreadConfig.Builder default methods
 
         /**
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/ImageCapture.java b/camera/camera-core/src/main/java/androidx/camera/core/ImageCapture.java
index a689360..c6d64d7 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/ImageCapture.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/ImageCapture.java
@@ -41,6 +41,7 @@
 import static androidx.camera.core.impl.ImageCaptureConfig.OPTION_USE_CASE_EVENT_CALLBACK;
 import static androidx.camera.core.impl.ImageCaptureConfig.OPTION_USE_SOFTWARE_JPEG_ENCODER;
 import static androidx.camera.core.impl.ImageInputConfig.OPTION_INPUT_FORMAT;
+import static androidx.camera.core.impl.ImageOutputConfig.OPTION_RESOLUTION_SELECTOR;
 import static androidx.camera.core.impl.UseCaseConfig.OPTION_CAMERA_SELECTOR;
 import static androidx.camera.core.impl.UseCaseConfig.OPTION_ZSL_DISABLED;
 import static androidx.camera.core.impl.utils.Threads.checkMainThread;
@@ -3142,6 +3143,37 @@
             return this;
         }
 
+        /**
+         * Sets the resolution selector to select the preferred supported resolution.
+         *
+         * <p>CameraX will select a supported resolution for the {@link ImageCapture} according to
+         * the target resolution, aspect ratio, max resolution or high resolution enabled
+         * settings of the resolution selector.
+         *
+         * <p>If no resolution selector is set, the largest available resolution will be selected
+         * to use. Usually, users will intend to get the largest still image that the camera
+         * device can support. Unlike {@link Builder#setTargetResolution(Size)},
+         * {@link #setCropAspectRatio(Rational)} won't be automatically called to set the
+         * corresponding value and crop the output image when a target resolution is set. Use
+         * {@link ViewPort} instead if the output images need to be cropped in a specific
+         * aspect ratio.
+         *
+         * <p>The existing {@link #setTargetResolution(Size)} and
+         * {@link #setTargetAspectRatio(int)} APIs are deprecated and are not compatible with
+         * {@link ResolutionSelector}. Calling any of these APIs together with
+         * {@link ResolutionSelector} will throw an {@link IllegalArgumentException} while
+         * {@link #build()} is called to create the {@link ImageCapture} instance.
+         *
+         * @hide
+         **/
+        @RestrictTo(Scope.LIBRARY_GROUP)
+        @Override
+        @NonNull
+        public Builder setResolutionSelector(@NonNull ResolutionSelector resolutionSelector) {
+            getMutableConfig().insertOption(OPTION_RESOLUTION_SELECTOR, resolutionSelector);
+            return this;
+        }
+
         /** @hide */
         @NonNull
         @RestrictTo(Scope.LIBRARY_GROUP)
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/Preview.java b/camera/camera-core/src/main/java/androidx/camera/core/Preview.java
index 7b7a0a2..e38dded 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/Preview.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/Preview.java
@@ -19,6 +19,7 @@
 import static androidx.camera.core.SurfaceOutput.GlTransformOptions.APPLY_CROP_ROTATE_AND_MIRRORING;
 import static androidx.camera.core.impl.ImageInputConfig.OPTION_INPUT_FORMAT;
 import static androidx.camera.core.impl.ImageOutputConfig.OPTION_APP_TARGET_ROTATION;
+import static androidx.camera.core.impl.ImageOutputConfig.OPTION_RESOLUTION_SELECTOR;
 import static androidx.camera.core.impl.PreviewConfig.IMAGE_INFO_PROCESSOR;
 import static androidx.camera.core.impl.PreviewConfig.OPTION_BACKGROUND_EXECUTOR;
 import static androidx.camera.core.impl.PreviewConfig.OPTION_CAPTURE_CONFIG_UNPACKER;
@@ -1075,6 +1076,41 @@
             return this;
         }
 
+        /**
+         * Sets the resolution selector to select the preferred supported resolution.
+         *
+         * <p>CameraX will select a supported resolution for the {@link Preview} according to
+         * the target resolution, aspect ratio, max resolution or high resolution enabled
+         * settings of the resolution selector.
+         *
+         * <p>The selected resolution will be limited by the <code>PREVIEW</code> size which is
+         * defined as the best size match to the device's screen resolution, or to 1080p
+         * (1920x1080), whichever is smaller. See the
+         * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraDevice#regular-capture">Regular capture</a>
+         * section in {@link android.hardware.camera2.CameraDevice}'. If the
+         * {@link ResolutionSelector} contains the max resolution setting larger than the
+         * <code>PREVIEW</code> size, a size larger than the device's screen resolution or 1080p
+         * can be selected to use for {@link Preview}.
+         *
+         * <p>Note that due to compatibility reasons, CameraX may select a resolution that is
+         * larger than the default screen resolution on certain devices.
+         *
+         * <p>The existing {@link #setTargetResolution(Size)} and
+         * {@link #setTargetAspectRatio(int)} APIs are deprecated and are not compatible with
+         * {@link ResolutionSelector}. Calling any of these APIs together with
+         * {@link ResolutionSelector} will throw an {@link IllegalArgumentException} while
+         * {@link #build()} is called to create the {@link Preview} instance.
+         *
+         * @hide
+         **/
+        @RestrictTo(Scope.LIBRARY_GROUP)
+        @Override
+        @NonNull
+        public Builder setResolutionSelector(@NonNull ResolutionSelector resolutionSelector) {
+            getMutableConfig().insertOption(OPTION_RESOLUTION_SELECTOR, resolutionSelector);
+            return this;
+        }
+
         // Implementations of ThreadConfig.Builder default methods
 
         /**
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/ResolutionSelector.java b/camera/camera-core/src/main/java/androidx/camera/core/ResolutionSelector.java
new file mode 100644
index 0000000..3667b24
--- /dev/null
+++ b/camera/camera-core/src/main/java/androidx/camera/core/ResolutionSelector.java
@@ -0,0 +1,358 @@
+/*
+ * Copyright 2022 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 androidx.camera.core;
+
+import android.util.Size;
+import android.view.Surface;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.RequiresApi;
+import androidx.annotation.RestrictTo;
+
+/**
+ * A set of requirements and priorities used to select a resolution for the use case.
+ *
+ * <p>The resolution size or the aspect ratio parameters being set in this
+ * {@link ResolutionSelector} is used to find the surface size in the camera sensor's natural
+ * orientation (landscape) from the supported resolution list. In camera2 implementation, this
+ * supported resolution list can be retrieved from
+ * {@link android.hardware.camera2.params.StreamConfigurationMap#getOutputSizes(int)}. The
+ * {@link android.hardware.camera2.params.StreamConfigurationMap} can be retrieved from interop
+ * class
+ * {@link androidx.camera.camera2.interop.Camera2CameraInfo#getCameraCharacteristic(CameraCharacteristics.Key)}.
+ *
+ * <p>When the target aspect ratio is set, the resolutions of the specified aspect ratio will be
+ * selected in priority. If no resolution matches the aspect ratio, the resolution of the aspect
+ * ratio which is closest to the specified target aspect ratio will be selected in priority.
+ *
+ * <p>When the target resolution is set, the target resolution attempts to establish a minimum
+ * bound for the image resolution. If no resolution exists that is equal to or larger than the
+ * target resolution, the nearest available resolution smaller than the target resolution will be
+ * chosen. Resolutions with the same aspect ratio of the provided size will be considered in
+ * higher priority before resolutions of different aspect ratios.
+ *
+ * <p>When the max resolution is set, the resolutions that either width or height exceed the
+ * specified max resolution will be filtered out to be prevented from selecting.
+ *
+ * <p>When the high resolution support is enabled, the resolutions retrieved from
+ * {@link android.hardware.camera2.params.StreamConfigurationMap#getHighResolutionOutputSizes(int)}
+ * can be selected. This is typically used to take high resolution still images. Please note that
+ * enabling high resolutions might cause the entire capture session to not meet the 20 fps frame
+ * rate.
+ *
+ * <p>According to the camera device's hardware level and the bound use cases combination,
+ * CameraX will select the best resolution for the use case by the all conditions. Applications
+ * can know which resolution is finally selected to use by the use case's
+ * <code>getResolutionInfo()</code> function. For more details see the guaranteed supported
+ * configurations tables in {@link android.hardware.camera2.CameraDevice}'s
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraDevice#regular-capture">Regular capture</a> section.
+ *
+ * <p>This {@link ResolutionSelector} determines the size of the {@link Surface} to get the camera
+ * output for the use case. However, the actual output that the user sees will be adjusted by the
+ * camera sensor orientation and the display orientation so that the user gets the upright image
+ * properly. For example, when setting the target resolution size to <code>(1920, 1080)</code> in
+ * a camera sensor that has sensor rotation 90 degrees and the device is in natural portrait
+ * orientation ({@link Surface#ROTATION_0}), the output images will be transformed properly to be
+ * displayed correctly in preview which has the aspect ratio of 9:16.
+ *
+ * <p>The existing setTargetResolution and setTargetAspectRatio APIs in
+ * Preview/ImageCapture/ImageAnalysis's Builder are deprecated and are not compatible with
+ * {@link ResolutionSelector}. Calling any of these APIs together with {@link ResolutionSelector}
+ * will throw an {@link IllegalArgumentException}.
+ *
+ * @hide
+ */
+@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+public final class ResolutionSelector {
+
+    private static final int ASPECT_RATIO_UNKNOWN = -1;
+    private final int mTargetAspectRatio;
+    @Nullable
+    private final Size mTargetResolution;
+    @Nullable
+    private final Size mMaxResolution;
+    private final boolean mIsHighResolutionEnabled;
+
+    ResolutionSelector(int targetAspectRatio, @Nullable Size targetResolution,
+            @Nullable Size maxResolution, boolean isHighResolutionEnabled) {
+        mTargetAspectRatio = targetAspectRatio;
+        mTargetResolution = targetResolution;
+        mMaxResolution = maxResolution;
+        mIsHighResolutionEnabled = isHighResolutionEnabled;
+    }
+
+    /**
+     * Returns whether a target aspect ratio is set in the {@link ResolutionSelector}.
+     *
+     * @hide
+     */
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    public boolean hasTargetAspectRatio() {
+        return mTargetAspectRatio != ASPECT_RATIO_UNKNOWN;
+    }
+
+    /**
+     * Retrieves the target aspect ratio setting in the {@link ResolutionSelector}.
+     *
+     * @throws IllegalArgumentException when no target aspect ratio is set.
+     * @hide
+     */
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    @AspectRatio.Ratio
+    public int getTargetAspectRatio() {
+        if (mTargetAspectRatio == ASPECT_RATIO_UNKNOWN) {
+            throw new IllegalArgumentException("No target aspect ratio is set!!");
+        }
+
+        return mTargetAspectRatio;
+    }
+
+    /**
+     * Retrieves the target resolution setting in the {@link ResolutionSelector}.
+     *
+     * @hide
+     */
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    @Nullable
+    public Size getTargetResolution() {
+        return mTargetResolution;
+    }
+
+    /**
+     * Returns the max resolution setting in the {@link ResolutionSelector}.
+     *
+     * @hide
+     */
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    @Nullable
+    public Size getMaxResolution() {
+        return mMaxResolution;
+    }
+
+    /**
+     * Returns whether high resolutions are allowed to be selected.
+     *
+     * @hide
+     */
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    public boolean isHighResolutionEnabled() {
+        return mIsHighResolutionEnabled;
+    }
+
+    /**
+     * Builder for a {@link ResolutionSelector}.
+     */
+    public static final class Builder {
+        private int mTargetAspectRatio = ASPECT_RATIO_UNKNOWN;
+        @Nullable
+        private Size mTargetResolution = null;
+        @Nullable
+        private Size mMaxResolution = null;
+        private boolean mIsHighResolutionEnabled = false;
+
+        /**
+         * Creates a new {@link Builder} object.
+         */
+        public Builder() {
+        }
+
+        private Builder(@NonNull ResolutionSelector selector) {
+            if (selector.hasTargetAspectRatio()) {
+                mTargetAspectRatio = selector.getTargetAspectRatio();
+            }
+            mTargetResolution = selector.getTargetResolution();
+            mMaxResolution = selector.getMaxResolution();
+            mIsHighResolutionEnabled = selector.isHighResolutionEnabled();
+        }
+
+        /**
+         * Generates a {@link Builder} from another {@link ResolutionSelector} object.
+         *
+         * @param selector An existing {@link ResolutionSelector}.
+         * @return The new {@link Builder}.
+         */
+        @NonNull
+        public static Builder fromSelector(@NonNull ResolutionSelector selector) {
+            return new Builder(selector);
+        }
+
+        /**
+         * Sets the target aspect ratio that the output images are expected to have.
+         *
+         * <p>The input aspect ratio parameter being set in this {@link ResolutionSelector} is
+         * used to find the surface size in the camera sensor's natural orientation (landscape)
+         * from the supported resolution list.
+         *
+         * <p>It is not allowed to set both target aspect ratio and target resolution on the same
+         * use case. Attempting so will throw an IllegalArgumentException when calling the
+         * {@link #build()} function.
+         *
+         * <p>About the usage example, if the device has a <code>16:9</code> display and wants to
+         * capture images matching the display aspect ratio, a {@link ResolutionSelector} created
+         * with {@link AspectRatio.Ratio#RATIO_16_9} target aspect ratio setting can be used. If
+         * no target aspect ratio and resolution is set for the use case,
+         * {@link AspectRatio.Ratio#RATIO_4_3} target aspect ratio is set by default. Usually,
+         * the camera sensor is in size of <code>4:3</code> aspect ratio and output images of
+         * <code>4:3</code> aspect ratio will have the full FOV of the camera device.
+         *
+         * <p>The target aspect ratio is used as a hint when determining the resulting output aspect
+         * ratio which may differ from the request, possibly due to device constraints. Application
+         * code should check the resulting output's resolution and the resulting aspect ratio may
+         * not be exactly as requested.
+         *
+         * @param targetAspectRatio A {@link AspectRatio} representing the ratio of the target's
+         *                          width and height.
+         */
+        @NonNull
+        public Builder setTargetAspectRatio(@AspectRatio.Ratio int targetAspectRatio) {
+            mTargetAspectRatio = targetAspectRatio;
+            return this;
+        }
+
+        /**
+         * Sets the target resolution that the output images are expected to have.
+         *
+         * <p>The input resolution parameter being set in this {@link ResolutionSelector} is used
+         * to find the surface size in the camera sensor's natural orientation (landscape) from
+         * the supported resolution list.
+         *
+         * <p>It is not allowed to set both target resolution and target aspect ratio on the same
+         * use case. Attempting so will throw an IllegalArgumentException when calling the
+         * {@link #build()} function.
+         *
+         * <p>About the usage example, if applications have a 1080p (1920x1080) display but only
+         * need a <code>640x480</code> preview for some specific performance or design concern, a
+         * {@link ResolutionSelector} created with <code>640x480</code> target resolution setting
+         * can be used. If no target resolution and aspect ratio is set, each type of use case
+         * has its own default value. {@link ImageCapture} will try to capture the largest image
+         * as it can. {@link ImageAnalysis} will capture <code>640x480</code> size of images for
+         * analyzing by default. {@link Preview} will select a resolution under the device's
+         * screen resolution or 1080p (1920x1080), whichever is smaller.
+         *
+         * <p>The target resolution attempts to establish a minimum bound for the image
+         * resolution. The actual image resolution will be the closest available resolution in
+         * size that is not smaller than the target resolution, as determined by the Camera
+         * implementation. However, if no resolution exists that is equal to or larger than the
+         * target resolution, the nearest available resolution smaller than the target resolution
+         * will be chosen. Resolutions with the same aspect ratio of the provided {@link Size}
+         * will be considered in higher priority before resolutions of different aspect ratios.
+         *
+         * @param targetResolution The target resolution to choose from supported output sizes list.
+         */
+        @NonNull
+        public Builder setTargetResolution(@NonNull Size targetResolution) {
+            mTargetResolution = targetResolution;
+            return this;
+        }
+
+        /**
+         * Sets the max resolution condition for the use case.
+         *
+         * <p>The max resolution prevents the use case to select the sizes which either width or
+         * height exceeds the specified resolution.
+         *
+         * <p>The resolution should be expressed in the camera sensor's natural orientation
+         * (landscape).
+         *
+         * <p>About the usage example, if applications want to select a resolution smaller than a
+         * specific resolution to have better performance, a {@link ResolutionSelector} which
+         * sets this specific resolution as the max resolution can be used. Or, if applications
+         * want to select a larger resolution for a {@link Preview} which has the default max
+         * resolution setting of the small one of device's screen size and 1080p (1920x1080), a
+         * {@link ResolutionSelector} created with max resolution setting can also be used.
+         *
+         * <p>When using the <code>camera-camera2</code> CameraX implementation, which resolution
+         * will be finally selected will depend on the camera device's hardware level and the
+         * bound use cases combination. The device hardware level information can be retrieved by
+         * {@link android.hardware.camera2.CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL}
+         * from the interop class
+         * {@link androidx.camera.camera2.interop.Camera2CameraInfo#getCameraCharacteristic(CameraCharacteristics.Key)}.
+         * For more details see the
+         * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraDevice#regular-capture">Regular capture</a>
+         * section in {@link android.hardware.camera2.CameraDevice}.
+         *
+         * @param resolution The max resolution limitation to choose from supported output sizes
+         *                   list.
+         * @return The current {@link Builder}.
+         */
+        @NonNull
+        public Builder setMaxResolution(@NonNull Size resolution) {
+            mMaxResolution = resolution;
+            return this;
+        }
+
+        /**
+         * Sets whether high resolutions are allowed to be selected for the use cases.
+         *
+         * <p> Calling this function will allow the use case to select the high resolution output
+         * sizes if it is supported for the camera device.
+         *
+         * <p>When using the <code>camera-camera2</code> CameraX implementation, the supported
+         * high resolutions are retrieved from
+         * {@link android.hardware.camera2.params.StreamConfigurationMap#getHighResolutionOutputSizes(int)}.
+         * Be noticed that the high resolution sizes might cause the entire capture session to
+         * not meet the 20 fps frame rate. Even if only an ImageCapture use case selects a high
+         * resolution, it might still impact the FPS of the Preview, ImageAnalysis or
+         * VideoCapture use cases which are bound together. This function only takes effect on
+         * devices with
+         * {@link android.hardware.camera2.CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE}
+         * capability. For devices without
+         * {@link android.hardware.camera2.CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE}
+         * capability, all resolutions can be retrieved from
+         * {@link android.hardware.camera2.params.StreamConfigurationMap#getOutputSizes(int)}, but
+         * it is not guaranteed to meet >= 20 fps for any resolution in the list.
+         *
+         * <p>Which resolution will be finally selected will depend on the camera device's
+         * hardware level and the bound use cases combination. The device hardware level
+         * information can be retrieved by
+         * {@link android.hardware.camera2.CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL}
+         * from the interop class
+         * {@link androidx.camera.camera2.interop.Camera2CameraInfo#getCameraCharacteristic(CameraCharacteristics.Key)}.
+         * For more details see the
+         * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraDevice#regular-capture">Regular capture</a>
+         * section in {@link android.hardware.camera2.CameraDevice}.
+         *
+         * @param enabled True to allow to select high resolution for the use case.
+         * @return The current {@link Builder}.
+         */
+        @NonNull
+        public Builder setHighResolutionEnabled(boolean enabled) {
+            mIsHighResolutionEnabled = enabled;
+            return this;
+        }
+
+        /**
+         * Builds the {@link ResolutionSelector}.
+         *
+         * @return the {@link ResolutionSelector} built with the specified resolution settings.
+         * @throws IllegalArgumentException when both target aspect and resolution are set at the
+         * same time.
+         */
+        @NonNull
+        public ResolutionSelector build() {
+            if (mTargetAspectRatio != ASPECT_RATIO_UNKNOWN && mTargetResolution != null) {
+                throw new IllegalArgumentException("Cannot use both setTargetResolution and "
+                        + "setTargetAspectRatio at the same time.");
+            }
+
+            return new ResolutionSelector(mTargetAspectRatio, mTargetResolution, mMaxResolution,
+                    mIsHighResolutionEnabled);
+        }
+    }
+}
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/VideoCapture.java b/camera/camera-core/src/main/java/androidx/camera/core/VideoCapture.java
index 6e77562..dcd6bfb 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/VideoCapture.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/VideoCapture.java
@@ -18,6 +18,7 @@
 
 import static androidx.camera.core.impl.ImageOutputConfig.OPTION_DEFAULT_RESOLUTION;
 import static androidx.camera.core.impl.ImageOutputConfig.OPTION_MAX_RESOLUTION;
+import static androidx.camera.core.impl.ImageOutputConfig.OPTION_RESOLUTION_SELECTOR;
 import static androidx.camera.core.impl.ImageOutputConfig.OPTION_SUPPORTED_RESOLUTIONS;
 import static androidx.camera.core.impl.ImageOutputConfig.OPTION_TARGET_ASPECT_RATIO;
 import static androidx.camera.core.impl.ImageOutputConfig.OPTION_TARGET_RESOLUTION;
@@ -1753,6 +1754,15 @@
             return this;
         }
 
+        /** @hide */
+        @RestrictTo(Scope.LIBRARY_GROUP)
+        @Override
+        @NonNull
+        public Builder setResolutionSelector(@NonNull ResolutionSelector resolutionSelector) {
+            getMutableConfig().insertOption(OPTION_RESOLUTION_SELECTOR, resolutionSelector);
+            return this;
+        }
+
         // Implementations of ThreadConfig.Builder default methods
 
         /**
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/impl/ImageOutputConfig.java b/camera/camera-core/src/main/java/androidx/camera/core/impl/ImageOutputConfig.java
index 334bc6c..ce28e63 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/impl/ImageOutputConfig.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/impl/ImageOutputConfig.java
@@ -26,6 +26,7 @@
 import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
 import androidx.camera.core.AspectRatio;
+import androidx.camera.core.ResolutionSelector;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -88,6 +89,12 @@
     Option<List<Pair<Integer, Size[]>>> OPTION_SUPPORTED_RESOLUTIONS =
             Option.create("camerax.core.imageOutput.supportedResolutions", List.class);
 
+    /**
+     * Option: camerax.core.imageOutput.resolutionSelector
+     */
+    Option<ResolutionSelector> OPTION_RESOLUTION_SELECTOR =
+            Option.create("camerax.core.imageOutput.resolutionSelector", ResolutionSelector.class);
+
     // *********************************************************************************************
 
     /**
@@ -243,6 +250,29 @@
     }
 
     /**
+     * Retrieves the resolution selector can be used by the target from this configuration.
+     *
+     * @param valueIfMissing The value to return if this configuration option has not been set.
+     * @return The stored value or <code>valueIfMissing</code> if the value does not exist in this
+     * configuration.
+     */
+    @Nullable
+    default ResolutionSelector getResolutionSelector(@Nullable ResolutionSelector valueIfMissing) {
+        return retrieveOption(OPTION_RESOLUTION_SELECTOR, valueIfMissing);
+    }
+
+    /**
+     * Retrieves the resolution selector can be used by the target from this configuration.
+     *
+     * @return The stored value, if it exists in this configuration.
+     * @throws IllegalArgumentException if the option does not exist in this configuration.
+     */
+    @NonNull
+    default ResolutionSelector getResolutionSelector() {
+        return retrieveOption(OPTION_RESOLUTION_SELECTOR);
+    }
+
+    /**
      * Retrieves the supported resolutions can be used by the target from this configuration.
      *
      * <p>Pair list is composed with {@link ImageFormat} and {@link Size} array. The returned
@@ -336,6 +366,15 @@
          */
         @NonNull
         B setSupportedResolutions(@NonNull List<Pair<Integer, Size[]>> resolutionsList);
+
+        /**
+         * Sets the resolution selector can be used by target from this configuration.
+         *
+         * @param resolutionSelector The resolution selector to select a preferred resolution.
+         * @return The current Builder.
+         */
+        @NonNull
+        B setResolutionSelector(@NonNull ResolutionSelector resolutionSelector);
     }
 
     /**
diff --git a/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeUseCaseConfig.java b/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeUseCaseConfig.java
index 71bc549..9f4fb98 100644
--- a/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeUseCaseConfig.java
+++ b/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeUseCaseConfig.java
@@ -22,6 +22,7 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.RequiresApi;
 import androidx.camera.core.CameraSelector;
+import androidx.camera.core.ResolutionSelector;
 import androidx.camera.core.UseCase;
 import androidx.camera.core.impl.CaptureConfig;
 import androidx.camera.core.impl.Config;
@@ -223,6 +224,13 @@
             return this;
         }
 
+        @NonNull
+        @Override
+        public Builder setResolutionSelector(@NonNull ResolutionSelector resolutionSelector) {
+            getMutableConfig().insertOption(OPTION_RESOLUTION_SELECTOR, resolutionSelector);
+            return this;
+        }
+
         /**
          * Sets specific image format to the fake use case.
          */
diff --git a/camera/camera-video/src/main/java/androidx/camera/video/VideoCapture.java b/camera/camera-video/src/main/java/androidx/camera/video/VideoCapture.java
index 8c33f0a..dbed878 100644
--- a/camera/camera-video/src/main/java/androidx/camera/video/VideoCapture.java
+++ b/camera/camera-video/src/main/java/androidx/camera/video/VideoCapture.java
@@ -20,6 +20,7 @@
 import static androidx.camera.core.SurfaceOutput.GlTransformOptions.APPLY_CROP_ROTATE_AND_MIRRORING;
 import static androidx.camera.core.impl.ImageOutputConfig.OPTION_DEFAULT_RESOLUTION;
 import static androidx.camera.core.impl.ImageOutputConfig.OPTION_MAX_RESOLUTION;
+import static androidx.camera.core.impl.ImageOutputConfig.OPTION_RESOLUTION_SELECTOR;
 import static androidx.camera.core.impl.ImageOutputConfig.OPTION_SUPPORTED_RESOLUTIONS;
 import static androidx.camera.core.impl.ImageOutputConfig.OPTION_TARGET_ASPECT_RATIO;
 import static androidx.camera.core.impl.ImageOutputConfig.OPTION_TARGET_ROTATION;
@@ -66,6 +67,7 @@
 import androidx.camera.core.CameraSelector;
 import androidx.camera.core.ImageCapture;
 import androidx.camera.core.Logger;
+import androidx.camera.core.ResolutionSelector;
 import androidx.camera.core.SurfaceRequest;
 import androidx.camera.core.UseCase;
 import androidx.camera.core.ViewPort;
@@ -1410,6 +1412,15 @@
             return this;
         }
 
+        /** @hide */
+        @RestrictTo(Scope.LIBRARY_GROUP)
+        @Override
+        @NonNull
+        public Builder<T> setResolutionSelector(@NonNull ResolutionSelector resolutionSelector) {
+            getMutableConfig().insertOption(OPTION_RESOLUTION_SELECTOR, resolutionSelector);
+            return this;
+        }
+
         // Implementations of ThreadConfig.Builder default methods
 
         /**