[go: nahoru, domu]

Move the resolution sort/filter related code to camera-core

- Add SupportedOutputSizesSorter to handle the sort/filter logic for the output sizes (major for ResolutionSelector)
- Add SupportedOutputSizesSorterLegacy to handle the legacy sort/filter logic
- Keep sizes larger than the mini-bounding sizes in the sorted list but have lower priority
- Make SupportedSurfaceCombination class only handle the guaranteed configurations related logic
- Re-write tests for the change (ResolutionSelector related tests will be added when ResolutionSelector v2 design is mreged)

Bug: 270825043
Test: ./gradlew bOS
Change-Id: Ie71580a73bdb8a71d91a638ce2ec4d8a21a7a6e6
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraSurfaceAdapter.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraSurfaceAdapter.kt
index bef9275..ad5975f 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraSurfaceAdapter.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraSurfaceAdapter.kt
@@ -134,8 +134,9 @@
      * @param cameraId          the camera id of the camera device used by the use cases
      * @param existingSurfaces  list of surfaces already configured and used by the camera. The
      *                          resolutions for these surface can not change.
-     * @param newUseCaseConfigs list of configurations of the use cases that will be given a
-     *                          suggested stream specification
+     * @param newUseCaseConfigsSupportedSizeMap map of configurations of the use cases to the
+     *                                          supported sizes list that will be given a
+     *                                          suggested stream specification
      * @return map of suggested stream specifications for given use cases
      * @throws IllegalArgumentException if {@code newUseCaseConfigs} is an empty list, if
      *                                  there isn't a supported combination of surfaces
@@ -146,7 +147,7 @@
         isConcurrentCameraModeOn: Boolean,
         cameraId: String,
         existingSurfaces: List<AttachedSurfaceInfo>,
-        newUseCaseConfigs: List<UseCaseConfig<*>>
+        newUseCaseConfigsSupportedSizeMap: Map<UseCaseConfig<*>, List<Size>>
     ): Map<UseCaseConfig<*>, StreamSpec> {
 
         if (!checkIfSupportedCombinationExist(cameraId)) {
@@ -158,7 +159,7 @@
         return supportedSurfaceCombinationMap[cameraId]!!.getSuggestedStreamSpecifications(
             isConcurrentCameraModeOn,
             existingSurfaces,
-            newUseCaseConfigs
+            newUseCaseConfigsSupportedSizeMap
         )
     }
 }
\ No newline at end of file
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/SupportedSurfaceCombination.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/SupportedSurfaceCombination.kt
index 2c23b2f..6733cc7 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/SupportedSurfaceCombination.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/SupportedSurfaceCombination.kt
@@ -26,33 +26,22 @@
 import android.media.CamcorderProfile
 import android.media.MediaRecorder
 import android.os.Build
-import android.util.Rational
 import android.util.Size
 import android.view.Display
-import android.view.Surface
 import androidx.annotation.RequiresApi
 import androidx.camera.camera2.pipe.CameraMetadata
-import androidx.camera.core.AspectRatio
-import androidx.camera.core.Logger
 import androidx.camera.core.impl.AttachedSurfaceInfo
 import androidx.camera.core.impl.EncoderProfilesProxy
 import androidx.camera.core.impl.ImageFormatConstants
-import androidx.camera.core.impl.ImageOutputConfig
 import androidx.camera.core.impl.StreamSpec
 import androidx.camera.core.impl.SurfaceCombination
 import androidx.camera.core.impl.SurfaceConfig
 import androidx.camera.core.impl.SurfaceSizeDefinition
 import androidx.camera.core.impl.UseCaseConfig
-import androidx.camera.core.impl.utils.AspectRatioUtil
-import androidx.camera.core.impl.utils.AspectRatioUtil.CompareAspectRatiosByMappingAreaInFullFovAspectRatioSpace
-import androidx.camera.core.impl.utils.AspectRatioUtil.hasMatchingAspectRatio
-import androidx.camera.core.impl.utils.CameraOrientationUtil
 import androidx.camera.core.impl.utils.CompareSizesByArea
-import androidx.camera.core.internal.utils.SizeUtil
 import androidx.camera.core.internal.utils.SizeUtil.RESOLUTION_1080P
 import androidx.camera.core.internal.utils.SizeUtil.RESOLUTION_480P
 import androidx.camera.core.internal.utils.SizeUtil.RESOLUTION_VGA
-import androidx.core.util.Preconditions
 import java.util.Arrays
 import java.util.Collections
 
@@ -77,7 +66,6 @@
     private val hardwareLevel =
         cameraMetadata[CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL]
             ?: CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY
-    private val isSensorLandscapeResolution = isSensorLandscapeResolution(cameraMetadata)
     private val concurrentSurfaceCombinations: MutableList<SurfaceCombination> = ArrayList()
     private val surfaceCombinations: MutableList<SurfaceCombination> = ArrayList()
     private val outputSizesCache: MutableMap<Int, Array<Size>> = HashMap()
@@ -86,8 +74,6 @@
     internal lateinit var surfaceSizeDefinition: SurfaceSizeDefinition
     private val displayManager: DisplayManager =
         (context.getSystemService(Context.DISPLAY_SERVICE) as DisplayManager)
-    private val activeArraySize =
-        cameraMetadata[CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE]
 
     init {
         checkCapabilities()
@@ -145,7 +131,8 @@
      *
      * @param isConcurrentCameraModeOn true if concurrent camera mode is on, otherwise false.
      * @param existingSurfaces  the existing surfaces.
-     * @param newUseCaseConfigs newly added UseCaseConfig.
+     * @param newUseCaseConfigsSupportedSizeMap newly added UseCaseConfig to supported output sizes
+     * map.
      * @return the suggested stream specs, which is a mapping from UseCaseConfig to the suggested
      * stream specification.
      * @throws IllegalArgumentException if the suggested solution for newUseCaseConfigs cannot be
@@ -154,13 +141,14 @@
     fun getSuggestedStreamSpecifications(
         isConcurrentCameraModeOn: Boolean,
         existingSurfaces: List<AttachedSurfaceInfo>,
-        newUseCaseConfigs: List<UseCaseConfig<*>>
+        newUseCaseConfigsSupportedSizeMap: Map<UseCaseConfig<*>, List<Size>>
     ): Map<UseCaseConfig<*>, StreamSpec> {
         refreshPreviewSize()
         val surfaceConfigs: MutableList<SurfaceConfig> = ArrayList()
         for (scc in existingSurfaces) {
             surfaceConfigs.add(scc.surfaceConfig)
         }
+        val newUseCaseConfigs = newUseCaseConfigsSupportedSizeMap.keys.toList()
         // Use the small size (640x480) for new use cases to check whether there is any possible
         // supported combination first
         for (useCaseConfig in newUseCaseConfigs) {
@@ -192,9 +180,8 @@
 
         // Collect supported output sizes for all use cases
         for (index in useCasesPriorityOrder) {
-            val supportedOutputSizes: List<Size> = getSupportedOutputSizes(
-                newUseCaseConfigs[index]
-            )
+            val supportedOutputSizes: List<Size> =
+                newUseCaseConfigsSupportedSizeMap[newUseCaseConfigs[index]]!!
             supportedOutputSizesList.add(supportedOutputSizes)
         }
         // Get all possible size arrangements
@@ -405,17 +392,6 @@
     }
 
     /**
-     * Check if the size obtained from sensor info indicates landscape mode.
-     */
-    private fun isSensorLandscapeResolution(cameraMetadata: CameraMetadata): Boolean {
-        val pixelArraySize: Size? =
-            cameraMetadata.get<Size>(CameraCharacteristics.SENSOR_INFO_PIXEL_ARRAY_SIZE)
-
-        // Make the default value is true since usually the sensor resolution is landscape.
-        return if (pixelArraySize != null) pixelArraySize.width >= pixelArraySize.height else true
-    }
-
-    /**
      * Calculates the size for preview. If the max size is larger than 1080p, use 1080p.
      */
     @SuppressWarnings("deprecation")
@@ -546,389 +522,6 @@
     }
 
     /**
-     * Retrieves the sorted customized supported resolutions from the given config
-     */
-    private fun getCustomizedSupportSizesFromConfig(
-        imageFormat: Int,
-        config: ImageOutputConfig
-    ): Array<Size>? {
-        var outputSizes: Array<Size>? = null
-
-        // Try to retrieve customized supported resolutions from config.
-        val formatResolutionsPairList = config.getSupportedResolutions(null)
-        if (formatResolutionsPairList != null) {
-            for (formatResolutionPair in formatResolutionsPairList) {
-                if (formatResolutionPair.first == imageFormat) {
-                    outputSizes = formatResolutionPair.second
-                    break
-                }
-            }
-        }
-        if (outputSizes != null) {
-            // TODO(b/244477758): Exclude problematic sizes
-
-            // Sort the output sizes. The Comparator result must be reversed to have a descending
-            // order result.
-            Arrays.sort(outputSizes, CompareSizesByArea(true))
-        }
-        return outputSizes
-    }
-
-    /**
-     * Flips the size if rotation is needed.
-     */
-    private fun flipSizeByRotation(size: Size?, targetRotation: Int): Size? {
-        var outputSize = size
-        // Calibrates the size with the display and sensor rotation degrees values.
-        if (size != null && isRotationNeeded(targetRotation)) {
-            outputSize = Size(/* width= */size.height, /* height= */size.width)
-        }
-        return outputSize
-    }
-
-    /**
-     * Determines whether rotation needs to be done on target rotation.
-     */
-    private fun isRotationNeeded(targetRotation: Int): Boolean {
-        val sensorOrientation: Int? =
-            cameraMetadata[CameraCharacteristics.SENSOR_ORIENTATION]
-        Preconditions.checkNotNull(
-            sensorOrientation, "Camera HAL in bad state, unable to " +
-                "retrieve the SENSOR_ORIENTATION"
-        )
-        val relativeRotationDegrees = CameraOrientationUtil.surfaceRotationToDegrees(targetRotation)
-
-        // Currently this assumes that a back-facing camera is always opposite to the screen.
-        // This may not be the case for all devices, so in the future we may need to handle that
-        // scenario.
-        val lensFacing: Int? = cameraMetadata[CameraCharacteristics.LENS_FACING]
-        Preconditions.checkNotNull(
-            lensFacing, "Camera HAL in bad state, unable to retrieve the " +
-                "LENS_FACING"
-        )
-        val isOppositeFacingScreen = CameraCharacteristics.LENS_FACING_BACK == lensFacing
-        val sensorRotationDegrees = CameraOrientationUtil.getRelativeImageRotation(
-            relativeRotationDegrees,
-            sensorOrientation!!,
-            isOppositeFacingScreen
-        )
-        return sensorRotationDegrees == 90 || sensorRotationDegrees == 270
-    }
-
-    /**
-     * Obtains the target size from ImageOutputConfig.
-     */
-    private fun getTargetSize(imageOutputConfig: ImageOutputConfig): Size? {
-        val targetRotation = imageOutputConfig.getTargetRotation(Surface.ROTATION_0)
-        // Calibrate targetSize by the target rotation value.
-        var targetSize = imageOutputConfig.getTargetResolution(null)
-        targetSize = flipSizeByRotation(targetSize, targetRotation)
-        return targetSize
-    }
-
-    /**
-     * Returns the aspect ratio group key of the target size when grouping the input resolution
-     * candidate list.
-     *
-     * The resolution candidate list will be grouped with mod 16 consideration. Therefore, we
-     * also need to consider the mod 16 factor to find which aspect ratio of group the target size
-     * might be put in. So that sizes of the group will be selected to use in the highest priority.
-     */
-    private fun getAspectRatioGroupKeyOfTargetSize(
-        targetSize: Size?,
-        resolutionCandidateList: List<Size>
-    ): Rational? {
-        if (targetSize == null) {
-            return null
-        }
-
-        val aspectRatios = getResolutionListGroupingAspectRatioKeys(
-            resolutionCandidateList
-        )
-        aspectRatios.forEach {
-            if (hasMatchingAspectRatio(targetSize, it)) {
-                return it
-            }
-        }
-        return Rational(targetSize.width, targetSize.height)
-    }
-
-    /**
-     * Returns the grouping aspect ratio keys of the input resolution list.
-     *
-     * Some sizes might be mod16 case. When grouping, those sizes will be grouped into an
-     * existing aspect ratio group if the aspect ratio can match by the mod16 rule.
-     */
-    private fun getResolutionListGroupingAspectRatioKeys(
-        resolutionCandidateList: List<Size>
-    ): List<Rational> {
-        val aspectRatios: MutableList<Rational> = mutableListOf()
-
-        // Adds the default 4:3 and 16:9 items first to avoid their mod16 sizes to create
-        // additional items.
-        aspectRatios.add(AspectRatioUtil.ASPECT_RATIO_4_3)
-        aspectRatios.add(AspectRatioUtil.ASPECT_RATIO_16_9)
-
-        // Tries to find the aspect ratio which the target size belongs to.
-        resolutionCandidateList.forEach { size ->
-            val newRatio = Rational(size.width, size.height)
-            var aspectRatioFound = aspectRatios.contains(newRatio)
-
-            // The checking size might be a mod16 size which can be mapped to an existing aspect
-            // ratio group.
-            if (!aspectRatioFound) {
-                var hasMatchingAspectRatio = false
-                aspectRatios.forEach loop@{ aspectRatio ->
-                    if (hasMatchingAspectRatio(size, aspectRatio)) {
-                        hasMatchingAspectRatio = true
-                        return@loop
-                    }
-                }
-                if (!hasMatchingAspectRatio) {
-                    aspectRatios.add(newRatio)
-                }
-            }
-        }
-        return aspectRatios
-    }
-
-    /**
-     * Returns the target aspect ratio value corrected by quirks.
-     *
-     * The final aspect ratio is determined by the following order:
-     * 1. The aspect ratio returned by TargetAspectRatio quirk (not implemented yet).
-     * 2. The use case's original aspect ratio if TargetAspectRatio quirk returns RATIO_ORIGINAL
-     * and the use case has target aspect ratio setting.
-     * 3. The aspect ratio of use case's target size setting if TargetAspectRatio quirk returns
-     * RATIO_ORIGINAL and the use case has no target aspect ratio but has target size setting.
-     *
-     * @param imageOutputConfig       the image output config of the use case.
-     * @param resolutionCandidateList the resolution candidate list which will be used to
-     *                                determine the aspect ratio by target size when target
-     *                                aspect ratio setting is not set.
-     */
-    private fun getTargetAspectRatio(
-        imageOutputConfig: ImageOutputConfig,
-        resolutionCandidateList: List<Size>
-    ): Rational? {
-        var outputRatio: Rational? = null
-        // TODO(b/245622117) Get the corrected aspect ratio from quirks instead of always using
-        //  TargetAspectRatio.RATIO_ORIGINAL
-        if (imageOutputConfig.hasTargetAspectRatio()) {
-            when (@AspectRatio.Ratio val aspectRatio = imageOutputConfig.targetAspectRatio) {
-                AspectRatio.RATIO_4_3 -> outputRatio =
-                    if (isSensorLandscapeResolution) AspectRatioUtil.ASPECT_RATIO_4_3
-                    else AspectRatioUtil.ASPECT_RATIO_3_4
-                AspectRatio.RATIO_16_9 -> outputRatio =
-                    if (isSensorLandscapeResolution) AspectRatioUtil.ASPECT_RATIO_16_9
-                    else AspectRatioUtil.ASPECT_RATIO_9_16
-                AspectRatio.RATIO_DEFAULT -> Unit
-                else -> Logger.e(
-                    TAG,
-                    "Undefined target aspect ratio: $aspectRatio"
-                )
-            }
-        } else {
-            // The legacy resolution API will use the aspect ratio of the target size to
-            // be the fallback target aspect ratio value when the use case has no target
-            // aspect ratio setting.
-            val targetSize = getTargetSize(imageOutputConfig)
-            if (targetSize != null) {
-                outputRatio = getAspectRatioGroupKeyOfTargetSize(
-                    targetSize,
-                    resolutionCandidateList
-                )
-            }
-        }
-        return outputRatio
-    }
-
-    /**
-     * Removes unnecessary sizes by target size.
-     *
-     *
-     * If the target resolution is set, a size that is equal to or closest to the target
-     * resolution will be selected. If the list includes more than one size equal to or larger
-     * than the target resolution, only one closest size needs to be kept. The other larger sizes
-     * can be removed so that they won't be selected to use.
-     *
-     * @param supportedSizesList The list should have been sorted in descending order.
-     * @param targetSize         The target size used to remove unnecessary sizes.
-     */
-    private fun removeSupportedSizesByTargetSize(
-        supportedSizesList: MutableList<Size>?,
-        targetSize: Size
-    ) {
-        if (supportedSizesList == null || supportedSizesList.isEmpty()) {
-            return
-        }
-        var indexBigEnough = -1
-        val removeSizes: MutableList<Size> = ArrayList()
-
-        // Get the index of the item that is equal to or closest to the target size.
-        for (i in supportedSizesList.indices) {
-            val outputSize = supportedSizesList[i]
-            if (outputSize.width >= targetSize.width && outputSize.height >= targetSize.height) {
-                // New big enough item closer to the target size is found. Adding the previous
-                // one into the sizes list that will be removed.
-                if (indexBigEnough >= 0) {
-                    removeSizes.add(supportedSizesList[indexBigEnough])
-                }
-                indexBigEnough = i
-            } else {
-                break
-            }
-        }
-        // Remove the unnecessary items that are larger than the item closest to the target size.
-        supportedSizesList.removeAll(removeSizes)
-    }
-
-    /**
-     * Groups sizes together according to their aspect ratios.
-     */
-    private fun groupSizesByAspectRatio(sizes: List<Size>): Map<Rational, MutableList<Size>> {
-        val aspectRatioSizeListMap: MutableMap<Rational, MutableList<Size>> = mutableMapOf()
-
-        val aspectRatioKeys = getResolutionListGroupingAspectRatioKeys(sizes)
-
-        aspectRatioKeys.forEach {
-            aspectRatioSizeListMap[it] = mutableListOf()
-        }
-
-        sizes.forEach { size ->
-            aspectRatioSizeListMap.keys.forEach { aspectRatio ->
-                // Put the size into all groups that is matched in mod16 condition since a size
-                // may match multiple aspect ratio in mod16 algorithm.
-                if (hasMatchingAspectRatio(size, aspectRatio)) {
-                    aspectRatioSizeListMap[aspectRatio]?.add(size)
-                }
-            }
-        }
-        return aspectRatioSizeListMap
-    }
-
-    /**
-     * Obtains the supported sizes for a given user case.
-     */
-    internal fun getSupportedOutputSizes(config: UseCaseConfig<*>): List<Size> {
-        val imageFormat = config.inputFormat
-        val imageOutputConfig = config as ImageOutputConfig
-        val customOrderedResolutions = imageOutputConfig.getCustomOrderedResolutions(null)
-        if (customOrderedResolutions != null) {
-            return customOrderedResolutions
-        }
-        var outputSizes: Array<Size>? =
-            getCustomizedSupportSizesFromConfig(imageFormat, imageOutputConfig)
-        if (outputSizes == null) {
-            outputSizes = getAllOutputSizesByFormat(imageFormat)
-        }
-        val outputSizeCandidates: MutableList<Size> = ArrayList()
-        var maxSize = imageOutputConfig.getMaxResolution(null)
-        val maxOutputSizeByFormat: Size = getMaxOutputSizeByFormat(imageFormat)
-
-        // Set maxSize as the max resolution setting or the max supported output size for the
-        // image format, whichever is smaller.
-        if (maxSize == null ||
-            SizeUtil.getArea(maxOutputSizeByFormat) < SizeUtil.getArea(maxSize)
-        ) {
-            maxSize = maxOutputSizeByFormat
-        }
-
-        // Sort the output sizes. The Comparator result must be reversed to have a descending order
-        // result.
-        Arrays.sort(outputSizes, CompareSizesByArea(true))
-        var targetSize: Size? = getTargetSize(imageOutputConfig)
-        var minSize = RESOLUTION_VGA
-        val defaultSizeArea = SizeUtil.getArea(RESOLUTION_VGA)
-        val maxSizeArea = SizeUtil.getArea(maxSize)
-        // When maxSize is smaller than 640x480, set minSize as 0x0. It means the min size bound
-        // will be ignored. Otherwise, set the minimal size according to min(DEFAULT_SIZE,
-        // TARGET_RESOLUTION).
-        if (maxSizeArea < defaultSizeArea) {
-            minSize = SizeUtil.RESOLUTION_ZERO
-        } else if (targetSize != null && SizeUtil.getArea(targetSize) < defaultSizeArea) {
-            minSize = targetSize
-        }
-
-        // Filter out the ones that exceed the maximum size and the minimum size. The output
-        // sizes candidates list won't have duplicated items.
-        for (outputSize: Size in outputSizes) {
-            if (SizeUtil.getArea(outputSize) <= SizeUtil.getArea(maxSize) &&
-                SizeUtil.getArea(outputSize) >= SizeUtil.getArea(minSize!!) &&
-                !outputSizeCandidates.contains(outputSize)
-            ) {
-                outputSizeCandidates.add(outputSize)
-            }
-        }
-        if (outputSizeCandidates.isEmpty()) {
-            throw java.lang.IllegalArgumentException(
-                "Can not get supported output size under supported maximum for the format: " +
-                    imageFormat
-            )
-        }
-
-        val aspectRatio: Rational? = getTargetAspectRatio(imageOutputConfig, outputSizeCandidates)
-
-        // Check the default resolution if the target resolution is not set
-        targetSize = targetSize ?: imageOutputConfig.getDefaultResolution(null)
-        var supportedResolutions: MutableList<Size> = ArrayList()
-        var aspectRatioSizeListMap: Map<Rational, MutableList<Size>>
-        if (aspectRatio == null) {
-            // If no target aspect ratio is set, all sizes can be added to the result list
-            // directly. No need to sort again since the source list has been sorted previously.
-            supportedResolutions.addAll(outputSizeCandidates)
-
-            // If the target resolution is set, use it to remove unnecessary larger sizes.
-            targetSize?.let { removeSupportedSizesByTargetSize(supportedResolutions, it) }
-        } else {
-            // Rearrange the supported size to put the ones with the same aspect ratio in the front
-            // of the list and put others in the end from large to small. Some low end devices may
-            // not able to get an supported resolution that match the preferred aspect ratio.
-
-            // Group output sizes by aspect ratio.
-            aspectRatioSizeListMap = groupSizesByAspectRatio(outputSizeCandidates)
-
-            // If the target resolution is set, use it to remove unnecessary larger sizes.
-            if (targetSize != null) {
-                // Remove unnecessary larger sizes from each aspect ratio size list
-                for (key: Rational? in aspectRatioSizeListMap.keys) {
-                    removeSupportedSizesByTargetSize(aspectRatioSizeListMap[key], targetSize)
-                }
-            }
-
-            // Sort the aspect ratio key set by the target aspect ratio.
-            val aspectRatios: List<Rational?> = ArrayList(aspectRatioSizeListMap.keys)
-            val fullFovRatio = if (activeArraySize != null) {
-                Rational(activeArraySize.width(), activeArraySize.height())
-            } else {
-                null
-            }
-            Collections.sort(
-                aspectRatios,
-                CompareAspectRatiosByMappingAreaInFullFovAspectRatioSpace(
-                    aspectRatio,
-                    fullFovRatio
-                )
-            )
-
-            // Put available sizes into final result list by aspect ratio distance to target ratio.
-            for (rational: Rational? in aspectRatios) {
-                for (size: Size in aspectRatioSizeListMap[rational]!!) {
-                    // A size may exist in multiple groups in mod16 condition. Keep only one in
-                    // the final list.
-                    if (!supportedResolutions.contains(size)) {
-                        supportedResolutions.add(size)
-                    }
-                }
-            }
-        }
-
-        // TODO(b/245619094): Use ExtraCroppingQuirk to insert selected resolutions
-
-        return supportedResolutions
-    }
-
-    /**
      * Given all supported output sizes, lists out all possible size arrangements.
      */
     private fun getAllPossibleSizeArrangements(
diff --git a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/adapter/SupportedSurfaceCombinationTest.kt b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/adapter/SupportedSurfaceCombinationTest.kt
index 11ce161..2f82f0a 100644
--- a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/adapter/SupportedSurfaceCombinationTest.kt
+++ b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/adapter/SupportedSurfaceCombinationTest.kt
@@ -28,10 +28,8 @@
 import android.media.CamcorderProfile.QUALITY_720P
 import android.media.MediaRecorder
 import android.os.Build
-import android.util.Pair
-import android.util.Rational
+import android.util.Range
 import android.util.Size
-import android.view.Surface
 import android.view.WindowManager
 import androidx.camera.camera2.pipe.CameraId
 import androidx.camera.camera2.pipe.integration.CameraPipeConfig
@@ -45,58 +43,41 @@
 import androidx.camera.camera2.pipe.testing.FakeCameraBackend
 import androidx.camera.camera2.pipe.testing.FakeCameraDevices
 import androidx.camera.camera2.pipe.testing.FakeCameraMetadata
-import androidx.camera.core.AspectRatio
 import androidx.camera.core.CameraSelector
 import androidx.camera.core.CameraSelector.LensFacing
 import androidx.camera.core.CameraX
 import androidx.camera.core.CameraXConfig
-import androidx.camera.core.ImageAnalysis
-import androidx.camera.core.ImageCapture
-import androidx.camera.core.Preview
-import androidx.camera.core.SurfaceRequest
 import androidx.camera.core.UseCase
 import androidx.camera.core.concurrent.CameraCoordinator
+import androidx.camera.core.impl.AttachedSurfaceInfo
 import androidx.camera.core.impl.CameraThreadConfig
 import androidx.camera.core.impl.EncoderProfilesProxy
 import androidx.camera.core.impl.EncoderProfilesProxy.VideoProfileProxy
-import androidx.camera.core.impl.MutableStateObservable
-import androidx.camera.core.impl.Observable
-import androidx.camera.core.impl.StreamSpec
+import androidx.camera.core.impl.ImageFormatConstants
 import androidx.camera.core.impl.SurfaceCombination
 import androidx.camera.core.impl.SurfaceConfig
 import androidx.camera.core.impl.UseCaseConfig
 import androidx.camera.core.impl.UseCaseConfigFactory
-import androidx.camera.core.impl.utils.CompareSizesByArea
-import androidx.camera.core.impl.utils.executor.CameraXExecutors
 import androidx.camera.core.internal.utils.SizeUtil
 import androidx.camera.core.internal.utils.SizeUtil.RESOLUTION_1440P
+import androidx.camera.core.internal.utils.SizeUtil.RESOLUTION_VGA
 import androidx.camera.testing.CameraUtil
 import androidx.camera.testing.CameraXUtil
-import androidx.camera.testing.Configs
 import androidx.camera.testing.EncoderProfilesUtil
-import androidx.camera.testing.SurfaceTextureProvider
 import androidx.camera.testing.fakes.FakeCamera
 import androidx.camera.testing.fakes.FakeCameraCoordinator
 import androidx.camera.testing.fakes.FakeCameraFactory
 import androidx.camera.testing.fakes.FakeCameraInfoInternal
 import androidx.camera.testing.fakes.FakeEncoderProfilesProvider
 import androidx.camera.testing.fakes.FakeUseCaseConfig
-import androidx.camera.video.FallbackStrategy
-import androidx.camera.video.MediaSpec
-import androidx.camera.video.Quality
-import androidx.camera.video.QualitySelector
-import androidx.camera.video.VideoCapture
-import androidx.camera.video.VideoOutput
-import androidx.camera.video.VideoOutput.SourceState
-import androidx.camera.video.VideoSpec
 import androidx.test.core.app.ApplicationProvider
 import androidx.test.platform.app.InstrumentationRegistry
 import androidx.testutils.assertThrows
 import com.google.common.truth.Truth.assertThat
-import java.util.Arrays
 import java.util.concurrent.ExecutionException
 import java.util.concurrent.TimeUnit
 import org.junit.After
+import org.junit.Assert
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -116,24 +97,15 @@
 @DoNotInstrument
 @Config(minSdk = Build.VERSION_CODES.LOLLIPOP)
 class SupportedSurfaceCombinationTest {
-    private val sensorOrientation0 = 0
     private val sensorOrientation90 = 90
-    private val aspectRatio43 = Rational(4, 3)
-    private val aspectRatio169 = Rational(16, 9)
     private val landscapePixelArraySize = Size(4032, 3024)
-    private val portraitPixelArraySize = Size(3024, 4032)
     private val displaySize = Size(720, 1280)
     private val vgaSize = Size(640, 480)
-    private val vgaSizeStreamSpec = StreamSpec.builder(vgaSize).build()
     private val previewSize = Size(1280, 720)
-    private val previewSizeStreamSpec = StreamSpec.builder(previewSize).build()
     private val recordSize = Size(3840, 2160)
-    private val recordSizeStreamSpec = StreamSpec.builder(recordSize).build()
     private val maximumSize = Size(4032, 3024)
-    private val maximumSizeStreamSpec = StreamSpec.builder(maximumSize).build()
     private val legacyVideoMaximumVideoSize = Size(1920, 1080)
     private val mod16Size = Size(960, 544)
-    private val mod16SizeStreamSpec = StreamSpec.builder(mod16Size).build()
     private val profileUhd = EncoderProfilesUtil.createFakeEncoderProfilesProxy(
         recordSize.width, recordSize.height
     )
@@ -157,9 +129,6 @@
         Size(960, 544), // a mod16 version of resolution with 16:9 aspect ratio.
         Size(800, 450), // 16:9
         Size(640, 480), // 4:3
-        Size(320, 240), // 4:3
-        Size(320, 180), // 16:9
-        Size(256, 144) // 16:9 For checkSmallSizesAreFilteredOut test.
     )
     private val context = InstrumentationRegistry.getInstrumentation().context
     private var cameraFactory: FakeCameraFactory? = null
@@ -194,6 +163,12 @@
         CameraXUtil.shutdown()[10000, TimeUnit.MILLISECONDS]
     }
 
+    // //////////////////////////////////////////////////////////////////////////////////////////
+    //
+    // Surface combination support tests for guaranteed configurations
+    //
+    // //////////////////////////////////////////////////////////////////////////////////////////
+
     @Test
     fun checkLegacySurfaceCombinationSupportedInLegacyDevice() {
         setupCamera(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY)
@@ -519,614 +494,11 @@
         assertThat(isSupported).isTrue()
     }
 
-    @Test
-    fun checkTargetAspectRatio() {
-        setupCamera(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY)
-        val supportedSurfaceCombination = SupportedSurfaceCombination(
-            context, fakeCameraMetadata,
-            mockEncoderProfilesAdapter
-        )
-        val fakeUseCase = FakeUseCaseConfig.Builder()
-            .setTargetAspectRatio(AspectRatio.RATIO_16_9)
-            .build()
-        val useCases: MutableList<UseCase> = ArrayList()
-        useCases.add(fakeUseCase)
-        val useCaseToConfigMap = Configs.useCaseConfigMapWithDefaultSettingsFromUseCaseList(
-            cameraFactory!!.getCamera(fakeCameraMetadata.camera.value).cameraInfoInternal,
-            useCases,
-            useCaseConfigFactory
-        )
-        val suggestedStreamSpecMap = supportedSurfaceCombination.getSuggestedStreamSpecifications(
-            false,
-            emptyList(),
-            ArrayList(useCaseToConfigMap.values)
-        )
-        val selectedStreamSpec = suggestedStreamSpecMap[useCaseToConfigMap[fakeUseCase]]!!
-        val resultAspectRatio = Rational(
-            selectedStreamSpec.resolution.width,
-            selectedStreamSpec.resolution.height
-        )
-        assertThat(resultAspectRatio).isEqualTo(aspectRatio169)
-    }
-
-    @Test
-    fun checkResolutionForMixedUseCase_AfterBindToLifecycle() {
-        setupCamera(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY)
-
-        // The test case make sure the selected result is expected after the regular flow.
-        val targetAspectRatio = aspectRatio169
-        val preview = Preview.Builder()
-            .setTargetAspectRatio(AspectRatio.RATIO_16_9)
-            .build()
-        preview.setSurfaceProvider(
-            CameraXExecutors.directExecutor(),
-            SurfaceTextureProvider.createSurfaceTextureProvider(mock())
-        )
-        val imageCapture = ImageCapture.Builder()
-            .setTargetAspectRatio(AspectRatio.RATIO_16_9)
-            .build()
-        val imageAnalysis = ImageAnalysis.Builder()
-            .setTargetAspectRatio(AspectRatio.RATIO_16_9)
-            .build()
-        val cameraUseCaseAdapter = CameraUtil
-            .createCameraUseCaseAdapter(
-                context,
-                cameraCoordinator,
-                CameraSelector.DEFAULT_BACK_CAMERA
-            )
-        cameraUseCaseAdapter.addUseCases(listOf(preview, imageCapture, imageAnalysis))
-        val previewResolution = preview.attachedSurfaceResolution!!
-        val previewRatio = Rational(
-            previewResolution.width,
-            previewResolution.height
-        )
-        val imageCaptureResolution = preview.attachedSurfaceResolution
-        val imageCaptureRatio = Rational(
-            imageCaptureResolution!!.width,
-            imageCaptureResolution.height
-        )
-        val imageAnalysisResolution = preview.attachedSurfaceResolution
-        val imageAnalysisRatio = Rational(
-            imageAnalysisResolution!!.width,
-            imageAnalysisResolution.height
-        )
-
-        // Checks no correction is needed.
-        assertThat(previewRatio).isEqualTo(targetAspectRatio)
-        assertThat(imageCaptureRatio).isEqualTo(targetAspectRatio)
-        assertThat(imageAnalysisRatio).isEqualTo(targetAspectRatio)
-    }
-
-    @Test
-    fun checkDefaultAspectRatioAndResolutionForMixedUseCase() {
-        setupCamera(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED)
-        val supportedSurfaceCombination = SupportedSurfaceCombination(
-            context, fakeCameraMetadata,
-            mockEncoderProfilesAdapter
-        )
-        val preview = Preview.Builder().build()
-        preview.setSurfaceProvider(
-            CameraXExecutors.directExecutor(),
-            SurfaceTextureProvider.createSurfaceTextureProvider(mock())
-        )
-        val imageCapture = ImageCapture.Builder().build()
-        val imageAnalysis = ImageAnalysis.Builder().build()
-
-        // Preview/ImageCapture/ImageAnalysis' default config settings that will be applied after
-        // bound to lifecycle. Calling bindToLifecycle here to make sure sizes matching to
-        // default aspect ratio will be selected.
-        val cameraUseCaseAdapter = CameraUtil.createCameraUseCaseAdapter(
-            context,
-            cameraCoordinator,
-            CameraSelector.DEFAULT_BACK_CAMERA
-        )
-        cameraUseCaseAdapter.addUseCases(
-            listOf(
-                preview,
-                imageCapture, imageAnalysis
-            )
-        )
-        val useCases: MutableList<UseCase> = ArrayList()
-        useCases.add(preview)
-        useCases.add(imageCapture)
-        useCases.add(imageAnalysis)
-        val useCaseToConfigMap = Configs.useCaseConfigMapWithDefaultSettingsFromUseCaseList(
-            cameraFactory!!.getCamera(fakeCameraMetadata.camera.value).cameraInfoInternal,
-            useCases,
-            useCaseConfigFactory
-        )
-        val suggestedStreamSpecMap = supportedSurfaceCombination.getSuggestedStreamSpecifications(
-            false,
-            emptyList(),
-            ArrayList(useCaseToConfigMap.values)
-        )
-        val previewSize = suggestedStreamSpecMap[useCaseToConfigMap[preview]]!!.resolution
-        val imageCaptureSize = suggestedStreamSpecMap[useCaseToConfigMap[imageCapture]]!!.resolution
-        val imageAnalysisSize =
-            suggestedStreamSpecMap[useCaseToConfigMap[imageAnalysis]]!!.resolution
-
-        val previewAspectRatio = Rational(
-            previewSize.width,
-            previewSize.height
-        )
-
-        val imageCaptureAspectRatio = Rational(
-            imageCaptureSize.width,
-            imageCaptureSize.height
-        )
-
-        val imageAnalysisAspectRatio = Rational(
-            imageAnalysisSize.width,
-            imageAnalysisSize.height
-        )
-
-        // Checks the default aspect ratio.
-        assertThat(previewAspectRatio).isEqualTo(aspectRatio43)
-        assertThat(imageCaptureAspectRatio).isEqualTo(aspectRatio43)
-        assertThat(imageAnalysisAspectRatio).isEqualTo(aspectRatio43)
-
-        // Checks the default resolution.
-        assertThat(imageAnalysisSize).isEqualTo(vgaSize)
-    }
-
-    @Test
-    fun checkSmallSizesAreFilteredOutByDefaultSize480p() {
-        setupCamera(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED)
-        val supportedSurfaceCombination = SupportedSurfaceCombination(
-            context, fakeCameraMetadata,
-            mockEncoderProfilesAdapter
-        )
-
-        /* This test case is for b/139018208 that get small resolution 144x256 with below
-        conditions:
-        1. The target aspect ratio is set to the screen size 1080 x 2220 (9:18.5).
-        2. The camera doesn't provide any 9:18.5 resolution and the size 144x256(9:16)
-         is considered the 9:18.5 mod16 version.
-        3. There is no other bigger resolution matched the target aspect ratio.
-        */
-        val displayWidth = 1080
-        val displayHeight = 2220
-        val preview = Preview.Builder()
-            .setTargetResolution(Size(displayHeight, displayWidth))
-            .build()
-        val useCases: MutableList<UseCase> = ArrayList()
-        useCases.add(preview)
-        val useCaseToConfigMap = Configs.useCaseConfigMapWithDefaultSettingsFromUseCaseList(
-            cameraFactory!!.getCamera(fakeCameraMetadata.camera.value).cameraInfoInternal,
-            useCases,
-            useCaseConfigFactory
-        )
-        val suggestedStreamSpecMap = supportedSurfaceCombination.getSuggestedStreamSpecifications(
-            false,
-            emptyList(),
-            ArrayList(useCaseToConfigMap.values)
-        )
-
-        // Checks the preconditions.
-        val preconditionSize = Size(256, 144)
-        val targetRatio = Rational(displayHeight, displayWidth)
-        val sizeList = ArrayList(listOf(*supportedSizes))
-        assertThat(sizeList).contains(preconditionSize)
-        for (s in supportedSizes) {
-            val supportedRational = Rational(s.width, s.height)
-            assertThat(supportedRational).isNotEqualTo(targetRatio)
-        }
-
-        // Checks the mechanism has filtered out the sizes which are smaller than default size
-        // 480p.
-        val previewSize = suggestedStreamSpecMap[useCaseToConfigMap[preview]]
-        assertThat(previewSize).isNotEqualTo(preconditionSize)
-    }
-
-    @Test
-    fun checkAspectRatioMatchedSizeCanBeSelected() {
-        setupCamera(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED)
-        val supportedSurfaceCombination = SupportedSurfaceCombination(
-            context, fakeCameraMetadata,
-            mockEncoderProfilesAdapter
-        )
-
-        // Sets each of mSupportedSizes as target resolution and also sets target rotation as
-        // Surface.ROTATION to make it aligns the sensor direction and then exactly the same size
-        // will be selected as the result. This test can also verify that size smaller than
-        // 640x480 can be selected after set as target resolution.
-        for (targetResolution in supportedSizes) {
-            val imageCapture = ImageCapture.Builder().setTargetResolution(
-                targetResolution
-            ).setTargetRotation(Surface.ROTATION_90).build()
-            val suggestedStreamSpecMap =
-                supportedSurfaceCombination.getSuggestedStreamSpecifications(
-                    false,
-                    emptyList(),
-                    listOf(imageCapture.currentConfig)
-                )
-            assertThat(targetResolution).isEqualTo(
-                suggestedStreamSpecMap[imageCapture.currentConfig]?.resolution
-            )
-        }
-    }
-
-    @Test
-    fun checkCorrectAspectRatioNotMatchedSizeCanBeSelected() {
-        setupCamera(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED)
-        val supportedSurfaceCombination = SupportedSurfaceCombination(
-            context, fakeCameraMetadata,
-            mockEncoderProfilesAdapter
-        )
-
-        // Sets target resolution as 1280x640, all supported resolutions will be put into aspect
-        // ratio not matched list. Then, 1280x720 will be the nearest matched one. Finally,
-        // checks whether 1280x720 is selected or not.
-        val targetResolution = Size(1280, 640)
-        val imageCapture = ImageCapture.Builder().setTargetResolution(
-            targetResolution
-        ).setTargetRotation(Surface.ROTATION_90).build()
-        val suggestedStreamSpecMap = supportedSurfaceCombination.getSuggestedStreamSpecifications(
-            false,
-            emptyList(),
-            listOf(imageCapture.currentConfig)
-        )
-        assertThat(Size(1280, 720)).isEqualTo(
-            suggestedStreamSpecMap[imageCapture.currentConfig]?.resolution
-        )
-    }
-
-    @Test
-    fun suggestedStreamSpecsForMixedUseCaseNotSupportedInLegacyDevice() {
-        setupCamera(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY)
-        val supportedSurfaceCombination = SupportedSurfaceCombination(
-            context, fakeCameraMetadata,
-            mockEncoderProfilesAdapter
-        )
-        val imageCapture = ImageCapture.Builder()
-            .setTargetAspectRatio(AspectRatio.RATIO_16_9)
-            .build()
-        val videoCapture = createVideoCapture()
-        val preview = Preview.Builder()
-            .setTargetAspectRatio(AspectRatio.RATIO_16_9)
-            .build()
-        val useCases: MutableList<UseCase> = ArrayList()
-        useCases.add(imageCapture)
-        useCases.add(videoCapture)
-        useCases.add(preview)
-        val useCaseToConfigMap = Configs.useCaseConfigMapWithDefaultSettingsFromUseCaseList(
-            cameraFactory!!.getCamera(fakeCameraMetadata.camera.value).cameraInfoInternal,
-            useCases,
-            useCaseConfigFactory
-        )
-        assertThrows(IllegalArgumentException::class.java) {
-            supportedSurfaceCombination.getSuggestedStreamSpecifications(
-                false,
-                emptyList(),
-                ArrayList(useCaseToConfigMap.values)
-            )
-        }
-    }
-
-    @Test
-    fun suggestedStreamSpecsForCustomizeResolutionsNotSupportedInLegacyDevice() {
-        setupCamera(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY)
-        val supportedSurfaceCombination = SupportedSurfaceCombination(
-            context, fakeCameraMetadata,
-            mockEncoderProfilesAdapter
-        )
-
-        // Legacy camera only support (PRIV, PREVIEW) + (PRIV, PREVIEW)
-        val quality = Quality.UHD
-        val previewResolutionsPairs = listOf(
-            Pair.create(ImageFormat.PRIVATE, arrayOf(previewSize))
-        )
-        val videoCapture = createVideoCapture(quality)
-        val preview = Preview.Builder()
-            .setSupportedResolutions(previewResolutionsPairs)
-            .build()
-        val useCases: MutableList<UseCase> = ArrayList()
-        useCases.add(videoCapture)
-        useCases.add(preview)
-        val useCaseToConfigMap = Configs.useCaseConfigMapWithDefaultSettingsFromUseCaseList(
-            cameraFactory!!.getCamera(fakeCameraMetadata.camera.value).cameraInfoInternal,
-            useCases,
-            useCaseConfigFactory
-        )
-        assertThrows(IllegalArgumentException::class.java) {
-            supportedSurfaceCombination.getSuggestedStreamSpecifications(
-                false,
-                emptyList(),
-                ArrayList(useCaseToConfigMap.values)
-            )
-        }
-    }
-
-    // (PRIV, PREVIEW) + (PRIV, RECORD) + (JPEG, RECORD)
-    @Test
-    fun suggestedStreamSpecsForMixedUseCaseInLimitedDevice() {
-        setupCamera(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED)
-        val supportedSurfaceCombination = SupportedSurfaceCombination(
-            context, fakeCameraMetadata,
-            mockEncoderProfilesAdapter
-        )
-        val imageCapture = ImageCapture.Builder()
-            .setTargetAspectRatio(AspectRatio.RATIO_16_9)
-            .build()
-        val videoCapture = createVideoCapture(Quality.HIGHEST)
-        val preview = Preview.Builder()
-            .setTargetAspectRatio(AspectRatio.RATIO_16_9)
-            .build()
-        val useCases: MutableList<UseCase> = ArrayList()
-        useCases.add(imageCapture)
-        useCases.add(videoCapture)
-        useCases.add(preview)
-        val useCaseToConfigMap = Configs.useCaseConfigMapWithDefaultSettingsFromUseCaseList(
-            cameraFactory!!.getCamera(fakeCameraMetadata.camera.value).cameraInfoInternal,
-            useCases,
-            useCaseConfigFactory
-        )
-        val suggestedStreamSpecMap: Map<UseCaseConfig<*>, StreamSpec> =
-            supportedSurfaceCombination.getSuggestedStreamSpecifications(
-                false,
-                emptyList(),
-                ArrayList(useCaseToConfigMap.values)
-            )
-
-        // (PRIV, PREVIEW) + (PRIV, RECORD) + (JPEG, RECORD)
-        assertThat(suggestedStreamSpecMap).containsEntry(
-            useCaseToConfigMap[imageCapture],
-            recordSizeStreamSpec
-        )
-        assertThat(suggestedStreamSpecMap).containsEntry(
-            useCaseToConfigMap[videoCapture],
-            recordSizeStreamSpec
-        )
-        assertThat(suggestedStreamSpecMap).containsEntry(
-            useCaseToConfigMap[preview],
-            previewSizeStreamSpec
-        )
-    }
-
-    @Test
-    fun suggestedStreamSpecsInFullDevice_videoHasHigherPriorityThanImage() {
-        setupCamera(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL)
-        val supportedSurfaceCombination = SupportedSurfaceCombination(
-            context, fakeCameraMetadata,
-            mockEncoderProfilesAdapter
-        )
-        val imageCapture = ImageCapture.Builder()
-            .setTargetAspectRatio(AspectRatio.RATIO_16_9)
-            .build()
-        val videoCapture = createVideoCapture(
-            QualitySelector.from(
-                Quality.UHD,
-                FallbackStrategy.lowerQualityOrHigherThan(Quality.UHD)
-            )
-        )
-        val preview = Preview.Builder()
-            .setTargetAspectRatio(AspectRatio.RATIO_16_9)
-            .build()
-        val useCases: MutableList<UseCase> = ArrayList()
-        useCases.add(imageCapture)
-        useCases.add(videoCapture)
-        useCases.add(preview)
-        val useCaseToConfigMap = Configs.useCaseConfigMapWithDefaultSettingsFromUseCaseList(
-            cameraFactory!!.getCamera(fakeCameraMetadata.camera.value).cameraInfoInternal,
-            useCases,
-            useCaseConfigFactory
-        )
-        val suggestedStreamSpecMap: Map<UseCaseConfig<*>, StreamSpec> =
-            supportedSurfaceCombination.getSuggestedStreamSpecifications(
-                false,
-                emptyList(),
-                ArrayList(useCaseToConfigMap.values)
-            )
-
-        // There are two possible combinations in Full level device
-        // (PRIV, PREVIEW) + (PRIV, RECORD) + (JPEG, RECORD) => should be applied
-        // (PRIV, PREVIEW) + (PRIV, PREVIEW) + (JPEG, MAXIMUM)
-        assertThat(suggestedStreamSpecMap).containsEntry(
-            useCaseToConfigMap[imageCapture],
-            recordSizeStreamSpec
-        )
-        assertThat(suggestedStreamSpecMap).containsEntry(
-            useCaseToConfigMap[videoCapture],
-            recordSizeStreamSpec
-        )
-        assertThat(suggestedStreamSpecMap).containsEntry(
-            useCaseToConfigMap[preview],
-            previewSizeStreamSpec
-        )
-    }
-
-    @Test
-    fun suggestedResInFullDevice_videoRecordSizeLowPriority_imageCanGetMaxSize() {
-        setupCamera(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL)
-        val supportedSurfaceCombination = SupportedSurfaceCombination(
-            context, fakeCameraMetadata,
-            mockEncoderProfilesAdapter
-        )
-        val imageCapture = ImageCapture.Builder()
-            .setTargetAspectRatio(AspectRatio.RATIO_4_3) // mMaximumSize(4032x3024) is 4:3
-            .build()
-        val videoCapture = createVideoCapture(
-            QualitySelector.fromOrderedList(
-                listOf(Quality.HD, Quality.FHD, Quality.UHD)
-            )
-        )
-        val preview = Preview.Builder()
-            .setTargetAspectRatio(AspectRatio.RATIO_16_9)
-            .build()
-        val useCases: MutableList<UseCase> = ArrayList()
-        useCases.add(imageCapture)
-        useCases.add(videoCapture)
-        useCases.add(preview)
-        val useCaseToConfigMap = Configs.useCaseConfigMapWithDefaultSettingsFromUseCaseList(
-            cameraFactory!!.getCamera(fakeCameraMetadata.camera.value).cameraInfoInternal,
-            useCases,
-            useCaseConfigFactory
-        )
-        val suggestedStreamSpecMap: Map<UseCaseConfig<*>, StreamSpec> =
-            supportedSurfaceCombination.getSuggestedStreamSpecifications(
-                false,
-                emptyList(),
-                ArrayList(useCaseToConfigMap.values)
-            )
-
-        // There are two possible combinations in Full level device
-        // (PRIV, PREVIEW) + (PRIV, RECORD) + (JPEG, RECORD)
-        // (PRIV, PREVIEW) + (PRIV, PREVIEW) + (JPEG, MAXIMUM) => should be applied
-        assertThat(suggestedStreamSpecMap).containsEntry(
-            useCaseToConfigMap[imageCapture],
-            maximumSizeStreamSpec
-        )
-        assertThat(suggestedStreamSpecMap).containsEntry(
-            useCaseToConfigMap[videoCapture],
-            previewSizeStreamSpec
-        ) // Quality.HD
-        assertThat(suggestedStreamSpecMap).containsEntry(
-            useCaseToConfigMap[preview],
-            previewSizeStreamSpec
-        )
-    }
-
-    @Test
-    fun suggestedStreamSpecsWithSameSupportedListForDifferentUseCases() {
-        setupCamera(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL)
-        val supportedSurfaceCombination = SupportedSurfaceCombination(
-            context, fakeCameraMetadata,
-            mockEncoderProfilesAdapter
-        )
-
-        /* This test case is for b/132603284 that divide by zero issue crash happened in below
-    conditions:
-    1. There are duplicated two 1280x720 supported sizes for ImageCapture and Preview.
-    2. supportedOutputSizes for ImageCapture and Preview in
-    SupportedSurfaceCombination#getAllPossibleSizeArrangements are the same.
-    */
-        val imageCapture = ImageCapture.Builder()
-            .setTargetResolution(displaySize)
-            .build()
-        val preview = Preview.Builder()
-            .setTargetResolution(displaySize)
-            .build()
-        val imageAnalysis = ImageAnalysis.Builder()
-            .setTargetResolution(displaySize)
-            .build()
-        val useCases: MutableList<UseCase> = ArrayList()
-        useCases.add(imageCapture)
-        useCases.add(preview)
-        useCases.add(imageAnalysis)
-        val useCaseToConfigMap = Configs.useCaseConfigMapWithDefaultSettingsFromUseCaseList(
-            cameraFactory!!.getCamera(fakeCameraMetadata.camera.value).cameraInfoInternal,
-            useCases,
-            useCaseConfigFactory
-        )
-        val suggestedStreamSpecMap: Map<UseCaseConfig<*>, StreamSpec> =
-            supportedSurfaceCombination.getSuggestedStreamSpecifications(
-                false,
-                emptyList(),
-                ArrayList(useCaseToConfigMap.values)
-            )
-        assertThat(suggestedStreamSpecMap).containsEntry(
-            useCaseToConfigMap[imageCapture],
-            previewSizeStreamSpec
-        )
-        assertThat(suggestedStreamSpecMap).containsEntry(
-            useCaseToConfigMap[preview],
-            previewSizeStreamSpec
-        )
-        assertThat(suggestedStreamSpecMap).containsEntry(
-            useCaseToConfigMap[imageAnalysis],
-            previewSizeStreamSpec
-        )
-    }
-
-    @Test
-    fun throwsWhenSetBothTargetResolutionAndAspectRatioForDifferentUseCases() {
-        setupCamera(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY)
-        var previewExceptionHappened = false
-        val previewBuilder = Preview.Builder()
-            .setTargetResolution(displaySize)
-            .setTargetAspectRatio(AspectRatio.RATIO_16_9)
-        try {
-            previewBuilder.build()
-        } catch (e: IllegalArgumentException) {
-            previewExceptionHappened = true
-        }
-        assertThat(previewExceptionHappened).isTrue()
-        var imageCaptureExceptionHappened = false
-        val imageCaptureConfigBuilder = ImageCapture.Builder()
-            .setTargetResolution(displaySize)
-            .setTargetAspectRatio(AspectRatio.RATIO_16_9)
-        try {
-            imageCaptureConfigBuilder.build()
-        } catch (e: IllegalArgumentException) {
-            imageCaptureExceptionHappened = true
-        }
-        assertThat(imageCaptureExceptionHappened).isTrue()
-        var imageAnalysisExceptionHappened = false
-        val imageAnalysisConfigBuilder = ImageAnalysis.Builder()
-            .setTargetResolution(displaySize)
-            .setTargetAspectRatio(AspectRatio.RATIO_16_9)
-        try {
-            imageAnalysisConfigBuilder.build()
-        } catch (e: IllegalArgumentException) {
-            imageAnalysisExceptionHappened = true
-        }
-        assertThat(imageAnalysisExceptionHappened).isTrue()
-    }
-
-    @Test
-    fun suggestedStreamSpecsForCustomizedSupportedResolutions() {
-
-        // Checks all suggested stream specs will have their resolutions become 640x480.
-        setupCamera(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED)
-        val supportedSurfaceCombination = SupportedSurfaceCombination(
-            context, fakeCameraMetadata,
-            mockEncoderProfilesAdapter
-        )
-        val formatResolutionsPairList: MutableList<Pair<Int, Array<Size>>> = ArrayList()
-        formatResolutionsPairList.add(Pair.create(ImageFormat.JPEG, arrayOf(vgaSize)))
-        formatResolutionsPairList.add(
-            Pair.create(ImageFormat.YUV_420_888, arrayOf(vgaSize))
-        )
-        formatResolutionsPairList.add(Pair.create(ImageFormat.PRIVATE, arrayOf(vgaSize)))
-
-        // Sets use cases customized supported resolutions to 640x480 only.
-        val imageCapture = ImageCapture.Builder()
-            .setSupportedResolutions(formatResolutionsPairList)
-            .build()
-        val videoCapture = createVideoCapture(Quality.SD)
-        val preview = Preview.Builder()
-            .setSupportedResolutions(formatResolutionsPairList)
-            .build()
-        val useCases: MutableList<UseCase> = ArrayList()
-        useCases.add(imageCapture)
-        useCases.add(videoCapture)
-        useCases.add(preview)
-        val useCaseToConfigMap = Configs.useCaseConfigMapWithDefaultSettingsFromUseCaseList(
-            cameraFactory!!.getCamera(fakeCameraMetadata.camera.value).cameraInfoInternal,
-            useCases,
-            useCaseConfigFactory
-        )
-        val suggestedStreamSpecMap: Map<UseCaseConfig<*>, StreamSpec> =
-            supportedSurfaceCombination.getSuggestedStreamSpecifications(
-                false,
-                emptyList(),
-                ArrayList(useCaseToConfigMap.values)
-            )
-
-        // Checks all suggested stream specs will have their resolutions become 640x480.
-        assertThat(suggestedStreamSpecMap).containsEntry(
-            useCaseToConfigMap[imageCapture],
-            vgaSizeStreamSpec
-        )
-        assertThat(suggestedStreamSpecMap).containsEntry(
-            useCaseToConfigMap[videoCapture],
-            vgaSizeStreamSpec
-        )
-        assertThat(suggestedStreamSpecMap).containsEntry(
-            useCaseToConfigMap[preview],
-            vgaSizeStreamSpec
-        )
-    }
+    // //////////////////////////////////////////////////////////////////////////////////////////
+    //
+    // Surface config transformation tests
+    //
+    // //////////////////////////////////////////////////////////////////////////////////////////
 
     @Test
     fun transformSurfaceConfigWithYUVAnalysisSize() {
@@ -1364,6 +736,755 @@
         assertThat(surfaceConfig).isEqualTo(expectedSurfaceConfig)
     }
 
+    // //////////////////////////////////////////////////////////////////////////////////////////
+    //
+    // Resolution selection tests for LEGACY-level guaranteed configurations
+    //
+    // //////////////////////////////////////////////////////////////////////////////////////////
+
+    /**
+     * PRIV/MAXIMUM
+     */
+    @Test
+    fun canSelectCorrectSize_singlePrivStream_inLegacyDevice() {
+        val privUseCase = createUseCase(UseCaseConfigFactory.CaptureType.VIDEO_CAPTURE)
+        val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+            put(privUseCase, maximumSize)
+        }
+        getSuggestedSpecsAndVerify(useCaseExpectedResultMap)
+    }
+
+    /**
+     * JPEG/MAXIMUM
+     */
+    @Test
+    fun canSelectCorrectSize_singleJpegStream_inLegacyDevice() {
+        val jpegUseCase = createUseCase(UseCaseConfigFactory.CaptureType.IMAGE_CAPTURE)
+        val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+            put(jpegUseCase, maximumSize)
+        }
+        getSuggestedSpecsAndVerify(useCaseExpectedResultMap)
+    }
+
+    /**
+     * YUV/MAXIMUM
+     */
+    @Test
+    fun canSelectCorrectSize_singleYuvStream_inLegacyDevice() {
+        val yuvUseCase = createUseCase(UseCaseConfigFactory.CaptureType.IMAGE_ANALYSIS)
+        val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+            put(yuvUseCase, maximumSize)
+        }
+        getSuggestedSpecsAndVerify(useCaseExpectedResultMap)
+    }
+
+    /**
+     * PRIV/PREVIEW + JPEG/MAXIMUM
+     */
+    @Test
+    fun canSelectCorrectSizes_privPlusJpeg_inLegacyDevice() {
+        val privUseCase = createUseCase(UseCaseConfigFactory.CaptureType.PREVIEW)
+        val jpegUseCase = createUseCase(UseCaseConfigFactory.CaptureType.IMAGE_CAPTURE)
+        val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+            put(privUseCase, previewSize)
+            put(jpegUseCase, maximumSize)
+        }
+        getSuggestedSpecsAndVerify(useCaseExpectedResultMap)
+    }
+
+    /**
+     * YUV/PREVIEW + JPEG/MAXIMUM
+     */
+    @Test
+    fun canSelectCorrectSizes_yuvPlusJpeg_inLegacyDevice() {
+        val yuvUseCase = createUseCase(UseCaseConfigFactory.CaptureType.IMAGE_ANALYSIS) // YUV
+        val jpegUseCase = createUseCase(UseCaseConfigFactory.CaptureType.IMAGE_CAPTURE) // JPEG
+        val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+            put(yuvUseCase, previewSize)
+            put(jpegUseCase, maximumSize)
+        }
+        getSuggestedSpecsAndVerify(useCaseExpectedResultMap)
+    }
+
+    /**
+     * PRIV/PREVIEW + PRIV/PREVIEW
+     */
+    @Test
+    fun canSelectCorrectSizes_privPlusPriv_inLegacyDevice() {
+        val privUseCase1 = createUseCase(UseCaseConfigFactory.CaptureType.PREVIEW) // PRIV
+        val privUseCase2 = createUseCase(UseCaseConfigFactory.CaptureType.VIDEO_CAPTURE) // PRIV
+        val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+            put(privUseCase1, previewSize)
+            put(privUseCase2, previewSize)
+        }
+        getSuggestedSpecsAndVerify(useCaseExpectedResultMap)
+    }
+
+    /**
+     * PRIV/PREVIEW + YUV/PREVIEW
+     */
+    @Test
+    fun canSelectCorrectSizes_privPlusYuv_inLegacyDevice() {
+        val privUseCase = createUseCase(UseCaseConfigFactory.CaptureType.PREVIEW) // PRIV
+        val yuvUseCase = createUseCase(UseCaseConfigFactory.CaptureType.IMAGE_ANALYSIS) // YUV
+        val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+            put(privUseCase, previewSize)
+            put(yuvUseCase, previewSize)
+        }
+        getSuggestedSpecsAndVerify(useCaseExpectedResultMap)
+    }
+
+    /**
+     * PRIV/PREVIEW + YUV/PREVIEW + JPEG/MAXIMUM
+     */
+    @Test
+    fun canSelectCorrectSizes_privPlusYuvPlusJpeg_inLegacyDevice() {
+        val privUseCase = createUseCase(UseCaseConfigFactory.CaptureType.PREVIEW) // PRIV
+        val yuvUseCase = createUseCase(UseCaseConfigFactory.CaptureType.IMAGE_ANALYSIS) // YUV
+        val jpegUseCase = createUseCase(UseCaseConfigFactory.CaptureType.IMAGE_CAPTURE) // JPEG
+        val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+            put(privUseCase, previewSize)
+            put(yuvUseCase, previewSize)
+            put(jpegUseCase, maximumSize)
+        }
+        getSuggestedSpecsAndVerify(useCaseExpectedResultMap)
+    }
+
+    /**
+     * Unsupported PRIV + JPEG + PRIV for legacy level devices
+     */
+    @Test
+    fun throwsException_unsupportedConfiguration_inLegacyDevice() {
+        val privUseCase1 = createUseCase(UseCaseConfigFactory.CaptureType.PREVIEW) // PRIV
+        val jpegUseCase = createUseCase(UseCaseConfigFactory.CaptureType.IMAGE_CAPTURE) // JPEG
+        val privUseCas2 = createUseCase(UseCaseConfigFactory.CaptureType.VIDEO_CAPTURE) // PRIV
+        val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+            put(privUseCase1, RESOLUTION_VGA)
+            put(jpegUseCase, RESOLUTION_VGA)
+            put(privUseCas2, RESOLUTION_VGA)
+        }
+        Assert.assertThrows(IllegalArgumentException::class.java) {
+            getSuggestedSpecsAndVerify(useCaseExpectedResultMap)
+        }
+    }
+
+    // //////////////////////////////////////////////////////////////////////////////////////////
+    //
+    // Resolution selection tests for LIMITED-level guaranteed configurations
+    //
+    // //////////////////////////////////////////////////////////////////////////////////////////
+
+    /**
+     * PRIV/PREVIEW + PRIV/RECORD
+     */
+    @Test
+    fun canSelectCorrectSizes_privPlusPriv_inLimitedDevice() {
+        val privUseCase1 = createUseCase(UseCaseConfigFactory.CaptureType.VIDEO_CAPTURE) // PRIV
+        val privUseCas2 = createUseCase(UseCaseConfigFactory.CaptureType.PREVIEW) // PRIV
+        val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+            put(privUseCase1, recordSize)
+            put(privUseCas2, previewSize)
+        }
+        getSuggestedSpecsAndVerify(
+            useCaseExpectedResultMap,
+            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
+        )
+    }
+
+    /**
+     * PRIV/PREVIEW + YUV/RECORD
+     */
+    @Test
+    fun canSelectCorrectSizes_privPlusYuv_inLimitedDevice() {
+        val privUseCase = createUseCase(UseCaseConfigFactory.CaptureType.PREVIEW) // PRIV
+        val yuvUseCase = createUseCase(UseCaseConfigFactory.CaptureType.IMAGE_ANALYSIS) // YUV
+        val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+            put(privUseCase, previewSize)
+            put(yuvUseCase, recordSize)
+        }
+        getSuggestedSpecsAndVerify(
+            useCaseExpectedResultMap,
+            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
+        )
+    }
+
+    /**
+     * YUV/PREVIEW + YUV/RECORD
+     */
+    @Test
+    fun canSelectCorrectSizes_yuvPlusYuv_inLimitedDevice() {
+        val yuvUseCase1 = createUseCase(UseCaseConfigFactory.CaptureType.IMAGE_ANALYSIS) // YUV
+        val yuvUseCase2 = createUseCase(UseCaseConfigFactory.CaptureType.IMAGE_ANALYSIS) // YUV
+        val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+            put(yuvUseCase1, recordSize)
+            put(yuvUseCase2, previewSize)
+        }
+        getSuggestedSpecsAndVerify(
+            useCaseExpectedResultMap,
+            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
+        )
+    }
+
+    /**
+     * PRIV/PREVIEW + PRIV/RECORD + JPEG/RECORD
+     */
+    @Test
+    fun canSelectCorrectSizes_privPlusPrivPlusJpeg_inLimitedDevice() {
+        val privUseCase1 = createUseCase(UseCaseConfigFactory.CaptureType.VIDEO_CAPTURE) // PRIV
+        val privUseCase2 = createUseCase(UseCaseConfigFactory.CaptureType.PREVIEW) // PRIV
+        val jpegUseCase = createUseCase(UseCaseConfigFactory.CaptureType.IMAGE_CAPTURE) // JPEG
+        val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+            put(privUseCase1, recordSize)
+            put(privUseCase2, previewSize)
+            put(jpegUseCase, recordSize)
+        }
+        getSuggestedSpecsAndVerify(
+            useCaseExpectedResultMap,
+            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
+        )
+    }
+
+    /**
+     * PRIV/PREVIEW + YUV/RECORD + JPEG/RECORD
+     */
+    @Test
+    fun canSelectCorrectSizes_privPlusYuvPlusJpeg_inLimitedDevice() {
+        val privUseCase = createUseCase(UseCaseConfigFactory.CaptureType.PREVIEW) // PRIV
+        val yuvUseCase = createUseCase(UseCaseConfigFactory.CaptureType.IMAGE_ANALYSIS) // YUV
+        val jpegUseCase = createUseCase(UseCaseConfigFactory.CaptureType.IMAGE_CAPTURE) // JPEG
+        val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+            put(privUseCase, previewSize)
+            put(yuvUseCase, recordSize)
+            put(jpegUseCase, recordSize)
+        }
+        getSuggestedSpecsAndVerify(
+            useCaseExpectedResultMap,
+            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
+        )
+    }
+
+    /**
+     * YUV/PREVIEW + YUV/PREVIEW + JPEG/MAXIMUM
+     */
+    @Test
+    fun canSelectCorrectSizes_yuvPlusYuvPlusJpeg_inLimitedDevice() {
+        val yuvUseCase1 = createUseCase(UseCaseConfigFactory.CaptureType.IMAGE_ANALYSIS) // YUV
+        val yuvUseCase2 = createUseCase(UseCaseConfigFactory.CaptureType.IMAGE_ANALYSIS) // YUV
+        val jpegUseCase = createUseCase(UseCaseConfigFactory.CaptureType.IMAGE_CAPTURE) // JPEG
+        val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+            put(yuvUseCase1, previewSize)
+            put(yuvUseCase2, previewSize)
+            put(jpegUseCase, maximumSize)
+        }
+        getSuggestedSpecsAndVerify(
+            useCaseExpectedResultMap,
+            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
+        )
+    }
+
+    /**
+     * Unsupported YUV + PRIV + YUV for limited level devices
+     */
+    @Test
+    fun throwsException_unsupportedConfiguration_inLimitedDevice() {
+        val yuvUseCase1 = createUseCase(UseCaseConfigFactory.CaptureType.IMAGE_ANALYSIS) // YUV
+        val privUseCase = createUseCase(UseCaseConfigFactory.CaptureType.PREVIEW) // PRIV
+        val yuvUseCase2 = createUseCase(UseCaseConfigFactory.CaptureType.IMAGE_ANALYSIS) // YUV
+        val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+            put(yuvUseCase1, RESOLUTION_VGA)
+            put(privUseCase, RESOLUTION_VGA)
+            put(yuvUseCase2, RESOLUTION_VGA)
+        }
+        Assert.assertThrows(IllegalArgumentException::class.java) {
+            getSuggestedSpecsAndVerify(
+                useCaseExpectedResultMap,
+                hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
+            )
+        }
+    }
+
+    // //////////////////////////////////////////////////////////////////////////////////////////
+    //
+    // Resolution selection tests for FULL-level guaranteed configurations
+    //
+    // //////////////////////////////////////////////////////////////////////////////////////////
+
+    /**
+     * PRIV/PREVIEW + PRIV/MAXIMUM
+     */
+    @Test
+    fun canSelectCorrectSizes_privPlusPriv_inFullDevice() {
+        val privUseCase1 = createUseCase(UseCaseConfigFactory.CaptureType.VIDEO_CAPTURE) // PRIV
+        val privUseCase2 = createUseCase(UseCaseConfigFactory.CaptureType.PREVIEW) // PRIV
+        val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+            put(privUseCase1, maximumSize)
+            put(privUseCase2, previewSize)
+        }
+        getSuggestedSpecsAndVerify(
+            useCaseExpectedResultMap,
+            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL
+        )
+    }
+
+    /**
+     * PRIV/PREVIEW + YUV/MAXIMUM
+     */
+    @Test
+    fun canSelectCorrectSizes_privPlusYuv_inFullDevice() {
+        val privUseCase = createUseCase(UseCaseConfigFactory.CaptureType.PREVIEW) // PRIV
+        val yuvUseCase = createUseCase(UseCaseConfigFactory.CaptureType.IMAGE_ANALYSIS) // YUV
+        val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+            put(privUseCase, previewSize)
+            put(yuvUseCase, maximumSize)
+        }
+        getSuggestedSpecsAndVerify(
+            useCaseExpectedResultMap,
+            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL
+        )
+    }
+
+    /**
+     * YUV/PREVIEW + YUV/MAXIMUM
+     */
+    @Test
+    fun canSelectCorrectSizes_yuvPlusYuv_inFullDevice() {
+        val yuvUseCase1 = createUseCase(UseCaseConfigFactory.CaptureType.IMAGE_ANALYSIS) // YUV
+        val yuvUseCase2 = createUseCase(UseCaseConfigFactory.CaptureType.IMAGE_ANALYSIS) // YUV
+        val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+            put(yuvUseCase1, maximumSize)
+            put(yuvUseCase2, previewSize)
+        }
+        getSuggestedSpecsAndVerify(
+            useCaseExpectedResultMap,
+            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL
+        )
+    }
+
+    /**
+     * PRIV/PREVIEW + PRIV/PREVIEW + JPEG/MAXIMUM
+     */
+    @Test
+    fun canSelectCorrectSizes_privPlusPrivPlusJpeg_inFullDevice() {
+        val jpegUseCase = createUseCase(UseCaseConfigFactory.CaptureType.IMAGE_CAPTURE) // JPEG
+        val privUseCase1 = createUseCase(UseCaseConfigFactory.CaptureType.VIDEO_CAPTURE) // PRIV
+        val privUseCase2 = createUseCase(UseCaseConfigFactory.CaptureType.PREVIEW) // PRIV
+        val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+            put(jpegUseCase, maximumSize)
+            put(privUseCase1, previewSize)
+            put(privUseCase2, previewSize)
+        }
+        getSuggestedSpecsAndVerify(
+            useCaseExpectedResultMap,
+            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL
+        )
+    }
+
+    /**
+     * YUV/VGA + PRIV/PREVIEW + YUV/MAXIMUM
+     */
+    @Test
+    fun canSelectCorrectSizes_yuvPlusPrivPlusYuv_inFullDevice() {
+        val privUseCase = createUseCase(UseCaseConfigFactory.CaptureType.PREVIEW) // PRIV
+        val yuvUseCase1 = createUseCase(UseCaseConfigFactory.CaptureType.IMAGE_ANALYSIS) // YUV
+        val yuvUseCase2 = createUseCase(UseCaseConfigFactory.CaptureType.IMAGE_ANALYSIS) // YUV
+        val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+            put(privUseCase, previewSize)
+            put(yuvUseCase1, maximumSize)
+            put(yuvUseCase2, RESOLUTION_VGA)
+        }
+        getSuggestedSpecsAndVerify(
+            useCaseExpectedResultMap,
+            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL
+        )
+    }
+
+    /**
+     * YUV/VGA + YUV/PREVIEW + YUV/MAXIMUM
+     */
+    @Test
+    fun canSelectCorrectSizes_yuvPlusYuvPlusYuv_inFullDevice() {
+        val yuvUseCase1 = createUseCase(UseCaseConfigFactory.CaptureType.IMAGE_ANALYSIS) // YUV
+        val yuvUseCase2 = createUseCase(UseCaseConfigFactory.CaptureType.IMAGE_ANALYSIS) // YUV
+        val yuvUseCase3 = createUseCase(UseCaseConfigFactory.CaptureType.IMAGE_ANALYSIS) // YUV
+        val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+            put(yuvUseCase1, maximumSize)
+            put(yuvUseCase2, previewSize)
+            put(yuvUseCase3, RESOLUTION_VGA)
+        }
+        getSuggestedSpecsAndVerify(
+            useCaseExpectedResultMap,
+            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL
+        )
+    }
+
+    /**
+     * Unsupported PRIV + PRIV + YUV + RAW for full level devices
+     */
+    @Test
+    fun throwsException_unsupportedConfiguration_inFullDevice() {
+        val privUseCase1 = createUseCase(UseCaseConfigFactory.CaptureType.VIDEO_CAPTURE) // PRIV
+        val privUseCase2 = createUseCase(UseCaseConfigFactory.CaptureType.PREVIEW) // PRIV
+        val yuvUseCase = createUseCase(UseCaseConfigFactory.CaptureType.IMAGE_ANALYSIS) // YUV
+        val rawUseCase = createRawUseCase() // RAW
+        val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+            put(privUseCase1, RESOLUTION_VGA)
+            put(privUseCase2, RESOLUTION_VGA)
+            put(yuvUseCase, RESOLUTION_VGA)
+            put(rawUseCase, RESOLUTION_VGA)
+        }
+        assertThrows(IllegalArgumentException::class.java) {
+            getSuggestedSpecsAndVerify(
+                useCaseExpectedResultMap,
+                hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
+            )
+        }
+    }
+
+    // //////////////////////////////////////////////////////////////////////////////////////////
+    //
+    // Resolution selection tests for Level-3 guaranteed configurations
+    //
+    // //////////////////////////////////////////////////////////////////////////////////////////
+
+    /**
+     * PRIV/PREVIEW + PRIV/VGA + YUV/MAXIMUM + RAW/MAXIMUM
+     */
+    @Test
+    fun canSelectCorrectSizes_privPlusPrivPlusYuvPlusRaw_inLevel3Device() {
+        val privUseCase1 = createUseCase(UseCaseConfigFactory.CaptureType.VIDEO_CAPTURE) // PRIV
+        val privUseCase2 = createUseCase(UseCaseConfigFactory.CaptureType.PREVIEW) // PRIV
+        val yuvUseCase = createUseCase(UseCaseConfigFactory.CaptureType.IMAGE_ANALYSIS) // YUV
+        val rawUseCase = createRawUseCase() // RAW
+        val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+            put(privUseCase1, previewSize)
+            put(privUseCase2, RESOLUTION_VGA)
+            put(yuvUseCase, maximumSize)
+            put(rawUseCase, maximumSize)
+        }
+        getSuggestedSpecsAndVerify(
+            useCaseExpectedResultMap,
+            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_3
+        )
+    }
+
+    /**
+     * PRIV/PREVIEW + PRIV/VGA + JPEG/MAXIMUM + RAW/MAXIMUM
+     */
+    @Test
+    fun canSelectCorrectSizes_privPlusPrivPlusJpegPlusRaw_inLevel3Device() {
+        val privUseCase1 = createUseCase(UseCaseConfigFactory.CaptureType.VIDEO_CAPTURE) // PRIV
+        val privUseCase2 = createUseCase(UseCaseConfigFactory.CaptureType.PREVIEW) // PRIV
+        val jpegUseCase = createUseCase(UseCaseConfigFactory.CaptureType.IMAGE_CAPTURE) // JPEG
+        val rawUseCase = createRawUseCase() // RAW
+        val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+            put(privUseCase1, previewSize)
+            put(privUseCase2, RESOLUTION_VGA)
+            put(jpegUseCase, maximumSize)
+            put(rawUseCase, maximumSize)
+        }
+        getSuggestedSpecsAndVerify(
+            useCaseExpectedResultMap,
+            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_3
+        )
+    }
+
+    /**
+     * Unsupported PRIV + YUV + YUV + RAW for level-3 devices
+     */
+    @Test
+    fun throwsException_unsupportedConfiguration_inLevel3Device() {
+        val privUseCase = createUseCase(UseCaseConfigFactory.CaptureType.PREVIEW) // PRIV
+        val yuvUseCase1 = createUseCase(UseCaseConfigFactory.CaptureType.IMAGE_ANALYSIS) // YUV
+        val yuvUseCase2 = createUseCase(UseCaseConfigFactory.CaptureType.IMAGE_ANALYSIS) // YUV
+        val rawUseCase = createRawUseCase() // RAW
+        val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+            put(privUseCase, RESOLUTION_VGA)
+            put(yuvUseCase1, RESOLUTION_VGA)
+            put(yuvUseCase2, RESOLUTION_VGA)
+            put(rawUseCase, RESOLUTION_VGA)
+        }
+        Assert.assertThrows(IllegalArgumentException::class.java) {
+            getSuggestedSpecsAndVerify(
+                useCaseExpectedResultMap,
+                hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_3
+            )
+        }
+    }
+
+    // //////////////////////////////////////////////////////////////////////////////////////////
+    //
+    // Resolution selection tests for Burst-capability guaranteed configurations
+    //
+    // //////////////////////////////////////////////////////////////////////////////////////////
+
+    /**
+     * PRIV/PREVIEW + PRIV/MAXIMUM
+     */
+    @Test
+    fun canSelectCorrectSizes_privPlusPriv_inLimitedDevice_withBurstCapability() {
+        val privUseCase1 = createUseCase(UseCaseConfigFactory.CaptureType.VIDEO_CAPTURE) // PRIV
+        val privUseCase2 = createUseCase(UseCaseConfigFactory.CaptureType.PREVIEW) // PRIV
+        val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+            put(privUseCase1, maximumSize)
+            put(privUseCase2, previewSize)
+        }
+        getSuggestedSpecsAndVerify(
+            useCaseExpectedResultMap,
+            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
+            capabilities = intArrayOf(
+                CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE
+            )
+        )
+    }
+
+    /**
+     * PRIV/PREVIEW + YUV/MAXIMUM
+     */
+    @Test
+    fun canSelectCorrectSizes_privPlusYuv_inLimitedDevice_withBurstCapability() {
+        val privUseCase = createUseCase(UseCaseConfigFactory.CaptureType.PREVIEW) // PRIV
+        val yuvUseCase = createUseCase(UseCaseConfigFactory.CaptureType.IMAGE_ANALYSIS) // YUV
+        val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+            put(privUseCase, previewSize)
+            put(yuvUseCase, maximumSize)
+        }
+        getSuggestedSpecsAndVerify(
+            useCaseExpectedResultMap,
+            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
+            capabilities = intArrayOf(
+                CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE
+            )
+        )
+    }
+
+    /**
+     * YUV/PREVIEW + YUV/MAXIMUM
+     */
+    @Test
+    fun canSelectCorrectSizes_yuvPlusYuv_inLimitedDevice_withBurstCapability() {
+        val yuvUseCase1 = createUseCase(UseCaseConfigFactory.CaptureType.IMAGE_ANALYSIS) // YUV
+        val yuvUseCase2 = createUseCase(UseCaseConfigFactory.CaptureType.IMAGE_ANALYSIS) // YUV
+        val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+            put(yuvUseCase1, maximumSize)
+            put(yuvUseCase2, previewSize)
+        }
+        getSuggestedSpecsAndVerify(
+            useCaseExpectedResultMap,
+            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
+            capabilities = intArrayOf(
+                CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE
+            )
+        )
+    }
+
+    // //////////////////////////////////////////////////////////////////////////////////////////
+    //
+    // Resolution selection tests for RAW-capability guaranteed configurations
+    //
+    // //////////////////////////////////////////////////////////////////////////////////////////
+
+    /**
+     * RAW/MAX
+     */
+    @Test
+    fun canSelectCorrectSizes_singleRawStream_inLimitedDevice_withRawCapability() {
+        val rawUseCase = createRawUseCase() // RAW
+        val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+            put(rawUseCase, maximumSize)
+        }
+        getSuggestedSpecsAndVerify(
+            useCaseExpectedResultMap,
+            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
+            capabilities = intArrayOf(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW)
+        )
+    }
+
+    /**
+     * PRIV/PREVIEW + RAW/MAXIMUM
+     */
+    @Test
+    fun canSelectCorrectSizes_privPlusRAW_inLimitedDevice_withRawCapability() {
+        val privUseCase = createUseCase(UseCaseConfigFactory.CaptureType.PREVIEW) // PRIV
+        val rawUseCase = createRawUseCase() // RAW
+        val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+            put(privUseCase, previewSize)
+            put(rawUseCase, maximumSize)
+        }
+        getSuggestedSpecsAndVerify(
+            useCaseExpectedResultMap,
+            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
+            capabilities = intArrayOf(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW)
+        )
+    }
+
+    /**
+     * PRIV/PREVIEW + PRIV/PREVIEW + RAW/MAXIMUM
+     */
+    @Test
+    fun canSelectCorrectSizes_privPlusPrivPlusRAW_inLimitedDevice_withRawCapability() {
+        val privUseCase1 = createUseCase(UseCaseConfigFactory.CaptureType.VIDEO_CAPTURE) // PRIV
+        val privUseCase2 = createUseCase(UseCaseConfigFactory.CaptureType.PREVIEW) // PRIV
+        val rawUseCase = createRawUseCase() // RAW
+        val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+            put(privUseCase1, previewSize)
+            put(privUseCase2, previewSize)
+            put(rawUseCase, maximumSize)
+        }
+        getSuggestedSpecsAndVerify(
+            useCaseExpectedResultMap,
+            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
+            capabilities = intArrayOf(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW)
+        )
+    }
+
+    /**
+     * PRIV/PREVIEW + YUV/PREVIEW + RAW/MAXIMUM
+     */
+    @Test
+    fun canSelectCorrectSizes_privPlusYuvPlusRAW_inLimitedDevice_withRawCapability() {
+        val privUseCase = createUseCase(UseCaseConfigFactory.CaptureType.PREVIEW) // PRIV
+        val yuvUseCase = createUseCase(UseCaseConfigFactory.CaptureType.IMAGE_ANALYSIS) // YUV
+        val rawUseCase = createRawUseCase() // RAW
+        val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+            put(privUseCase, previewSize)
+            put(yuvUseCase, previewSize)
+            put(rawUseCase, maximumSize)
+        }
+        getSuggestedSpecsAndVerify(
+            useCaseExpectedResultMap,
+            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
+            capabilities = intArrayOf(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW)
+        )
+    }
+
+    /**
+     * YUV/PREVIEW + YUV/PREVIEW + RAW/MAXIMUM
+     */
+    @Test
+    fun canSelectCorrectSizes_yuvPlusYuvPlusRAW_inLimitedDevice_withRawCapability() {
+        val yuvUseCase1 = createUseCase(UseCaseConfigFactory.CaptureType.IMAGE_ANALYSIS) // YUV
+        val yuvUseCase2 = createUseCase(UseCaseConfigFactory.CaptureType.IMAGE_ANALYSIS) // YUV
+        val rawUseCase = createRawUseCase() // RAW
+        val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+            put(yuvUseCase1, previewSize)
+            put(yuvUseCase2, previewSize)
+            put(rawUseCase, maximumSize)
+        }
+        getSuggestedSpecsAndVerify(
+            useCaseExpectedResultMap,
+            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
+            capabilities = intArrayOf(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW)
+        )
+    }
+
+    /**
+     * PRIV/PREVIEW + JPEG/MAXIMUM + RAW/MAXIMUM
+     */
+    @Test
+    fun canSelectCorrectSizes_privPlusJpegPlusRAW_inLimitedDevice_withRawCapability() {
+        val privUseCase = createUseCase(UseCaseConfigFactory.CaptureType.PREVIEW) // PRIV
+        val jpegUseCase = createUseCase(UseCaseConfigFactory.CaptureType.IMAGE_CAPTURE) // JPEG
+        val rawUseCase = createRawUseCase() // RAW
+        val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+            put(privUseCase, previewSize)
+            put(jpegUseCase, maximumSize)
+            put(rawUseCase, maximumSize)
+        }
+        getSuggestedSpecsAndVerify(
+            useCaseExpectedResultMap,
+            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
+            capabilities = intArrayOf(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW)
+        )
+    }
+
+    /**
+     * YUV/PREVIEW + JPEG/MAXIMUM + RAW/MAXIMUM
+     */
+    @Test
+    fun canSelectCorrectSizes_yuvPlusJpegPlusRAW_inLimitedDevice_withRawCapability() {
+        val yuvUseCase = createUseCase(UseCaseConfigFactory.CaptureType.IMAGE_ANALYSIS) // YUV
+        val jpegUseCase = createUseCase(UseCaseConfigFactory.CaptureType.IMAGE_CAPTURE) // JPEG
+        val rawUseCase = createRawUseCase() // RAW
+        val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+            put(yuvUseCase, previewSize)
+            put(jpegUseCase, maximumSize)
+            put(rawUseCase, maximumSize)
+        }
+        getSuggestedSpecsAndVerify(
+            useCaseExpectedResultMap,
+            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
+            capabilities = intArrayOf(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW)
+        )
+    }
+
+    private fun getSuggestedSpecsAndVerify(
+        useCasesExpectedResultMap: Map<UseCase, Size>,
+        attachedSurfaceInfoList: List<AttachedSurfaceInfo> = emptyList(),
+        hardwareLevel: Int = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY,
+        capabilities: IntArray? = null,
+        compareWithAtMost: Boolean = false
+    ) {
+        setupCamera(hardwareLevel = hardwareLevel, capabilities = capabilities)
+        val supportedSurfaceCombination = SupportedSurfaceCombination(
+            context, fakeCameraMetadata,
+            mockEncoderProfilesAdapter
+        )
+
+        val useCaseConfigMap = getUseCaseToConfigMap(useCasesExpectedResultMap.keys.toList())
+        val useCaseConfigToOutputSizesMap =
+            getUseCaseConfigToOutputSizesMap(useCaseConfigMap.values.toList())
+        val suggestedStreamSpecs = supportedSurfaceCombination.getSuggestedStreamSpecifications(
+            false,
+            attachedSurfaceInfoList,
+            useCaseConfigToOutputSizesMap
+        )
+
+        useCasesExpectedResultMap.keys.forEach {
+            val resultSize = suggestedStreamSpecs[useCaseConfigMap[it]]!!.resolution
+            val expectedSize = useCasesExpectedResultMap[it]!!
+            if (!compareWithAtMost) {
+                assertThat(resultSize).isEqualTo(expectedSize)
+            } else {
+                assertThat(sizeIsAtMost(resultSize, expectedSize)).isTrue()
+            }
+        }
+    }
+
+    private fun getUseCaseToConfigMap(useCases: List<UseCase>): Map<UseCase, UseCaseConfig<*>> {
+        val useCaseConfigMap = mutableMapOf<UseCase, UseCaseConfig<*>>().apply {
+            useCases.forEach {
+                put(it, it.currentConfig)
+            }
+        }
+        return useCaseConfigMap
+    }
+
+    private fun getUseCaseConfigToOutputSizesMap(
+        useCaseConfigs: List<UseCaseConfig<*>>
+    ): Map<UseCaseConfig<*>, List<Size>> {
+        val resultMap = mutableMapOf<UseCaseConfig<*>, List<Size>>().apply {
+            useCaseConfigs.forEach {
+                put(it, supportedSizes.toList())
+            }
+        }
+
+        return resultMap
+    }
+
+    /**
+     * Helper function that returns whether size is <= maxSize
+     *
+     */
+    private fun sizeIsAtMost(size: Size, maxSize: Size): Boolean {
+        return (size.height * size.width) <= (maxSize.height * maxSize.width)
+    }
+
+    // //////////////////////////////////////////////////////////////////////////////////////////
+    //
+    // Other tests
+    //
+    // //////////////////////////////////////////////////////////////////////////////////////////
+
     @Test
     fun maximumSizeForImageFormat() {
         setupCamera(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY)
@@ -1380,981 +1501,6 @@
     }
 
     @Test
-    fun isAspectRatioMatchWithSupportedMod16Resolution() {
-        setupCamera(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED)
-        val supportedSurfaceCombination = SupportedSurfaceCombination(
-            context, fakeCameraMetadata,
-            mockEncoderProfilesAdapter
-        )
-        val preview = Preview.Builder()
-            .setTargetAspectRatio(AspectRatio.RATIO_16_9)
-            .setDefaultResolution(mod16Size)
-            .build()
-        val imageCapture = ImageCapture.Builder()
-            .setTargetAspectRatio(AspectRatio.RATIO_16_9)
-            .setDefaultResolution(mod16Size)
-            .build()
-        val useCases: MutableList<UseCase> = ArrayList()
-        useCases.add(preview)
-        useCases.add(imageCapture)
-        val useCaseToConfigMap = Configs.useCaseConfigMapWithDefaultSettingsFromUseCaseList(
-            cameraFactory!!.getCamera(fakeCameraMetadata.camera.value).cameraInfoInternal,
-            useCases,
-            useCaseConfigFactory
-        )
-        val suggestedStreamSpecMap: Map<UseCaseConfig<*>, StreamSpec> =
-            supportedSurfaceCombination.getSuggestedStreamSpecifications(
-                false,
-                emptyList(),
-                ArrayList(useCaseToConfigMap.values)
-            )
-        assertThat(suggestedStreamSpecMap).containsEntry(
-            useCaseToConfigMap[preview],
-            mod16SizeStreamSpec
-        )
-        assertThat(suggestedStreamSpecMap).containsEntry(
-            useCaseToConfigMap[imageCapture],
-            mod16SizeStreamSpec
-        )
-    }
-
-    @Test
-    fun sortByCompareSizesByArea_canSortSizesCorrectly() {
-        val sizes = arrayOfNulls<Size>(supportedSizes.size)
-
-        // Generates a unsorted array from mSupportedSizes.
-        val centerIndex = supportedSizes.size / 2
-        // Puts 2nd half sizes in the front
-        if (supportedSizes.size - centerIndex >= 0) {
-            System.arraycopy(
-                supportedSizes,
-                centerIndex, sizes, 0,
-                supportedSizes.size - centerIndex
-            )
-        }
-        // Puts 1st half sizes inversely in the tail
-        for (j in centerIndex - 1 downTo 0) {
-            sizes[supportedSizes.size - j - 1] = supportedSizes[j]
-        }
-
-        // The testing sizes array will be equal to mSupportedSizes after sorting.
-        Arrays.sort(sizes, CompareSizesByArea(true))
-        assertThat(listOf(*sizes)).isEqualTo(listOf(*supportedSizes))
-    }
-
-    @Test
-    fun supportedOutputSizes_noConfigSettings() {
-        setupCamera(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED)
-        val supportedSurfaceCombination = SupportedSurfaceCombination(
-            context, fakeCameraMetadata,
-            mockEncoderProfilesAdapter
-        )
-        val useCase = FakeUseCaseConfig.Builder().build()
-
-        // There is default minimum size 640x480 setting. Sizes smaller than 640x480 will be
-        // removed. No any aspect ratio related setting. The returned sizes list will be sorted in
-        // descending order.
-        val resultList: List<Size?> = supportedSurfaceCombination.getSupportedOutputSizes(
-            useCase.currentConfig
-        )
-        val expectedList = listOf(
-            Size(4032, 3024),
-            Size(3840, 2160),
-            Size(1920, 1440),
-            Size(1920, 1080),
-            Size(1280, 960),
-            Size(1280, 720),
-            Size(960, 544),
-            Size(800, 450),
-            Size(640, 480)
-        )
-        assertThat(resultList).isEqualTo(expectedList)
-    }
-
-    @Test
-    fun supportedOutputSizes_aspectRatio4x3() {
-        setupCamera(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED)
-        val supportedSurfaceCombination = SupportedSurfaceCombination(
-            context, fakeCameraMetadata,
-            mockEncoderProfilesAdapter
-        )
-        val useCase = FakeUseCaseConfig.Builder().setTargetAspectRatio(AspectRatio.RATIO_4_3)
-            .build()
-
-        // There is default minimum size 640x480 setting. Sizes smaller than 640x480 will be
-        // removed. Sizes of aspect ratio 4/3 will be in front of the returned sizes list and the
-        // list is sorted in descending order. Other items will be put in the following that are
-        // sorted by aspect ratio delta and then area size.
-        val resultList: List<Size?> = supportedSurfaceCombination.getSupportedOutputSizes(
-            useCase.currentConfig
-        )
-        val expectedList = listOf( // Matched AspectRatio items, sorted by area size.
-            Size(4032, 3024),
-            Size(1920, 1440),
-            Size(1280, 960),
-            Size(
-                640,
-                480
-            ), // Mismatched AspectRatio items, sorted by aspect ratio delta then area size.
-            Size(3840, 2160),
-            Size(1920, 1080),
-            Size(1280, 720),
-            Size(960, 544),
-            Size(800, 450)
-        )
-        assertThat(resultList).isEqualTo(expectedList)
-    }
-
-    @Test
-    fun supportedOutputSizes_aspectRatio16x9() {
-        setupCamera(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED)
-        val supportedSurfaceCombination = SupportedSurfaceCombination(
-            context, fakeCameraMetadata,
-            mockEncoderProfilesAdapter
-        )
-        val useCase = FakeUseCaseConfig.Builder().setTargetAspectRatio(
-            AspectRatio.RATIO_16_9
-        ).build()
-
-        // There is default minimum size 640x480 setting. Sizes smaller than 640x480 will be
-        // removed. Sizes of aspect ratio 16/9 will be in front of the returned sizes list and the
-        // list is sorted in descending order. Other items will be put in the following that are
-        // sorted by aspect ratio delta and then area size.
-        val resultList: List<Size?> = supportedSurfaceCombination.getSupportedOutputSizes(
-            useCase.currentConfig
-        )
-        val expectedList = listOf( // Matched AspectRatio items, sorted by area size.
-            Size(3840, 2160),
-            Size(1920, 1080),
-            Size(1280, 720),
-            Size(960, 544),
-            Size(
-                800,
-                450
-            ), // Mismatched AspectRatio items, sorted by aspect ratio delta then area size.
-            Size(4032, 3024),
-            Size(1920, 1440),
-            Size(1280, 960),
-            Size(640, 480)
-        )
-        assertThat(resultList).isEqualTo(expectedList)
-    }
-
-    @Test
-    fun supportedOutputSizes_targetResolution1080x1920InRotation0() {
-        setupCamera(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED)
-        val supportedSurfaceCombination = SupportedSurfaceCombination(
-            context, fakeCameraMetadata,
-            mockEncoderProfilesAdapter
-        )
-        val useCase = FakeUseCaseConfig.Builder().setTargetResolution(
-            Size(1080, 1920)
-        ).build()
-
-        // Unnecessary big enough sizes will be removed from the result list. There is default
-        // minimum size 640x480 setting. Sizes smaller than 640x480 will also be removed. The
-        // target resolution will be calibrated by default target rotation 0 degree. The
-        // auto-resolution mechanism will try to select the sizes which aspect ratio is nearest
-        // to the aspect ratio of target resolution in priority. Therefore, sizes of aspect ratio
-        // 16/9 will be in front of the returned sizes list and the list is sorted in descending
-        // order. Other items will be put in the following that are sorted by aspect ratio delta
-        // and then area size.
-        val resultList: List<Size?> = supportedSurfaceCombination.getSupportedOutputSizes(
-            useCase.currentConfig
-        )
-        val expectedList = listOf( // Matched AspectRatio items, sorted by area size.
-            Size(1920, 1080),
-            Size(1280, 720),
-            Size(960, 544),
-            Size(
-                800,
-                450
-            ), // Mismatched AspectRatio items, sorted by aspect ratio delta then area size.
-            Size(1920, 1440),
-            Size(1280, 960),
-            Size(640, 480)
-        )
-        assertThat(resultList).isEqualTo(expectedList)
-    }
-
-    @Test
-    fun supportedOutputSizes_targetResolutionLargerThan640x480() {
-        setupCamera(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED)
-        val supportedSurfaceCombination = SupportedSurfaceCombination(
-            context, fakeCameraMetadata,
-            mockEncoderProfilesAdapter
-        )
-        val useCase = FakeUseCaseConfig.Builder().setTargetRotation(
-            Surface.ROTATION_90
-        ).setTargetResolution(Size(1280, 960)).build()
-
-        // Unnecessary big enough sizes will be removed from the result list. There is default
-        // minimum size 640x480 setting. Target resolution larger than 640x480 won't overwrite
-        // minimum size setting. Sizes smaller than 640x480 will be removed. The auto-resolution
-        // mechanism will try to select the sizes which aspect ratio is nearest to the aspect
-        // ratio of target resolution in priority. Therefore, sizes of aspect ratio 4/3 will be
-        // in front of the returned sizes list and the list is sorted in descending order. Other
-        // items will be put in the following that are sorted by aspect ratio delta and then area
-        // size.
-        val resultList: List<Size?> = supportedSurfaceCombination.getSupportedOutputSizes(
-            useCase.currentConfig
-        )
-        val expectedList = listOf( // Matched AspectRatio items, sorted by area size.
-            Size(1280, 960),
-            Size(
-                640,
-                480
-            ), // Mismatched AspectRatio items, sorted by aspect ratio delta then area size.
-            Size(1920, 1080),
-            Size(1280, 720),
-            Size(960, 544),
-            Size(800, 450)
-        )
-        assertThat(resultList).isEqualTo(expectedList)
-    }
-
-    @Test
-    fun supportedOutputSizes_targetResolutionSmallerThan640x480() {
-        setupCamera(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED)
-        val supportedSurfaceCombination = SupportedSurfaceCombination(
-            context, fakeCameraMetadata,
-            mockEncoderProfilesAdapter
-        )
-        val useCase = FakeUseCaseConfig.Builder().setTargetRotation(
-            Surface.ROTATION_90
-        ).setTargetResolution(Size(320, 240)).build()
-
-        // Unnecessary big enough sizes will be removed from the result list. Minimum size will
-        // be overwritten as 320x240. Sizes smaller than 320x240 will also be removed. The
-        // auto-resolution mechanism will try to select the sizes which aspect ratio is nearest
-        // to the aspect ratio of target resolution in priority. Therefore, sizes of aspect ratio
-        // 4/3 will be in front of the returned sizes list and the list is sorted in descending
-        // order. Other items will be put in the following that are sorted by aspect ratio delta
-        // and then area size.
-        val resultList: List<Size?> = supportedSurfaceCombination.getSupportedOutputSizes(
-            useCase.currentConfig
-        )
-        val expectedList = listOf( // Matched AspectRatio items, sorted by area size.
-            Size(
-                320,
-                240
-            ), // Mismatched AspectRatio items, sorted by aspect ratio delta then area size.
-            Size(800, 450)
-        )
-        assertThat(resultList).isEqualTo(expectedList)
-    }
-
-    @Test
-    fun supportedOutputSizes_targetResolution1800x1440NearTo4x3() {
-        setupCamera(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED)
-        val supportedSurfaceCombination = SupportedSurfaceCombination(
-            context, fakeCameraMetadata,
-            mockEncoderProfilesAdapter
-        )
-        val useCase = FakeUseCaseConfig.Builder().setTargetRotation(
-            Surface.ROTATION_90
-        ).setTargetResolution(Size(1800, 1440)).build()
-
-        // Unnecessary big enough sizes will be removed from the result list. There is default
-        // minimum size 640x480 setting. Sizes smaller than 640x480 will also be removed. The
-        // auto-resolution mechanism will try to select the sizes which aspect ratio is nearest
-        // to the aspect ratio of target resolution in priority. Size 1800x1440 is near to 4/3
-        // therefore, sizes of aspect ratio 4/3 will be in front of the returned sizes list and
-        // the list is sorted in descending order.
-        val resultList: List<Size?> = supportedSurfaceCombination.getSupportedOutputSizes(
-            useCase.currentConfig
-        )
-        val expectedList = listOf( // Sizes of 4/3 are near to aspect ratio of 1800/1440
-            Size(1920, 1440),
-            Size(1280, 960),
-            Size(640, 480), // Sizes of 16/9 are far to aspect ratio of 1800/1440
-            Size(3840, 2160),
-            Size(1920, 1080),
-            Size(1280, 720),
-            Size(960, 544),
-            Size(800, 450)
-        )
-        assertThat(resultList).isEqualTo(expectedList)
-    }
-
-    @Test
-    fun supportedOutputSizes_targetResolution1280x600NearTo16x9() {
-        setupCamera(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED)
-        val supportedSurfaceCombination = SupportedSurfaceCombination(
-            context, fakeCameraMetadata,
-            mockEncoderProfilesAdapter
-        )
-        val useCase = FakeUseCaseConfig.Builder().setTargetResolution(
-            Size(1280, 600)
-        ).setTargetRotation(Surface.ROTATION_90).build()
-
-        // Unnecessary big enough sizes will be removed from the result list. There is default
-        // minimum size 640x480 setting. Sizes smaller than 640x480 will also be removed. The
-        // auto-resolution mechanism will try to select the sizes which aspect ratio is nearest
-        // to the aspect ratio of target resolution in priority. Size 1280x600 is near to 16/9,
-        // therefore, sizes of aspect ratio 16/9 will be in front of the returned sizes list and
-        // the list is sorted in descending order.
-        val resultList: List<Size?> = supportedSurfaceCombination.getSupportedOutputSizes(
-            useCase.currentConfig
-        )
-        val expectedList = listOf( // Sizes of 16/9 are near to aspect ratio of 1280/600
-            Size(1280, 720),
-            Size(960, 544),
-            Size(800, 450), // Sizes of 4/3 are far to aspect ratio of 1280/600
-            Size(1280, 960),
-            Size(640, 480)
-        )
-        assertThat(resultList).isEqualTo(expectedList)
-    }
-
-    @Test
-    fun supportedOutputSizes_maxResolution1280x720() {
-        setupCamera(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED)
-        val supportedSurfaceCombination = SupportedSurfaceCombination(
-            context, fakeCameraMetadata,
-            mockEncoderProfilesAdapter
-        )
-        val useCase = FakeUseCaseConfig.Builder().setMaxResolution(Size(1280, 720)).build()
-
-        // There is default minimum size 640x480 setting. Sizes smaller than 640x480 or
-        // larger than 1280x720 will be removed. The returned sizes list will be sorted in
-        // descending order.
-        val resultList: List<Size?> = supportedSurfaceCombination.getSupportedOutputSizes(
-            useCase.currentConfig
-        )
-        val expectedList = listOf(
-            Size(1280, 720),
-            Size(960, 544),
-            Size(800, 450),
-            Size(640, 480)
-        )
-        assertThat(resultList).isEqualTo(expectedList)
-    }
-
-    @Test
-    fun supportedOutputSizes_setCustomOrderedResolutions() {
-        setupCamera(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED)
-        val supportedSurfaceCombination = SupportedSurfaceCombination(
-            context, fakeCameraMetadata,
-            mockEncoderProfilesAdapter
-        )
-        val customOrderedResolutions = listOf(
-            Size(640, 480),
-            Size(1280, 720),
-            Size(1920, 1080),
-            Size(3840, 2160),
-        )
-        val useCase = FakeUseCaseConfig.Builder()
-            .setCustomOrderedResolutions(customOrderedResolutions)
-            .setTargetResolution(Size(1280, 720))
-            .setMaxResolution(Size(1920, 1440))
-            .setDefaultResolution(Size(1280, 720))
-            .setSupportedResolutions(
-                listOf(
-                    Pair.create(
-                        ImageFormat.PRIVATE, arrayOf(
-                            Size(800, 450),
-                            Size(640, 480),
-                            Size(320, 240),
-                        )
-                    )
-                )
-            ).build()
-
-        // Custom ordered resolutions is fully respected, meaning it will not be sorted or filtered
-        // by other configurations such as max/default/target/supported resolutions.
-        val resultList: List<Size?> = supportedSurfaceCombination.getSupportedOutputSizes(
-            useCase.currentConfig
-        )
-        assertThat(resultList).containsExactlyElementsIn(customOrderedResolutions).inOrder()
-    }
-
-    @Test
-    fun supportedOutputSizes_defaultResolution1280x720_noTargetResolution() {
-        setupCamera(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED)
-        val supportedSurfaceCombination = SupportedSurfaceCombination(
-            context, fakeCameraMetadata,
-            mockEncoderProfilesAdapter
-        )
-        val useCase = FakeUseCaseConfig.Builder().setDefaultResolution(
-            Size(
-                1280,
-                720
-            )
-        ).build()
-
-        // There is default minimum size 640x480 setting. Sizes smaller than 640x480 will be
-        // removed. If there is no target resolution setting, it will be overwritten by default
-        // resolution as 1280x720. Unnecessary big enough sizes will also be removed. The
-        // returned sizes list will be sorted in descending order.
-        val resultList: List<Size?> = supportedSurfaceCombination.getSupportedOutputSizes(
-            useCase.currentConfig
-        )
-        val expectedList = listOf(
-            Size(1280, 720),
-            Size(960, 544),
-            Size(800, 450),
-            Size(640, 480)
-        )
-        assertThat(resultList).isEqualTo(expectedList)
-    }
-
-    @Test
-    fun supportedOutputSizes_defaultResolution1280x720_targetResolution1920x1080() {
-        setupCamera(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED)
-        val supportedSurfaceCombination = SupportedSurfaceCombination(
-            context, fakeCameraMetadata,
-            mockEncoderProfilesAdapter
-        )
-        val useCase = FakeUseCaseConfig.Builder().setDefaultResolution(
-            Size(1280, 720)
-        ).setTargetRotation(Surface.ROTATION_90).setTargetResolution(
-            Size(1920, 1080)
-        ).build()
-
-        // There is default minimum size 640x480 setting. Sizes smaller than 640x480 will be
-        // removed. There is target resolution 1920x1080, it won't be overwritten by default
-        // resolution 1280x720. Unnecessary big enough sizes will also be removed. Sizes of
-        // aspect ratio 16/9 will be in front of the returned sizes list and the list is sorted
-        // in descending order.  Other items will be put in the following that are sorted by
-        // aspect ratio delta and then area size.
-        val resultList: List<Size?> = supportedSurfaceCombination.getSupportedOutputSizes(
-            useCase.currentConfig
-        )
-        val expectedList = listOf( // Matched AspectRatio items, sorted by area size.
-            Size(1920, 1080),
-            Size(1280, 720),
-            Size(960, 544),
-            Size(
-                800,
-                450
-            ), // Mismatched AspectRatio items, sorted by aspect ratio delta then area size.
-            Size(1920, 1440),
-            Size(1280, 960),
-            Size(640, 480)
-        )
-        assertThat(resultList).isEqualTo(expectedList)
-    }
-
-    @Test
-    fun supportedOutputSizes_fallbackToGuaranteedResolution_whenNotFulfillConditions() {
-        setupCamera(
-            CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED, arrayOf(
-                Size(640, 480),
-                Size(320, 240),
-                Size(320, 180),
-                Size(256, 144)
-            )
-        )
-        val supportedSurfaceCombination = SupportedSurfaceCombination(
-            context, fakeCameraMetadata,
-            mockEncoderProfilesAdapter
-        )
-        val useCase = FakeUseCaseConfig.Builder().setTargetResolution(
-            Size(1920, 1080)
-        ).setTargetRotation(Surface.ROTATION_90).build()
-
-        // There is default minimum size 640x480 setting. Sizes smaller than 640x480 will be
-        // removed. There is target resolution 1920x1080 (16:9). Even 640x480 does not match 16:9
-        // requirement, it will still be returned to use.
-        val resultList: List<Size?> = supportedSurfaceCombination.getSupportedOutputSizes(
-            useCase.currentConfig
-        )
-        val expectedList = listOf(Size(640, 480))
-        assertThat(resultList).isEqualTo(expectedList)
-    }
-
-    @Test
-    fun supportedOutputSizes_whenMaxSizeSmallerThanDefaultMiniSize() {
-        setupCamera(
-            CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED, arrayOf(
-                Size(640, 480),
-                Size(320, 240),
-                Size(320, 180),
-                Size(256, 144)
-            )
-        )
-        val supportedSurfaceCombination = SupportedSurfaceCombination(
-            context, fakeCameraMetadata,
-            mockEncoderProfilesAdapter
-        )
-        val useCase = FakeUseCaseConfig.Builder().setMaxResolution(
-            Size(320, 240)
-        ).build()
-
-        // There is default minimum size 640x480 setting. Originally, sizes smaller than 640x480
-        // will be removed. Due to maximal size bound is smaller than the default minimum size
-        // bound and it is also smaller than 640x480, the default minimum size bound will be
-        // ignored. Then, sizes equal to or smaller than 320x240 will be kept in the result list.
-        val resultList: List<Size?> = supportedSurfaceCombination.getSupportedOutputSizes(
-            useCase.currentConfig
-        )
-        val expectedList = listOf(
-            Size(320, 240),
-            Size(320, 180),
-            Size(256, 144)
-        )
-        assertThat(resultList).isEqualTo(expectedList)
-    }
-
-    @Test
-    fun supportedOutputSizes_whenMaxSizeSmallerThanSmallTargetResolution() {
-        setupCamera(
-            CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED, arrayOf(
-                Size(640, 480),
-                Size(320, 240),
-                Size(320, 180),
-                Size(256, 144)
-            )
-        )
-        val supportedSurfaceCombination = SupportedSurfaceCombination(
-            context, fakeCameraMetadata,
-            mockEncoderProfilesAdapter
-        )
-        val useCase = FakeUseCaseConfig.Builder().setMaxResolution(
-            Size(320, 180)
-        ).setTargetResolution(Size(320, 240)).setTargetRotation(
-            Surface.ROTATION_90
-        ).build()
-
-        // The default minimum size 640x480 will be overwritten by the target resolution 320x240.
-        // Originally, sizes smaller than 320x240 will be removed. Due to maximal size bound is
-        // smaller than the minimum size bound and it is also smaller than 640x480, the minimum
-        // size bound will be ignored. Then, sizes equal to or smaller than 320x180 will be kept
-        // in the result list.
-        val resultList: List<Size?> = supportedSurfaceCombination.getSupportedOutputSizes(
-            useCase.currentConfig
-        )
-        val expectedList = listOf(
-            Size(320, 180),
-            Size(256, 144)
-        )
-        assertThat(resultList).isEqualTo(expectedList)
-    }
-
-    @Test
-    fun supportedOutputSizes_whenBothMaxAndTargetResolutionsSmallerThan640x480() {
-        setupCamera(
-            CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED, arrayOf(
-                Size(640, 480),
-                Size(320, 240),
-                Size(320, 180),
-                Size(256, 144)
-            )
-        )
-        val supportedSurfaceCombination = SupportedSurfaceCombination(
-            context, fakeCameraMetadata,
-            mockEncoderProfilesAdapter
-        )
-        val useCase = FakeUseCaseConfig.Builder().setMaxResolution(
-            Size(320, 240)
-        ).setTargetResolution(Size(320, 180)).setTargetRotation(
-            Surface.ROTATION_90
-        ).build()
-
-        // The default minimum size 640x480 will be overwritten by the target resolution 320x180.
-        // Originally, sizes smaller than 320x180 will be removed. Due to maximal size bound is
-        // smaller than the minimum size bound and it is also smaller than 640x480, the minimum
-        // size bound will be ignored. Then, all sizes equal to or smaller than 320x320 will be
-        // kept in the result list.
-        val resultList: List<Size?> = supportedSurfaceCombination.getSupportedOutputSizes(
-            useCase.currentConfig
-        )
-        val expectedList = listOf(
-            Size(320, 180),
-            Size(256, 144),
-            Size(320, 240)
-        )
-        assertThat(resultList).isEqualTo(expectedList)
-    }
-
-    @Test
-    fun supportedOutputSizes_whenMaxSizeSmallerThanBigTargetResolution() {
-        setupCamera(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED)
-        val supportedSurfaceCombination = SupportedSurfaceCombination(
-            context, fakeCameraMetadata,
-            mockEncoderProfilesAdapter
-        )
-        val useCase = FakeUseCaseConfig.Builder().setMaxResolution(
-            Size(1920, 1080)
-        ).setTargetResolution(Size(3840, 2160)).setTargetRotation(
-            Surface.ROTATION_90
-        ).build()
-
-        // Because the target size 3840x2160 is larger than 640x480, it won't overwrite the
-        // default minimum size 640x480. Sizes smaller than 640x480 will be removed. The
-        // auto-resolution mechanism will try to select the sizes which aspect ratio is nearest
-        // to the aspect ratio of target resolution in priority. Therefore, sizes of aspect ratio
-        // 16/9 will be in front of the returned sizes list and the list is sorted in descending
-        // order. Other items will be put in the following that are sorted by aspect ratio delta
-        // and then area size.
-        val resultList: List<Size?> = supportedSurfaceCombination.getSupportedOutputSizes(
-            useCase.currentConfig
-        )
-        val expectedList = listOf( // Matched AspectRatio items, sorted by area size.
-            Size(1920, 1080),
-            Size(1280, 720),
-            Size(960, 544),
-            Size(
-                800,
-                450
-            ), // Mismatched AspectRatio items, sorted by aspect ratio delta then area size.
-            Size(1280, 960),
-            Size(640, 480)
-        )
-        assertThat(resultList).isEqualTo(expectedList)
-    }
-
-    @Test
-    fun supportedOutputSizes_whenNoSizeBetweenMaxSizeAndTargetResolution() {
-        setupCamera(
-            CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED, arrayOf(
-                Size(640, 480),
-                Size(320, 240),
-                Size(320, 180),
-                Size(256, 144)
-            )
-        )
-        val supportedSurfaceCombination = SupportedSurfaceCombination(
-            context, fakeCameraMetadata,
-            mockEncoderProfilesAdapter
-        )
-        val useCase = FakeUseCaseConfig.Builder().setMaxResolution(
-            Size(320, 200)
-        ).setTargetResolution(Size(320, 190)).setTargetRotation(
-            Surface.ROTATION_90
-        ).build()
-
-        // The default minimum size 640x480 will be overwritten by the target resolution 320x190.
-        // Originally, sizes smaller than 320x190 will be removed. Due to there is no available
-        // size between the maximal size and the minimum size bound and the maximal size is
-        // smaller than 640x480, the default minimum size bound will be ignored. Then, sizes
-        // equal to or smaller than 320x200 will be kept in the result list.
-        val resultList: List<Size?> = supportedSurfaceCombination.getSupportedOutputSizes(
-            useCase.currentConfig
-        )
-        val expectedList = listOf(
-            Size(320, 180),
-            Size(256, 144)
-        )
-        assertThat(resultList).isEqualTo(expectedList)
-    }
-
-    @Test
-    fun supportedOutputSizes_whenTargetResolutionSmallerThanAnySize() {
-        setupCamera(
-            CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED, arrayOf(
-                Size(640, 480),
-                Size(320, 240),
-                Size(320, 180),
-                Size(256, 144)
-            )
-        )
-        val supportedSurfaceCombination = SupportedSurfaceCombination(
-            context, fakeCameraMetadata,
-            mockEncoderProfilesAdapter
-        )
-        val useCase = FakeUseCaseConfig.Builder().setTargetResolution(
-            Size(192, 144)
-        ).setTargetRotation(Surface.ROTATION_90).build()
-
-        // The default minimum size 640x480 will be overwritten by the target resolution 192x144.
-        // Because 192x144 is smaller than any size in the supported list, no one will be
-        // filtered out by it. The result list will only keep one big enough size of aspect ratio
-        // 4:3 and 16:9.
-        val resultList: List<Size?> = supportedSurfaceCombination.getSupportedOutputSizes(
-            useCase.currentConfig
-        )
-        val expectedList = listOf(
-            Size(320, 240),
-            Size(256, 144)
-        )
-        assertThat(resultList).isEqualTo(expectedList)
-    }
-
-    @Test
-    fun supportedOutputSizes_whenMaxResolutionSmallerThanAnySize() {
-        setupCamera(
-            CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY, arrayOf(
-                Size(640, 480),
-                Size(320, 240),
-                Size(320, 180),
-                Size(256, 144)
-            )
-        )
-        val supportedSurfaceCombination = SupportedSurfaceCombination(
-            context, fakeCameraMetadata,
-            mockEncoderProfilesAdapter
-        )
-        val useCase = FakeUseCaseConfig.Builder().setMaxResolution(
-            Size(192, 144)
-        ).build()
-
-        // All sizes will be filtered out by the max resolution 192x144 setting and an
-        // IllegalArgumentException will be thrown.
-        assertThrows(IllegalArgumentException::class.java) {
-            supportedSurfaceCombination.getSupportedOutputSizes(useCase.currentConfig)
-        }
-    }
-
-    @Test
-    fun supportedOutputSizes_whenMod16IsIgnoredForSmallSizes() {
-        setupCamera(
-            CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED, arrayOf(
-                Size(640, 480),
-                Size(320, 240),
-                Size(320, 180),
-                Size(296, 144),
-                Size(256, 144)
-            )
-        )
-        val supportedSurfaceCombination = SupportedSurfaceCombination(
-            context, fakeCameraMetadata,
-            mockEncoderProfilesAdapter
-        )
-        val useCase = FakeUseCaseConfig.Builder().setTargetResolution(
-            Size(185, 90)
-        ).setTargetRotation(Surface.ROTATION_90).build()
-
-        // The default minimum size 640x480 will be overwritten by the target resolution 185x90
-        // (18.5:9). If mod 16 calculation is not ignored for the sizes smaller than 640x480, the
-        // size 256x144 will be considered to match 18.5:9 and then become the first item in the
-        // result list. After ignoring mod 16 calculation for small sizes, 256x144 will still be
-        // kept as a 16:9 resolution as the result.
-        val resultList: List<Size?> = supportedSurfaceCombination.getSupportedOutputSizes(
-            useCase.currentConfig
-        )
-        val expectedList = listOf(
-            Size(296, 144),
-            Size(256, 144),
-            Size(320, 240)
-        )
-        assertThat(resultList).isEqualTo(expectedList)
-    }
-
-    @Test
-    fun supportedOutputSizes_whenOneMod16SizeClosestToTargetResolution() {
-        setupCamera(
-            CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY, arrayOf(
-                Size(1920, 1080),
-                Size(1440, 1080),
-                Size(1280, 960),
-                Size(1280, 720),
-                Size(864, 480), // This is a 16:9 mod16 size that is closest to 2016x1080
-                Size(768, 432),
-                Size(640, 480),
-                Size(640, 360),
-                Size(480, 360),
-                Size(384, 288)
-            )
-        )
-        val supportedSurfaceCombination = SupportedSurfaceCombination(
-            context, fakeCameraMetadata,
-            mockEncoderProfilesAdapter
-        )
-        val useCase = FakeUseCaseConfig.Builder().setTargetResolution(
-            Size(1080, 2016)
-        ).build()
-        val resultList: List<Size?> = supportedSurfaceCombination.getSupportedOutputSizes(
-            useCase.currentConfig
-        )
-        val expectedList = listOf(
-            Size(1920, 1080),
-            Size(1280, 720),
-            Size(864, 480),
-            Size(768, 432),
-            Size(1440, 1080),
-            Size(1280, 960),
-            Size(640, 480)
-        )
-        assertThat(resultList).isEqualTo(expectedList)
-    }
-
-    @Test
-    fun supportedOutputSizesWithPortraitPixelArraySize_aspectRatio16x9() {
-        val supportedSizes = arrayOf(
-            Size(1080, 1920),
-            Size(1080, 1440),
-            Size(960, 1280),
-            Size(720, 1280),
-            Size(1280, 720),
-            Size(480, 640),
-            Size(640, 480),
-            Size(360, 480)
-        )
-
-        // Sets the sensor orientation as 0 and pixel array size as a portrait size to simulate a
-        // phone device which majorly supports portrait output sizes.
-        setupCamera(
-            CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
-            sensorOrientation0, portraitPixelArraySize, supportedSizes, null
-        )
-        val supportedSurfaceCombination = SupportedSurfaceCombination(
-            context, fakeCameraMetadata,
-            mockEncoderProfilesAdapter
-        )
-        val useCase = FakeUseCaseConfig.Builder().setTargetAspectRatio(
-            AspectRatio.RATIO_16_9
-        ).build()
-
-        // There is default minimum size 640x480 setting. Sizes smaller than 640x480 will be
-        // removed. Due to the pixel array size is portrait, sizes of aspect ratio 9/16 will be in
-        // front of the returned sizes list and the list is sorted in descending order. Other
-        // items will be put in the following that are sorted by aspect ratio delta and then area
-        // size.
-        val resultList: List<Size?> = supportedSurfaceCombination.getSupportedOutputSizes(
-            useCase.currentConfig
-        )
-        val expectedList = listOf( // Matched AspectRatio items, sorted by area size.
-            Size(1080, 1920),
-            Size(
-                720,
-                1280
-            ), // Mismatched AspectRatio items, sorted by aspect ratio delta then area size.
-            Size(1080, 1440),
-            Size(960, 1280),
-            Size(480, 640),
-            Size(640, 480),
-            Size(1280, 720)
-        )
-        assertThat(resultList).isEqualTo(expectedList)
-    }
-
-    @Test
-    fun supportedOutputSizesOnTabletWithPortraitPixelArraySize_aspectRatio16x9() {
-        val supportedSizes = arrayOf(
-            Size(1080, 1920),
-            Size(1080, 1440),
-            Size(960, 1280),
-            Size(720, 1280),
-            Size(1280, 720),
-            Size(480, 640),
-            Size(640, 480),
-            Size(360, 480)
-        )
-
-        // Sets the sensor orientation as 90 and pixel array size as a portrait size to simulate a
-        // tablet device which majorly supports portrait output sizes.
-        setupCamera(
-            CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
-            sensorOrientation90, portraitPixelArraySize, supportedSizes, null
-        )
-        val supportedSurfaceCombination = SupportedSurfaceCombination(
-            context, fakeCameraMetadata,
-            mockEncoderProfilesAdapter
-        )
-        val useCase = FakeUseCaseConfig.Builder().setTargetAspectRatio(
-            AspectRatio.RATIO_16_9
-        ).build()
-
-        // There is default minimum size 640x480 setting. Sizes smaller than 640x480 will be
-        // removed. Due to the pixel array size is portrait, sizes of aspect ratio 9/16 will be in
-        // front of the returned sizes list and the list is sorted in descending order. Other
-        // items will be put in the following that are sorted by aspect ratio delta and then area
-        // size.
-        val resultList: List<Size?> = supportedSurfaceCombination.getSupportedOutputSizes(
-            useCase.currentConfig
-        )
-        val expectedList = listOf( // Matched AspectRatio items, sorted by area size.
-            Size(1080, 1920),
-            Size(
-                720,
-                1280
-            ), // Mismatched AspectRatio items, sorted by aspect ratio delta then area size.
-            Size(1080, 1440),
-            Size(960, 1280),
-            Size(480, 640),
-            Size(640, 480),
-            Size(1280, 720)
-        )
-        assertThat(resultList).isEqualTo(expectedList)
-    }
-
-    @Test
-    fun supportedOutputSizesOnTablet_aspectRatio16x9() {
-        setupCamera(
-            CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
-            sensorOrientation0, landscapePixelArraySize, supportedSizes, null
-        )
-        val supportedSurfaceCombination = SupportedSurfaceCombination(
-            context, fakeCameraMetadata,
-            mockEncoderProfilesAdapter
-        )
-        val useCase = FakeUseCaseConfig.Builder().setTargetAspectRatio(
-            AspectRatio.RATIO_16_9
-        ).build()
-
-        // There is default minimum size 640x480 setting. Sizes smaller than 640x480 will be
-        // removed. Sizes of aspect ratio 16/9 will be in front of the returned sizes list and the
-        // list is sorted in descending order. Other items will be put in the following that are
-        // sorted by aspect ratio delta and then area size.
-        val resultList: List<Size?> = supportedSurfaceCombination.getSupportedOutputSizes(
-            useCase.currentConfig
-        )
-        val expectedList = listOf( // Matched AspectRatio items, sorted by area size.
-            Size(3840, 2160),
-            Size(1920, 1080),
-            Size(1280, 720),
-            Size(960, 544),
-            Size(
-                800,
-                450
-            ), // Mismatched AspectRatio items, sorted by aspect ratio delta then area size.
-            Size(4032, 3024),
-            Size(1920, 1440),
-            Size(1280, 960),
-            Size(640, 480)
-        )
-        assertThat(resultList).isEqualTo(expectedList)
-    }
-
-    @Test
-    fun supportedOutputSizesOnTabletWithPortraitSizes_aspectRatio16x9() {
-        val supportedSizes = arrayOf(
-            Size(1920, 1080),
-            Size(1440, 1080),
-            Size(1280, 960),
-            Size(1280, 720),
-            Size(720, 1280),
-            Size(640, 480),
-            Size(480, 640),
-            Size(480, 360)
-        )
-        setupCamera(
-            CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
-            sensorOrientation0, landscapePixelArraySize, supportedSizes, null
-        )
-        val supportedSurfaceCombination = SupportedSurfaceCombination(
-            context, fakeCameraMetadata,
-            mockEncoderProfilesAdapter
-        )
-        val useCase = FakeUseCaseConfig.Builder().setTargetAspectRatio(
-            AspectRatio.RATIO_16_9
-        ).build()
-
-        // There is default minimum size 640x480 setting. Sizes smaller than 640x480 will be
-        // removed. Sizes of aspect ratio 16/9 will be in front of the returned sizes list and the
-        // list is sorted in descending order. Other items will be put in the following that are
-        // sorted by aspect ratio delta and then area size.
-        val resultList: List<Size?> = supportedSurfaceCombination.getSupportedOutputSizes(
-            useCase.currentConfig
-        )
-        val expectedList = listOf( // Matched AspectRatio items, sorted by area size.
-            Size(1920, 1080),
-            Size(
-                1280,
-                720
-            ), // Mismatched AspectRatio items, sorted by aspect ratio delta then area size.
-            Size(1440, 1080),
-            Size(1280, 960),
-            Size(640, 480),
-            Size(480, 640),
-            Size(720, 1280)
-        )
-        assertThat(resultList).isEqualTo(expectedList)
-    }
-
-    @Test
     fun determineRecordSizeFromStreamConfigurationMap() {
         // Setup camera with non-integer camera Id
         setupCamera(
@@ -2374,173 +1520,6 @@
         )
     }
 
-    @Test
-    fun canGet640x480_whenAnotherGroupMatchedInMod16Exists() {
-        val supportedSizes = arrayOf(
-            Size(4000, 3000),
-            Size(3840, 2160),
-            Size(1920, 1080),
-            Size(1024, 738), // This will create a 512/269 aspect ratio group that
-            // 640x480 will be considered to match in mod16 condition.
-            Size(800, 600),
-            Size(640, 480),
-            Size(320, 240)
-        )
-        setupCamera(
-            CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
-            sensorOrientation90, landscapePixelArraySize, supportedSizes, null
-        )
-        val supportedSurfaceCombination = SupportedSurfaceCombination(
-            context, fakeCameraMetadata,
-            mockEncoderProfilesAdapter
-        )
-
-        // Sets the target resolution as 640x480 with target rotation as ROTATION_90 because the
-        // sensor orientation is 90.
-        val useCase = FakeUseCaseConfig.Builder().setTargetResolution(
-            vgaSize
-        ).setTargetRotation(Surface.ROTATION_90).build()
-        val suggestedStreamSpecMap = supportedSurfaceCombination.getSuggestedStreamSpecifications(
-            false,
-            emptyList(),
-            listOf(useCase.currentConfig)
-        )
-
-        // Checks 640x480 is final selected for the use case.
-        assertThat(suggestedStreamSpecMap[useCase.currentConfig]?.resolution).isEqualTo(vgaSize)
-    }
-
-    @Test
-    fun canGetSupportedSizeSmallerThan640x480_whenLargerMaxResolutionIsSet() {
-        val supportedSizes = arrayOf(
-            Size(480, 480)
-        )
-        setupCamera(
-            CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
-            sensorOrientation90, landscapePixelArraySize, supportedSizes, null
-        )
-        val supportedSurfaceCombination = SupportedSurfaceCombination(
-            context, fakeCameraMetadata,
-            mockEncoderProfilesAdapter
-        )
-
-        // Sets the max resolution as 720x1280
-        val useCase = FakeUseCaseConfig.Builder().setMaxResolution(displaySize).build()
-        val suggestedStreamSpecMap = supportedSurfaceCombination.getSuggestedStreamSpecifications(
-            false,
-            emptyList(),
-            listOf(useCase.currentConfig)
-        )
-
-        // Checks 480x480 is final selected for the use case.
-        assertThat(suggestedStreamSpecMap[useCase.currentConfig]?.resolution).isEqualTo(
-            Size(480, 480)
-        )
-    }
-
-    @Test
-    fun previewSizeIsSelectedForImageAnalysis_imageCaptureHasNoSetSizeInLimitedDevice() {
-        setupCamera(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED)
-        val supportedSurfaceCombination = SupportedSurfaceCombination(
-            context, fakeCameraMetadata,
-            mockEncoderProfilesAdapter
-        )
-        val preview = Preview.Builder().build()
-        preview.setSurfaceProvider(
-            CameraXExecutors.directExecutor(),
-            SurfaceTextureProvider.createSurfaceTextureProvider(
-                mock()
-            )
-        )
-
-        // ImageCapture has no explicit target resolution setting
-        val imageCapture = ImageCapture.Builder().build()
-
-        // A LEGACY-level above device supports the following configuration.
-        //     PRIV/PREVIEW + YUV/PREVIEW + JPEG/MAXIMUM
-        //
-        // A LIMITED-level above device supports the following configuration.
-        //     PRIV/PREVIEW + YUV/RECORD + JPEG/RECORD
-        //
-        // Even there is a RECORD size target resolution setting for ImageAnalysis, ImageCapture
-        // will still have higher priority to have a MAXIMUM size resolution if the app doesn't
-        // explicitly specify a RECORD size target resolution to ImageCapture.
-        val imageAnalysis = ImageAnalysis.Builder()
-            .setTargetRotation(Surface.ROTATION_90)
-            .setTargetResolution(recordSize)
-            .build()
-        val useCases: MutableList<UseCase> = ArrayList()
-        useCases.add(preview)
-        useCases.add(imageCapture)
-        useCases.add(imageAnalysis)
-        val useCaseToConfigMap = Configs.useCaseConfigMapWithDefaultSettingsFromUseCaseList(
-            cameraFactory!!.getCamera(fakeCameraMetadata.camera.value).cameraInfoInternal,
-            useCases,
-            useCaseConfigFactory
-        )
-        val suggestedStreamSpecMap = supportedSurfaceCombination.getSuggestedStreamSpecifications(
-            false,
-            emptyList(),
-            ArrayList(useCaseToConfigMap.values)
-        )
-        assertThat(suggestedStreamSpecMap[useCaseToConfigMap[imageAnalysis]]?.resolution).isEqualTo(
-            previewSize
-        )
-    }
-
-    @Test
-    fun recordSizeIsSelectedForImageAnalysis_imageCaptureHasExplicitSizeInLimitedDevice() {
-        setupCamera(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED)
-        val supportedSurfaceCombination = SupportedSurfaceCombination(
-            context, fakeCameraMetadata,
-            mockEncoderProfilesAdapter
-        )
-        val preview = Preview.Builder().build()
-        preview.setSurfaceProvider(
-            CameraXExecutors.directExecutor(),
-            SurfaceTextureProvider.createSurfaceTextureProvider(
-                mock()
-            )
-        )
-
-        // ImageCapture has no explicit RECORD size target resolution setting
-        val imageCapture = ImageCapture.Builder()
-            .setTargetRotation(Surface.ROTATION_90)
-            .setTargetResolution(recordSize)
-            .build()
-
-        // A LEGACY-level above device supports the following configuration.
-        //     PRIV/PREVIEW + YUV/PREVIEW + JPEG/MAXIMUM
-        //
-        // A LIMITED-level above device supports the following configuration.
-        //     PRIV/PREVIEW + YUV/RECORD + JPEG/RECORD
-        //
-        // A RECORD can be selected for ImageAnalysis if the ImageCapture has a explicit RECORD
-        // size target resolution setting. It means that the application know the trade-off and
-        // the ImageAnalysis has higher priority to get a larger resolution than ImageCapture.
-        val imageAnalysis = ImageAnalysis.Builder()
-            .setTargetRotation(Surface.ROTATION_90)
-            .setTargetResolution(recordSize)
-            .build()
-        val useCases: MutableList<UseCase> = ArrayList()
-        useCases.add(preview)
-        useCases.add(imageCapture)
-        useCases.add(imageAnalysis)
-        val useCaseToConfigMap = Configs.useCaseConfigMapWithDefaultSettingsFromUseCaseList(
-            cameraFactory!!.getCamera(fakeCameraMetadata.camera.value).cameraInfoInternal,
-            useCases,
-            useCaseConfigFactory
-        )
-        val suggestedStreamSpecMap = supportedSurfaceCombination.getSuggestedStreamSpecifications(
-            false,
-            emptyList(),
-            ArrayList(useCaseToConfigMap.values)
-        )
-        assertThat(suggestedStreamSpecMap[useCaseToConfigMap[imageAnalysis]]?.resolution).isEqualTo(
-            recordSize
-        )
-    }
-
     private fun setupCamera(hardwareLevel: Int, capabilities: IntArray) {
         setupCamera(
             hardwareLevel, sensorOrientation90, landscapePixelArraySize,
@@ -2696,43 +1675,27 @@
         return true
     }
 
-    /** Creates a VideoCapture with one ore more specific Quality  */
-    private fun createVideoCapture(vararg quality: Quality): VideoCapture<TestVideoOutput> {
-        return createVideoCapture(QualitySelector.fromOrderedList(listOf(*quality)))
-    }
-    /** Creates a VideoCapture with a customized QualitySelector  */
-    /** Creates a VideoCapture with a default QualitySelector  */
-    @JvmOverloads
-    fun createVideoCapture(
-        qualitySelector: QualitySelector = VideoSpec.QUALITY_SELECTOR_AUTO
-    ): VideoCapture<TestVideoOutput> {
-        val mediaSpecBuilder = MediaSpec.builder()
-        mediaSpecBuilder.configureVideo { builder: VideoSpec.Builder ->
-            builder.setQualitySelector(
-                qualitySelector
-            )
+    private fun createUseCase(
+        captureType: UseCaseConfigFactory.CaptureType,
+        targetFrameRate: Range<Int>? = null
+    ): UseCase {
+        val builder = FakeUseCaseConfig.Builder(captureType, when (captureType) {
+            UseCaseConfigFactory.CaptureType.IMAGE_CAPTURE -> ImageFormat.JPEG
+            UseCaseConfigFactory.CaptureType.IMAGE_ANALYSIS -> ImageFormat.YUV_420_888
+            else -> ImageFormatConstants.INTERNAL_DEFINED_IMAGE_FORMAT_PRIVATE
+        })
+        targetFrameRate?.let {
+            builder.mutableConfig.insertOption(UseCaseConfig.OPTION_TARGET_FRAME_RATE, it)
         }
-        val videoOutput = TestVideoOutput()
-        videoOutput.mediaSpecObservable.setState(mediaSpecBuilder.build())
-        return VideoCapture.withOutput(videoOutput)
+        return builder.build()
     }
 
-    /** A fake implementation of VideoOutput  */
-    class TestVideoOutput : VideoOutput {
-        var mediaSpecObservable =
-            MutableStateObservable.withInitialState(MediaSpec.builder().build())
-        private var surfaceRequest: SurfaceRequest? = null
-        private var sourceState: SourceState? = null
-        override fun onSurfaceRequested(request: SurfaceRequest) {
-            surfaceRequest = request
-        }
-
-        override fun getMediaSpec(): Observable<MediaSpec> {
-            return mediaSpecObservable
-        }
-
-        override fun onSourceStateChanged(sourceState: SourceState) {
-            this.sourceState = sourceState
-        }
+    private fun createRawUseCase(): UseCase {
+        val builder = FakeUseCaseConfig.Builder()
+        builder.mutableConfig.insertOption(
+            UseCaseConfig.OPTION_INPUT_FORMAT,
+            ImageFormat.RAW_SENSOR
+        )
+        return builder.build()
     }
 }
\ No newline at end of file
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2DeviceSurfaceManager.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2DeviceSurfaceManager.java
index 5e8dd36..38a2e6a 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2DeviceSurfaceManager.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2DeviceSurfaceManager.java
@@ -17,6 +17,7 @@
 package androidx.camera.camera2.internal;
 
 import android.content.Context;
+import android.hardware.camera2.CameraDevice;
 import android.media.CamcorderProfile;
 import android.util.Size;
 
@@ -43,7 +44,7 @@
  * Camera device manager to provide the guaranteed supported stream capabilities related info for
  * all camera devices
  *
- * <p>{@link android.hardware.camera2.CameraDevice#createCaptureSession} defines the default
+ * <p>{@link CameraDevice#createCaptureSession} defines the default
  * guaranteed stream combinations for different hardware level devices. It defines what combination
  * of surface configuration type and size pairs can be supported for different hardware level camera
  * devices. This structure is used to store the guaranteed supported stream capabilities related
@@ -177,12 +178,16 @@
     /**
      * Retrieves a map of suggested stream specifications for the given list of use cases.
      *
-     * @param isConcurrentCameraModeOn true if concurrent camera mode is on, otherwise false.
-     * @param cameraId          the camera id of the camera device used by the use cases
-     * @param existingSurfaces  list of surfaces already configured and used by the camera. The
-     *                          stream specifications for these surface can not change.
-     * @param newUseCaseConfigs list of configurations of the use cases that will be given a
-     *                          suggested stream specification
+     * @param isConcurrentCameraModeOn          true if concurrent camera mode is on, otherwise
+     *                                          false.
+     * @param cameraId                          the camera id of the camera device used by the
+     *                                          use cases
+     * @param existingSurfaces                  list of surfaces already configured and used by
+     *                                          the camera. The stream specifications for these
+     *                                          surface can not change.
+     * @param newUseCaseConfigsSupportedSizeMap map of configurations of the use cases to the
+     *                                          supported sizes list that will be given a
+     *                                          suggested stream specification
      * @return map of suggested stream specifications for given use cases
      * @throws IllegalStateException    if not initialized
      * @throws IllegalArgumentException if {@code newUseCaseConfigs} is an empty list, if
@@ -193,11 +198,11 @@
     @NonNull
     @Override
     public Map<UseCaseConfig<?>, StreamSpec> getSuggestedStreamSpecs(
-            boolean isConcurrentCameraModeOn,
-            @NonNull String cameraId,
+            boolean isConcurrentCameraModeOn, @NonNull String cameraId,
             @NonNull List<AttachedSurfaceInfo> existingSurfaces,
-            @NonNull List<UseCaseConfig<?>> newUseCaseConfigs) {
-        Preconditions.checkArgument(!newUseCaseConfigs.isEmpty(), "No new use cases to be bound.");
+            @NonNull Map<UseCaseConfig<?>, List<Size>> newUseCaseConfigsSupportedSizeMap) {
+        Preconditions.checkArgument(!newUseCaseConfigsSupportedSizeMap.isEmpty(),
+                "No new use cases to be bound.");
 
         SupportedSurfaceCombination supportedSurfaceCombination =
                 mCameraSupportedSurfaceCombinationMap.get(cameraId);
@@ -210,6 +215,6 @@
         return supportedSurfaceCombination.getSuggestedStreamSpecifications(
                 isConcurrentCameraModeOn,
                 existingSurfaces,
-                newUseCaseConfigs);
+                newUseCaseConfigsSupportedSizeMap);
     }
 }
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/SupportedOutputSizesCollector.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/SupportedOutputSizesCollector.java
deleted file mode 100644
index b0b6984..0000000
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/SupportedOutputSizesCollector.java
+++ /dev/null
@@ -1,590 +0,0 @@
-/*
- * 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.camera2.internal;
-
-import static androidx.camera.core.impl.utils.AspectRatioUtil.ASPECT_RATIO_16_9;
-import static androidx.camera.core.impl.utils.AspectRatioUtil.ASPECT_RATIO_3_4;
-import static androidx.camera.core.impl.utils.AspectRatioUtil.ASPECT_RATIO_4_3;
-import static androidx.camera.core.impl.utils.AspectRatioUtil.ASPECT_RATIO_9_16;
-import static androidx.camera.core.impl.utils.AspectRatioUtil.hasMatchingAspectRatio;
-
-import android.graphics.Rect;
-import android.hardware.camera2.CameraCharacteristics;
-import android.hardware.camera2.params.StreamConfigurationMap;
-import android.os.Build;
-import android.util.Rational;
-import android.util.Size;
-
-import androidx.annotation.DoNotInline;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.annotation.RequiresApi;
-import androidx.camera.camera2.internal.compat.CameraCharacteristicsCompat;
-import androidx.camera.camera2.internal.compat.StreamConfigurationMapCompat;
-import androidx.camera.camera2.internal.compat.workaround.ResolutionCorrector;
-import androidx.camera.core.AspectRatio;
-import androidx.camera.core.Logger;
-import androidx.camera.core.ResolutionSelector;
-import androidx.camera.core.impl.ImageOutputConfig;
-import androidx.camera.core.impl.SizeCoordinate;
-import androidx.camera.core.impl.SurfaceConfig;
-import androidx.camera.core.impl.utils.AspectRatioUtil;
-import androidx.camera.core.impl.utils.CameraOrientationUtil;
-import androidx.camera.core.impl.utils.CompareSizesByArea;
-import androidx.camera.core.internal.utils.SizeUtil;
-import androidx.core.util.Preconditions;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-/**
- * The supported output sizes collector to help collect the available resolution candidate list
- * according to the use case config and the following settings in {@link ResolutionSelector}:
- *
- * 1. Preferred aspect ratio
- * 2. Preferred resolution
- * 3. Max resolution
- * 4. Is high resolution enabled
- */
-@RequiresApi(21)
-final class SupportedOutputSizesCollector {
-    private static final String TAG = "SupportedOutputSizesCollector";
-    @NonNull
-    private final CameraCharacteristicsCompat mCharacteristics;
-    @NonNull
-    private final DisplayInfoManager mDisplayInfoManager;
-    private final ResolutionCorrector mResolutionCorrector = new ResolutionCorrector();
-    private final Map<Integer, Size[]> mOutputSizesCache = new HashMap<>();
-    private final Map<Integer, Size[]> mHighResolutionOutputSizesCache = new HashMap<>();
-    private final boolean mIsSensorLandscapeResolution;
-    private final boolean mIsBurstCaptureSupported;
-    private final Size mActiveArraySize;
-    private final int mSensorOrientation;
-    private final int mLensFacing;
-
-    SupportedOutputSizesCollector(@NonNull String cameraId,
-            @NonNull CameraCharacteristicsCompat cameraCharacteristics,
-            @NonNull DisplayInfoManager displayInfoManager) {
-        mCharacteristics = cameraCharacteristics;
-        mDisplayInfoManager = displayInfoManager;
-
-        mIsSensorLandscapeResolution = isSensorLandscapeResolution(mCharacteristics);
-        mIsBurstCaptureSupported = isBurstCaptureSupported();
-
-        Rect rect = mCharacteristics.get(CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE);
-        mActiveArraySize = rect != null ? new Size(rect.width(), rect.height()) : null;
-
-        mSensorOrientation = mCharacteristics.get(CameraCharacteristics.SENSOR_ORIENTATION);
-        mLensFacing = mCharacteristics.get(CameraCharacteristics.LENS_FACING);
-    }
-
-    /**
-     * Collects and sorts the resolution candidate list by the following steps:
-     *
-     * 1. Collects the candidate list by the high resolution enable setting.
-     * 2. Filters out the candidate list according to the min size bound and max resolution.
-     * 3. Sorts the candidate list according to the rules of legacy resolution API or new
-     * Resolution API.
-     * 4. Forces select specific resolutions according to ResolutionCorrector workaround.
-     */
-    @NonNull
-    List<Size> getSupportedOutputSizes(@NonNull ResolutionSelector resolutionSelector,
-            int imageFormat, @Nullable Size miniBoundingSize, boolean isHighResolutionDisabled,
-            @Nullable Size[] customizedSupportSizes) {
-        // 1. Collects the candidate list by the high resolution enable setting.
-        List<Size> resolutionCandidateList = collectResolutionCandidateList(resolutionSelector,
-                imageFormat, isHighResolutionDisabled, customizedSupportSizes);
-
-        // 2. Filters out the candidate list according to the min size bound and max resolution.
-        resolutionCandidateList = filterOutResolutionCandidateListBySettings(
-                resolutionCandidateList, resolutionSelector);
-
-        // 3. Sorts the candidate list according to the rules of new Resolution API.
-        resolutionCandidateList = sortResolutionCandidateListByResolutionSelector(
-                resolutionCandidateList, resolutionSelector,
-                mDisplayInfoManager.getMaxSizeDisplay().getRotation(), miniBoundingSize);
-
-        // 4. Forces select specific resolutions according to ResolutionCorrector workaround.
-        resolutionCandidateList = mResolutionCorrector.insertOrPrioritize(
-                SurfaceConfig.getConfigType(imageFormat), resolutionCandidateList);
-
-        return resolutionCandidateList;
-    }
-
-    /**
-     * Collects the resolution candidate list.
-     *
-     * 1. Customized supported resolutions list will be returned when it exists
-     * 2. Otherwise, the sizes retrieved from {@link StreamConfigurationMap#getOutputSizes(int)}
-     * will be the base of the resolution candidate list.
-     * 3. High resolution sizes retrieved from
-     * {@link StreamConfigurationMap#getHighResolutionOutputSizes(int)} will be included when
-     * {@link ResolutionSelector#isHighResolutionEnabled()} returns true.
-     *
-     * The returned list will be sorted in descending order and duplicate items will be removed.
-     */
-    @NonNull
-    private List<Size> collectResolutionCandidateList(
-            @NonNull ResolutionSelector resolutionSelector, int imageFormat,
-            boolean isHighResolutionDisabled, @Nullable Size[] customizedSupportedSizes) {
-        Size[] outputSizes = customizedSupportedSizes;
-
-        if (outputSizes == null) {
-            boolean highResolutionEnabled =
-                    !isHighResolutionDisabled && resolutionSelector.isHighResolutionEnabled();
-            outputSizes = getAllOutputSizesByFormat(imageFormat, highResolutionEnabled);
-        }
-
-        // Sort the output sizes. The Comparator result must be reversed to have a descending order
-        // result.
-        Arrays.sort(outputSizes, new CompareSizesByArea(true));
-
-        List<Size> resultList = Arrays.asList(outputSizes);
-
-        if (resultList.isEmpty()) {
-            throw new IllegalArgumentException(
-                    "Resolution candidate list is empty when collecting by the settings!");
-        }
-
-        return resultList;
-    }
-
-    /**
-     * Filters out the resolution candidate list by the max resolution setting.
-     *
-     * The input size list should have been sorted in descending order.
-     */
-    private List<Size> filterOutResolutionCandidateListBySettings(
-            @NonNull List<Size> resolutionCandidateList,
-            @NonNull ResolutionSelector resolutionSelector) {
-        // Retrieves the max resolution setting. When ResolutionSelector is used, all resolution
-        // selection logic should depend on ResolutionSelector's settings.
-        Size maxResolution = resolutionSelector.getMaxResolution();
-
-        // Filter out the resolution candidate list by the max resolution. Sizes that any edge
-        // exceeds the max resolution will be filtered out.
-        List<Size> resultList;
-
-        if (maxResolution == null) {
-            resultList = new ArrayList<>(resolutionCandidateList);
-        } else {
-            resultList = new ArrayList<>();
-            for (Size outputSize : resolutionCandidateList) {
-                if (!SizeUtil.isLongerInAnyEdge(outputSize, maxResolution)) {
-                    resultList.add(outputSize);
-                }
-            }
-        }
-
-        if (resultList.isEmpty()) {
-            throw new IllegalArgumentException(
-                    "Resolution candidate list is empty after filtering out by the settings!");
-        }
-
-        return resultList;
-    }
-
-    /**
-     * Sorts the resolution candidate list according to the new ResolutionSelector API logic.
-     *
-     * The list will be sorted by the following order:
-     * 1. size of preferred resolution
-     * 2. a resolution with preferred aspect ratio, is not smaller than, and is closest to the
-     * preferred resolution.
-     * 3. resolutions with preferred aspect ratio and is smaller than the preferred resolution
-     * size in descending order of resolution area size.
-     * 4. Other sizes sorted by CompareAspectRatiosByMappingAreaInFullFovAspectRatioSpace and
-     * area size.
-     */
-    @NonNull
-    private List<Size> sortResolutionCandidateListByResolutionSelector(
-            @NonNull List<Size> resolutionCandidateList,
-            @NonNull ResolutionSelector resolutionSelector,
-            @ImageOutputConfig.RotationValue int targetRotation,
-            @Nullable Size miniBoundingSize) {
-        Rational aspectRatio = getTargetAspectRatioByResolutionSelector(resolutionSelector);
-        Preconditions.checkNotNull(aspectRatio, "ResolutionSelector should also have aspect ratio"
-                + " value.");
-
-        Size targetSize = getTargetSizeByResolutionSelector(resolutionSelector, targetRotation,
-                mSensorOrientation, mLensFacing);
-        List<Size> resultList = sortResolutionCandidateListByTargetAspectRatioAndSize(
-                resolutionCandidateList, aspectRatio, miniBoundingSize);
-
-        // Moves the target size to the first position if it exists in the resolution candidate
-        // list and there is no quirk that needs to select specific aspect ratio sizes in priority.
-        if (resultList.contains(targetSize)) {
-            resultList.remove(targetSize);
-            resultList.add(0, targetSize);
-        }
-
-        return resultList;
-    }
-
-    @NonNull
-    private Size[] getAllOutputSizesByFormat(int imageFormat, boolean highResolutionEnabled) {
-        Size[] outputs = mOutputSizesCache.get(imageFormat);
-        if (outputs == null) {
-            outputs = doGetOutputSizesByFormat(imageFormat);
-            mOutputSizesCache.put(imageFormat, outputs);
-        }
-
-        Size[] highResolutionOutputs = null;
-
-        // A device that does not support the BURST_CAPTURE capability,
-        // StreamConfigurationMap#getHighResolutionOutputSizes() will return null.
-        if (highResolutionEnabled && mIsBurstCaptureSupported) {
-            highResolutionOutputs = mHighResolutionOutputSizesCache.get(imageFormat);
-
-            // High resolution output sizes list may be empty. If it is empty and cached in the
-            // map, don't need to query it again.
-            if (highResolutionOutputs == null && !mHighResolutionOutputSizesCache.containsKey(
-                    imageFormat)) {
-                highResolutionOutputs = doGetHighResolutionOutputSizesByFormat(imageFormat);
-                mHighResolutionOutputSizesCache.put(imageFormat, highResolutionOutputs);
-            }
-        }
-
-        // Combines output sizes if high resolution sizes list is not empty.
-        if (highResolutionOutputs != null) {
-            Size[] allOutputs = Arrays.copyOf(highResolutionOutputs,
-                    highResolutionOutputs.length + outputs.length);
-            System.arraycopy(outputs, 0, allOutputs, highResolutionOutputs.length, outputs.length);
-            outputs = allOutputs;
-        }
-
-        return outputs;
-    }
-
-    @NonNull
-    private Size[] doGetOutputSizesByFormat(int imageFormat) {
-        StreamConfigurationMapCompat mapCompat = mCharacteristics.getStreamConfigurationMapCompat();
-        Size[] outputSizes = mapCompat.getOutputSizes(imageFormat);
-        if (outputSizes == null) {
-            throw new IllegalArgumentException(
-                    "Can not get supported output size for the format: " + imageFormat);
-        }
-
-        return outputSizes;
-    }
-
-    @Nullable
-    private Size[] doGetHighResolutionOutputSizesByFormat(int imageFormat) {
-        if (Build.VERSION.SDK_INT < 23) {
-            return null;
-        }
-
-        Size[] outputSizes;
-
-        StreamConfigurationMap map =
-                mCharacteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
-
-        if (map == null) {
-            throw new IllegalArgumentException("Can not retrieve SCALER_STREAM_CONFIGURATION_MAP");
-        }
-
-        outputSizes = Api23Impl.getHighResolutionOutputSizes(map, imageFormat);
-
-        return outputSizes;
-    }
-
-    /**
-     * Returns the target aspect ratio rational value.
-     *
-     * @param resolutionSelector the resolution selector of the use case.
-     */
-    @Nullable
-    private Rational getTargetAspectRatioByResolutionSelector(
-            @NonNull ResolutionSelector resolutionSelector) {
-        Rational outputRatio = null;
-
-        @AspectRatio.Ratio int aspectRatio = resolutionSelector.getPreferredAspectRatio();
-        switch (aspectRatio) {
-            case AspectRatio.RATIO_4_3:
-                outputRatio = mIsSensorLandscapeResolution ? ASPECT_RATIO_4_3
-                        : ASPECT_RATIO_3_4;
-                break;
-            case AspectRatio.RATIO_16_9:
-                outputRatio = mIsSensorLandscapeResolution ? ASPECT_RATIO_16_9
-                        : ASPECT_RATIO_9_16;
-                break;
-            case AspectRatio.RATIO_DEFAULT:
-                break;
-            default:
-                Logger.e(TAG, "Undefined target aspect ratio: " + aspectRatio);
-        }
-        return outputRatio;
-    }
-
-    @Nullable
-    static Size getTargetSizeByResolutionSelector(@NonNull ResolutionSelector resolutionSelector,
-            int targetRotation, int sensorOrientation, int lensFacing) {
-        Size targetSize = resolutionSelector.getPreferredResolution();
-
-        // Calibrate targetSize by the target rotation value if it is set by the Android View
-        // coordinate orientation.
-        if (resolutionSelector.getSizeCoordinate() == SizeCoordinate.ANDROID_VIEW) {
-            targetSize = flipSizeByRotation(targetSize, targetRotation, lensFacing,
-                    sensorOrientation);
-        }
-        return targetSize;
-    }
-
-    private static boolean isRotationNeeded(int targetRotation, int lensFacing,
-            int sensorOrientation) {
-        int relativeRotationDegrees =
-                CameraOrientationUtil.surfaceRotationToDegrees(targetRotation);
-
-        // Currently this assumes that a back-facing camera is always opposite to the screen.
-        // This may not be the case for all devices, so in the future we may need to handle that
-        // scenario.
-        boolean isOppositeFacingScreen = CameraCharacteristics.LENS_FACING_BACK == lensFacing;
-
-        int sensorRotationDegrees = CameraOrientationUtil.getRelativeImageRotation(
-                relativeRotationDegrees,
-                sensorOrientation,
-                isOppositeFacingScreen);
-        return sensorRotationDegrees == 90 || sensorRotationDegrees == 270;
-    }
-
-    /**
-     * Sorts the resolution candidate list according to the target aspect ratio and size settings.
-     *
-     * 1. The resolution candidate list will be grouped by aspect ratio.
-     * 2. Each group only keeps one size which is not smaller than the target size.
-     * 3. The aspect ratios of groups will be sorted against to the target aspect ratio setting by
-     * CompareAspectRatiosByMappingAreaInFullFovAspectRatioSpace.
-     * 4. Concatenate all sizes as the result list
-     */
-    @NonNull
-    private List<Size> sortResolutionCandidateListByTargetAspectRatioAndSize(
-            @NonNull List<Size> resolutionCandidateList, @NonNull Rational aspectRatio,
-            @Nullable Size miniBoundingSize) {
-        // Rearrange the supported size to put the ones with the same aspect ratio in the front
-        // of the list and put others in the end from large to small. Some low end devices may
-        // not able to get an supported resolution that match the preferred aspect ratio.
-
-        // Group output sizes by aspect ratio.
-        Map<Rational, List<Size>> aspectRatioSizeListMap =
-                groupSizesByAspectRatio(resolutionCandidateList);
-
-        // If the target resolution is set, use it to remove unnecessary larger sizes.
-        if (miniBoundingSize != null) {
-            // Remove unnecessary larger sizes from each aspect ratio size list
-            for (Rational key : aspectRatioSizeListMap.keySet()) {
-                removeSupportedSizesByMiniBoundingSize(aspectRatioSizeListMap.get(key),
-                        miniBoundingSize);
-            }
-        }
-
-        // Sort the aspect ratio key set by the target aspect ratio.
-        List<Rational> aspectRatios = new ArrayList<>(aspectRatioSizeListMap.keySet());
-        Rational fullFovRatio = mActiveArraySize != null ? new Rational(
-                mActiveArraySize.getWidth(), mActiveArraySize.getHeight()) : null;
-        Collections.sort(aspectRatios,
-                new AspectRatioUtil.CompareAspectRatiosByMappingAreaInFullFovAspectRatioSpace(
-                        aspectRatio, fullFovRatio));
-
-        List<Size> resultList = new ArrayList<>();
-
-        // Put available sizes into final result list by aspect ratio distance to target ratio.
-        for (Rational rational : aspectRatios) {
-            for (Size size : aspectRatioSizeListMap.get(rational)) {
-                // A size may exist in multiple groups in mod16 condition. Keep only one in
-                // the final list.
-                if (!resultList.contains(size)) {
-                    resultList.add(size);
-                }
-            }
-        }
-
-        return resultList;
-    }
-
-    private boolean isBurstCaptureSupported() {
-        int[] availableCapabilities =
-                mCharacteristics.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES);
-
-        if (availableCapabilities != null) {
-            for (int capability : availableCapabilities) {
-                if (capability
-                        == CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE) {
-                    return true;
-                }
-            }
-        }
-
-        return false;
-    }
-
-    //////////////////////////////////////////////////////////////////////////////////////////
-    // The following functions can be reused by the legacy resolution selection logic. The can be
-    // changed as private function after the legacy resolution API is completely removed.
-    //////////////////////////////////////////////////////////////////////////////////////////
-
-    // Use target rotation to calibrate the size.
-    @Nullable
-    static Size flipSizeByRotation(@Nullable Size size, int targetRotation, int lensFacing,
-            int sensorOrientation) {
-        Size outputSize = size;
-        // Calibrates the size with the display and sensor rotation degrees values.
-        if (size != null && isRotationNeeded(targetRotation, lensFacing, sensorOrientation)) {
-            outputSize = new Size(/* width= */size.getHeight(), /* height= */size.getWidth());
-        }
-        return outputSize;
-    }
-
-    static Map<Rational, List<Size>> groupSizesByAspectRatio(List<Size> sizes) {
-        Map<Rational, List<Size>> aspectRatioSizeListMap = new HashMap<>();
-
-        List<Rational> aspectRatioKeys = getResolutionListGroupingAspectRatioKeys(sizes);
-
-        for (Rational aspectRatio: aspectRatioKeys) {
-            aspectRatioSizeListMap.put(aspectRatio, new ArrayList<>());
-        }
-
-        for (Size outputSize : sizes) {
-            for (Rational key : aspectRatioSizeListMap.keySet()) {
-                // Put the size into all groups that is matched in mod16 condition since a size
-                // may match multiple aspect ratio in mod16 algorithm.
-                if (hasMatchingAspectRatio(outputSize, key)) {
-                    aspectRatioSizeListMap.get(key).add(outputSize);
-                }
-            }
-        }
-
-        return aspectRatioSizeListMap;
-    }
-
-    /**
-     * Returns the grouping aspect ratio keys of the input resolution list.
-     *
-     * <p>Some sizes might be mod16 case. When grouping, those sizes will be grouped into an
-     * existing aspect ratio group if the aspect ratio can match by the mod16 rule.
-     */
-    @NonNull
-    static List<Rational> getResolutionListGroupingAspectRatioKeys(
-            @NonNull List<Size> resolutionCandidateList) {
-        List<Rational> aspectRatios = new ArrayList<>();
-
-        // Adds the default 4:3 and 16:9 items first to avoid their mod16 sizes to create
-        // additional items.
-        aspectRatios.add(ASPECT_RATIO_4_3);
-        aspectRatios.add(ASPECT_RATIO_16_9);
-
-        // Tries to find the aspect ratio which the target size belongs to.
-        for (Size size : resolutionCandidateList) {
-            Rational newRatio = new Rational(size.getWidth(), size.getHeight());
-            boolean aspectRatioFound = aspectRatios.contains(newRatio);
-
-            // The checking size might be a mod16 size which can be mapped to an existing aspect
-            // ratio group.
-            if (!aspectRatioFound) {
-                boolean hasMatchingAspectRatio = false;
-                for (Rational aspectRatio : aspectRatios) {
-                    if (hasMatchingAspectRatio(size, aspectRatio)) {
-                        hasMatchingAspectRatio = true;
-                        break;
-                    }
-                }
-                if (!hasMatchingAspectRatio) {
-                    aspectRatios.add(newRatio);
-                }
-            }
-        }
-
-        return aspectRatios;
-    }
-
-    /**
-     * Removes unnecessary sizes by target size.
-     *
-     * <p>If the target resolution is set, a size that is equal to or closest to the target
-     * resolution will be selected. If the list includes more than one size equal to or larger
-     * than the target resolution, only one closest size needs to be kept. The other larger sizes
-     * can be removed so that they won't be selected to use.
-     *
-     * @param supportedSizesList The list should have been sorted in descending order.
-     * @param miniBoundingSize The target size used to remove unnecessary sizes.
-     */
-    static void removeSupportedSizesByMiniBoundingSize(List<Size> supportedSizesList,
-            Size miniBoundingSize) {
-        if (supportedSizesList == null || supportedSizesList.isEmpty()) {
-            return;
-        }
-
-        int indexBigEnough = -1;
-        List<Size> removeSizes = new ArrayList<>();
-
-        // Get the index of the item that is equal to or closest to the target size.
-        for (int i = 0; i < supportedSizesList.size(); i++) {
-            Size outputSize = supportedSizesList.get(i);
-            if (outputSize.getWidth() >= miniBoundingSize.getWidth()
-                    && outputSize.getHeight() >= miniBoundingSize.getHeight()) {
-                // New big enough item closer to the target size is found. Adding the previous
-                // one into the sizes list that will be removed.
-                if (indexBigEnough >= 0) {
-                    removeSizes.add(supportedSizesList.get(indexBigEnough));
-                }
-
-                indexBigEnough = i;
-            } else {
-                // If duplicated miniBoundingSize items exist in the list, the size will be added
-                // into the removeSizes list. Removes it from the removeSizes list to keep the
-                // miniBoundingSize items in the final result list.
-                if (indexBigEnough >= 0) {
-                    removeSizes.remove(supportedSizesList.get(indexBigEnough));
-                }
-                break;
-            }
-        }
-
-        // Remove the unnecessary items that are larger than the item closest to the target size.
-        supportedSizesList.removeAll(removeSizes);
-    }
-
-    static boolean isSensorLandscapeResolution(
-            @NonNull CameraCharacteristicsCompat characteristicsCompat) {
-        Size pixelArraySize =
-                characteristicsCompat.get(CameraCharacteristics.SENSOR_INFO_PIXEL_ARRAY_SIZE);
-
-        // Make the default value is true since usually the sensor resolution is landscape.
-        return pixelArraySize == null || pixelArraySize.getWidth() >= pixelArraySize.getHeight();
-    }
-
-    //////////////////////////////////////////////////////////////////////////////////////////
-    // The above functions can be reused by the legacy resolution selection logic. The can be
-    // changed as private function after the legacy resolution API is completely removed.
-    //////////////////////////////////////////////////////////////////////////////////////////
-
-    @RequiresApi(23)
-    private static class Api23Impl {
-        private Api23Impl() {
-            // This class is not instantiable.
-        }
-
-        @DoNotInline
-        static Size[] getHighResolutionOutputSizes(StreamConfigurationMap streamConfigurationMap,
-                int format) {
-            return streamConfigurationMap.getHighResolutionOutputSizes(format);
-        }
-    }
-}
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/SupportedSurfaceCombination.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/SupportedSurfaceCombination.java
index 5b19fe1..628d8be 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/SupportedSurfaceCombination.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/SupportedSurfaceCombination.java
@@ -18,38 +18,19 @@
 
 import static android.content.pm.PackageManager.FEATURE_CAMERA_CONCURRENT;
 
-import static androidx.camera.camera2.internal.SupportedOutputSizesCollector.flipSizeByRotation;
-import static androidx.camera.camera2.internal.SupportedOutputSizesCollector.getResolutionListGroupingAspectRatioKeys;
-import static androidx.camera.camera2.internal.SupportedOutputSizesCollector.getTargetSizeByResolutionSelector;
-import static androidx.camera.camera2.internal.SupportedOutputSizesCollector.groupSizesByAspectRatio;
-import static androidx.camera.camera2.internal.SupportedOutputSizesCollector.isSensorLandscapeResolution;
-import static androidx.camera.camera2.internal.SupportedOutputSizesCollector.removeSupportedSizesByMiniBoundingSize;
-import static androidx.camera.core.impl.utils.AspectRatioUtil.ASPECT_RATIO_16_9;
-import static androidx.camera.core.impl.utils.AspectRatioUtil.ASPECT_RATIO_3_4;
-import static androidx.camera.core.impl.utils.AspectRatioUtil.ASPECT_RATIO_4_3;
-import static androidx.camera.core.impl.utils.AspectRatioUtil.ASPECT_RATIO_9_16;
-import static androidx.camera.core.impl.utils.AspectRatioUtil.hasMatchingAspectRatio;
 import static androidx.camera.core.internal.utils.SizeUtil.RESOLUTION_1080P;
 import static androidx.camera.core.internal.utils.SizeUtil.RESOLUTION_480P;
-import static androidx.camera.core.internal.utils.SizeUtil.RESOLUTION_VGA;
-import static androidx.camera.core.internal.utils.SizeUtil.RESOLUTION_ZERO;
-import static androidx.camera.core.internal.utils.SizeUtil.getArea;
 
 import android.content.Context;
-import android.graphics.Rect;
 import android.graphics.SurfaceTexture;
 import android.hardware.camera2.CameraCharacteristics;
 import android.hardware.camera2.params.StreamConfigurationMap;
 import android.media.CamcorderProfile;
 import android.media.MediaRecorder;
-import android.util.Pair;
 import android.util.Range;
-import android.util.Rational;
 import android.util.Size;
-import android.view.Surface;
 
 import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
 import androidx.annotation.VisibleForTesting;
 import androidx.camera.camera2.internal.compat.CameraAccessExceptionCompat;
@@ -57,20 +38,14 @@
 import androidx.camera.camera2.internal.compat.CameraManagerCompat;
 import androidx.camera.camera2.internal.compat.StreamConfigurationMapCompat;
 import androidx.camera.camera2.internal.compat.workaround.ExtraSupportedSurfaceCombinationsContainer;
-import androidx.camera.camera2.internal.compat.workaround.ResolutionCorrector;
-import androidx.camera.core.AspectRatio;
 import androidx.camera.core.CameraUnavailableException;
-import androidx.camera.core.Logger;
-import androidx.camera.core.ResolutionSelector;
 import androidx.camera.core.impl.AttachedSurfaceInfo;
 import androidx.camera.core.impl.ImageFormatConstants;
-import androidx.camera.core.impl.ImageOutputConfig;
 import androidx.camera.core.impl.StreamSpec;
 import androidx.camera.core.impl.SurfaceCombination;
 import androidx.camera.core.impl.SurfaceConfig;
 import androidx.camera.core.impl.SurfaceSizeDefinition;
 import androidx.camera.core.impl.UseCaseConfig;
-import androidx.camera.core.impl.utils.AspectRatioUtil;
 import androidx.camera.core.impl.utils.CompareSizesByArea;
 import androidx.core.util.Preconditions;
 
@@ -101,19 +76,12 @@
     private final ExtraSupportedSurfaceCombinationsContainer
             mExtraSupportedSurfaceCombinationsContainer;
     private final int mHardwareLevel;
-    private final boolean mIsSensorLandscapeResolution;
     private boolean mIsRawSupported = false;
     private boolean mIsBurstCaptureSupported = false;
     @VisibleForTesting
     SurfaceSizeDefinition mSurfaceSizeDefinition;
-    private final Map<Integer, Size[]> mOutputSizesCache = new HashMap<>();
     @NonNull
     private final DisplayInfoManager mDisplayInfoManager;
-    private final ResolutionCorrector mResolutionCorrector = new ResolutionCorrector();
-    private final Size mActiveArraySize;
-    private final int mSensorOrientation;
-    private final int mLensFacing;
-    private final SupportedOutputSizesCollector mSupportedOutputSizesCollector;
 
     SupportedSurfaceCombination(@NonNull Context context, @NonNull String cameraId,
             @NonNull CameraManagerCompat cameraManagerCompat,
@@ -131,7 +99,6 @@
                     CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL);
             mHardwareLevel = keyValue != null ? keyValue
                     : CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY;
-            mIsSensorLandscapeResolution = isSensorLandscapeResolution(mCharacteristics);
         } catch (CameraAccessExceptionCompat e) {
             throw CameraUnavailableExceptionHelper.createFrom(e);
         }
@@ -150,21 +117,12 @@
             }
         }
 
-        Rect rect = mCharacteristics.get(CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE);
-        mActiveArraySize = rect != null ? new Size(rect.width(), rect.height()) : null;
-
-        mSensorOrientation = mCharacteristics.get(CameraCharacteristics.SENSOR_ORIENTATION);
-        mLensFacing = mCharacteristics.get(CameraCharacteristics.LENS_FACING);
-
         generateSupportedCombinationList();
         if (context.getPackageManager().hasSystemFeature(FEATURE_CAMERA_CONCURRENT)) {
             generateConcurrentSupportedCombinationList();
         }
         generateSurfaceSizeDefinition();
         checkCustomization();
-
-        mSupportedOutputSizesCollector = new SupportedOutputSizesCollector(mCameraId,
-                mCharacteristics, mDisplayInfoManager);
     }
 
     String getCameraId() {
@@ -284,9 +242,11 @@
     /**
      * Finds the suggested stream specifications of the newly added UseCaseConfig.
      *
-     * @param isConcurrentCameraModeOn true if concurrent camera mode is on, otherwise false.
-     * @param attachedSurfaces  the existing surfaces.
-     * @param newUseCaseConfigs newly added UseCaseConfig.
+     * @param isConcurrentCameraModeOn          true if concurrent camera mode is on, otherwise
+     *                                          false.
+     * @param attachedSurfaces                  the existing surfaces.
+     * @param newUseCaseConfigsSupportedSizeMap newly added UseCaseConfig to supported output
+     *                                          sizes map.
      * @return the suggested stream specifications, which is a mapping from UseCaseConfig to the
      * suggested stream specification.
      * @throws IllegalArgumentException if the suggested solution for newUseCaseConfigs cannot be
@@ -297,7 +257,7 @@
     Map<UseCaseConfig<?>, StreamSpec> getSuggestedStreamSpecifications(
             boolean isConcurrentCameraModeOn,
             @NonNull List<AttachedSurfaceInfo> attachedSurfaces,
-            @NonNull List<UseCaseConfig<?>> newUseCaseConfigs) {
+            @NonNull Map<UseCaseConfig<?>, List<Size>> newUseCaseConfigsSupportedSizeMap) {
         // Refresh Preview Size based on current display configurations.
         refreshPreviewSize();
         List<SurfaceConfig> surfaceConfigs = new ArrayList<>();
@@ -305,6 +265,8 @@
             surfaceConfigs.add(attachedSurface.getSurfaceConfig());
         }
 
+        List<UseCaseConfig<?>> newUseCaseConfigs = new ArrayList<>(
+                newUseCaseConfigsSupportedSizeMap.keySet());
         // Use the small size (640x480) for new use cases to check whether there is any possible
         // supported combination first
         for (UseCaseConfig<?> useCaseConfig : newUseCaseConfigs) {
@@ -347,7 +309,8 @@
 
         // Collect supported output sizes for all use cases
         for (Integer index : useCasesPriorityOrder) {
-            List<Size> supportedOutputSizes = getSupportedOutputSizes(newUseCaseConfigs.get(index));
+            List<Size> supportedOutputSizes = newUseCaseConfigsSupportedSizeMap.get(
+                    newUseCaseConfigs.get(index));
             supportedOutputSizesList.add(supportedOutputSizes);
         }
 
@@ -455,48 +418,6 @@
         return suggestedStreamSpecMap;
     }
 
-    /**
-     * Returns the target aspect ratio rational value.
-     *
-     * @param imageOutputConfig       the image output config of the use case.
-     * @param resolutionCandidateList the resolution candidate list which will be used to
-     *                                determine the aspect ratio by target size when target
-     *                                aspect ratio setting is not set.
-     */
-    private Rational getTargetAspectRatio(@NonNull ImageOutputConfig imageOutputConfig,
-            @NonNull List<Size> resolutionCandidateList) {
-        Rational outputRatio = null;
-
-        if (imageOutputConfig.hasTargetAspectRatio()) {
-            @AspectRatio.Ratio int aspectRatio = imageOutputConfig.getTargetAspectRatio();
-            switch (aspectRatio) {
-                case AspectRatio.RATIO_4_3:
-                    outputRatio = mIsSensorLandscapeResolution ? ASPECT_RATIO_4_3
-                            : ASPECT_RATIO_3_4;
-                    break;
-                case AspectRatio.RATIO_16_9:
-                    outputRatio = mIsSensorLandscapeResolution ? ASPECT_RATIO_16_9
-                            : ASPECT_RATIO_9_16;
-                    break;
-                case AspectRatio.RATIO_DEFAULT:
-                    break;
-                default:
-                    Logger.e(TAG, "Undefined target aspect ratio: " + aspectRatio);
-            }
-        } else {
-            // The legacy resolution API will use the aspect ratio of the target size to
-            // be the fallback target aspect ratio value when the use case has no target
-            // aspect ratio setting.
-            Size targetSize = getTargetSize(imageOutputConfig);
-            if (targetSize != null) {
-                outputRatio = getAspectRatioGroupKeyOfTargetSize(targetSize,
-                        resolutionCandidateList);
-            }
-        }
-
-        return outputRatio;
-    }
-
     private List<Integer> getUseCasesPriorityOrder(List<UseCaseConfig<?>> newUseCaseConfigs) {
         List<Integer> priorityOrder = new ArrayList<>();
 
@@ -531,180 +452,6 @@
         return priorityOrder;
     }
 
-    @NonNull
-    @VisibleForTesting
-    List<Size> getSupportedOutputSizes(@NonNull UseCaseConfig<?> config) {
-        int imageFormat = config.getInputFormat();
-        ImageOutputConfig imageOutputConfig = (ImageOutputConfig) config;
-
-        List<Size> customOrderedResolutions = imageOutputConfig.getCustomOrderedResolutions(null);
-        if (customOrderedResolutions != null) {
-            return customOrderedResolutions;
-        }
-        ResolutionSelector resolutionSelector = imageOutputConfig.getResolutionSelector(null);
-        if (resolutionSelector != null) {
-            Size miniBoundingSize = imageOutputConfig.getDefaultResolution(null);
-
-            if (resolutionSelector.getPreferredResolution() != null) {
-                miniBoundingSize = getTargetSizeByResolutionSelector(resolutionSelector,
-                        mDisplayInfoManager.getMaxSizeDisplay().getRotation(), mSensorOrientation,
-                        mLensFacing);
-            }
-
-            return mSupportedOutputSizesCollector.getSupportedOutputSizes(resolutionSelector,
-                    imageFormat, miniBoundingSize, config.isHigResolutionDisabled(false),
-                    getCustomizedSupportSizesFromConfig(imageFormat, imageOutputConfig));
-        }
-
-        Size[] outputSizes = getCustomizedSupportSizesFromConfig(imageFormat, imageOutputConfig);
-        if (outputSizes == null) {
-            outputSizes = getAllOutputSizesByFormat(imageFormat);
-        }
-
-        // Sort the result sizes. The Comparator result must be reversed to have a descending
-        // order result.
-        Arrays.sort(outputSizes, new CompareSizesByArea(true));
-
-        List<Size> outputSizeCandidates = new ArrayList<>();
-        Size maxSize = imageOutputConfig.getMaxResolution(null);
-        Size maxOutputSizeByFormat = getMaxOutputSizeByFormat(imageFormat);
-
-        // Set maxSize as the max resolution setting or the max supported output size for the
-        // image format, whichever is smaller.
-        if (maxSize == null || getArea(maxOutputSizeByFormat) < getArea(maxSize)) {
-            maxSize = maxOutputSizeByFormat;
-        }
-
-        // Sort the output sizes. The Comparator result must be reversed to have a descending order
-        // result.
-        Arrays.sort(outputSizes, new CompareSizesByArea(true));
-
-        Size targetSize = getTargetSize(imageOutputConfig);
-        Size minSize = RESOLUTION_VGA;
-        int defaultSizeArea = getArea(RESOLUTION_VGA);
-        int maxSizeArea = getArea(maxSize);
-        // When maxSize is smaller than 640x480, set minSize as 0x0. It means the min size bound
-        // will be ignored. Otherwise, set the minimal size according to min(DEFAULT_SIZE,
-        // TARGET_RESOLUTION).
-        if (maxSizeArea < defaultSizeArea) {
-            minSize = RESOLUTION_ZERO;
-        } else if (targetSize != null && getArea(targetSize) < defaultSizeArea) {
-            minSize = targetSize;
-        }
-
-        // Filter out the ones that exceed the maximum size and the minimum size. The output
-        // sizes candidates list won't have duplicated items.
-        for (Size outputSize : outputSizes) {
-            if (getArea(outputSize) <= getArea(maxSize) && getArea(outputSize) >= getArea(minSize)
-                    && !outputSizeCandidates.contains(outputSize)) {
-                outputSizeCandidates.add(outputSize);
-            }
-        }
-
-        if (outputSizeCandidates.isEmpty()) {
-            throw new IllegalArgumentException(
-                    "Can not get supported output size under supported maximum for the format: "
-                            + imageFormat);
-        }
-
-        Rational aspectRatio = getTargetAspectRatio(imageOutputConfig, outputSizeCandidates);
-
-        // Check the default resolution if the target resolution is not set
-        targetSize = targetSize == null ? imageOutputConfig.getDefaultResolution(null) : targetSize;
-
-        List<Size> supportedResolutions = new ArrayList<>();
-        Map<Rational, List<Size>> aspectRatioSizeListMap = new HashMap<>();
-
-        if (aspectRatio == null) {
-            // If no target aspect ratio is set, all sizes can be added to the result list
-            // directly. No need to sort again since the source list has been sorted previously.
-            supportedResolutions.addAll(outputSizeCandidates);
-
-            // If the target resolution is set, use it to remove unnecessary larger sizes.
-            if (targetSize != null) {
-                removeSupportedSizesByMiniBoundingSize(supportedResolutions, targetSize);
-            }
-        } else {
-            // Rearrange the supported size to put the ones with the same aspect ratio in the front
-            // of the list and put others in the end from large to small. Some low end devices may
-            // not able to get an supported resolution that match the preferred aspect ratio.
-
-            // Group output sizes by aspect ratio.
-            aspectRatioSizeListMap = groupSizesByAspectRatio(outputSizeCandidates);
-
-            // If the target resolution is set, use it to remove unnecessary larger sizes.
-            if (targetSize != null) {
-                // Remove unnecessary larger sizes from each aspect ratio size list
-                for (Rational key : aspectRatioSizeListMap.keySet()) {
-                    removeSupportedSizesByMiniBoundingSize(aspectRatioSizeListMap.get(key),
-                            targetSize);
-                }
-            }
-
-            // Sort the aspect ratio key set by the target aspect ratio.
-            List<Rational> aspectRatios = new ArrayList<>(aspectRatioSizeListMap.keySet());
-            Rational fullFovRatio = mActiveArraySize != null ? new Rational(
-                    mActiveArraySize.getWidth(), mActiveArraySize.getHeight()) : null;
-            Collections.sort(aspectRatios,
-                    new AspectRatioUtil.CompareAspectRatiosByMappingAreaInFullFovAspectRatioSpace(
-                            aspectRatio, fullFovRatio));
-
-            // Put available sizes into final result list by aspect ratio distance to target ratio.
-            for (Rational rational : aspectRatios) {
-                for (Size size : aspectRatioSizeListMap.get(rational)) {
-                    // A size may exist in multiple groups in mod16 condition. Keep only one in
-                    // the final list.
-                    if (!supportedResolutions.contains(size)) {
-                        supportedResolutions.add(size);
-                    }
-                }
-            }
-        }
-
-        supportedResolutions = mResolutionCorrector.insertOrPrioritize(
-                SurfaceConfig.getConfigType(config.getInputFormat()),
-                supportedResolutions);
-
-        return supportedResolutions;
-    }
-
-    @Nullable
-    private Size getTargetSize(@NonNull ImageOutputConfig imageOutputConfig) {
-        int targetRotation = imageOutputConfig.getTargetRotation(Surface.ROTATION_0);
-        // Calibrate targetSize by the target rotation value.
-        Size targetSize = imageOutputConfig.getTargetResolution(null);
-        targetSize = flipSizeByRotation(targetSize, targetRotation, mLensFacing,
-                mSensorOrientation);
-        return targetSize;
-    }
-
-    /**
-     * Returns the aspect ratio group key of the target size when grouping the input resolution
-     * candidate list.
-     *
-     * The resolution candidate list will be grouped with mod 16 consideration. Therefore, we
-     * also need to consider the mod 16 factor to find which aspect ratio of group the target size
-     * might be put in. So that sizes of the group will be selected to use in the highest priority.
-     */
-    @Nullable
-    private Rational getAspectRatioGroupKeyOfTargetSize(@Nullable Size targetSize,
-            @NonNull List<Size> resolutionCandidateList) {
-        if (targetSize == null) {
-            return null;
-        }
-
-        List<Rational> aspectRatios = getResolutionListGroupingAspectRatioKeys(
-                resolutionCandidateList);
-
-        for (Rational aspectRatio: aspectRatios) {
-            if (hasMatchingAspectRatio(targetSize, aspectRatio)) {
-                return aspectRatio;
-            }
-        }
-
-        return new Rational(targetSize.getWidth(), targetSize.getHeight());
-    }
-
     private List<List<Size>> getAllPossibleSizeArrangements(
             List<List<Size>> supportedOutputSizesList) {
         int totalArrangementsCount = 1;
@@ -758,50 +505,6 @@
         return allPossibleSizeArrangements;
     }
 
-    @Nullable
-    private Size[] getCustomizedSupportSizesFromConfig(int imageFormat,
-            @NonNull ImageOutputConfig config) {
-        Size[] outputSizes = null;
-
-        // Try to retrieve customized supported resolutions from config.
-        List<Pair<Integer, Size[]>> formatResolutionsPairList =
-                config.getSupportedResolutions(null);
-
-        if (formatResolutionsPairList != null) {
-            for (Pair<Integer, Size[]> formatResolutionPair : formatResolutionsPairList) {
-                if (formatResolutionPair.first == imageFormat) {
-                    outputSizes = formatResolutionPair.second;
-                    break;
-                }
-            }
-        }
-
-        return outputSizes;
-    }
-
-    @NonNull
-    private Size[] getAllOutputSizesByFormat(int imageFormat) {
-        Size[] outputs = mOutputSizesCache.get(imageFormat);
-        if (outputs == null) {
-            outputs = doGetAllOutputSizesByFormat(imageFormat);
-            mOutputSizesCache.put(imageFormat, outputs);
-        }
-
-        return outputs;
-    }
-
-    @NonNull
-    private Size[] doGetAllOutputSizesByFormat(int imageFormat) {
-        StreamConfigurationMapCompat mapCompat = mCharacteristics.getStreamConfigurationMapCompat();
-        Size[] outputSizes = mapCompat.getOutputSizes(imageFormat);
-        if (outputSizes == null) {
-            throw new IllegalArgumentException(
-                    "Can not get supported output size for the format: " + imageFormat);
-        }
-
-        return outputSizes;
-    }
-
     /**
      * Get max supported output size for specific image format
      *
diff --git a/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/SupportedOutputSizesCollectorTest.kt b/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/SupportedOutputSizesCollectorTest.kt
deleted file mode 100644
index 7761531..0000000
--- a/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/SupportedOutputSizesCollectorTest.kt
+++ /dev/null
@@ -1,1386 +0,0 @@
-/*
- * 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.camera2.internal
-
-import android.content.Context
-import android.graphics.SurfaceTexture
-import android.hardware.camera2.CameraCharacteristics
-import android.hardware.camera2.CameraManager
-import android.hardware.camera2.params.StreamConfigurationMap
-import android.media.MediaRecorder
-import android.os.Build
-import android.util.Pair
-import android.util.Size
-import android.view.Surface
-import android.view.WindowManager
-import androidx.camera.camera2.Camera2Config
-import androidx.camera.camera2.internal.compat.CameraCharacteristicsCompat
-import androidx.camera.camera2.internal.compat.CameraManagerCompat
-import androidx.camera.core.AspectRatio
-import androidx.camera.core.CameraSelector
-import androidx.camera.core.CameraX
-import androidx.camera.core.CameraXConfig
-import androidx.camera.core.ImageAnalysis
-import androidx.camera.core.ImageCapture
-import androidx.camera.core.Preview
-import androidx.camera.core.ResolutionSelector
-import androidx.camera.core.UseCase
-import androidx.camera.core.impl.CameraDeviceSurfaceManager
-import androidx.camera.core.impl.ImageOutputConfig
-import androidx.camera.core.impl.SizeCoordinate
-import androidx.camera.core.impl.UseCaseConfigFactory
-import androidx.camera.testing.CameraUtil
-import androidx.camera.testing.CameraXUtil
-import androidx.camera.testing.Configs
-import androidx.camera.testing.fakes.FakeCamera
-import androidx.camera.testing.fakes.FakeCameraFactory
-import androidx.camera.testing.fakes.FakeCameraInfoInternal
-import androidx.camera.testing.fakes.FakeUseCaseConfig
-import androidx.test.core.app.ApplicationProvider
-import com.google.common.truth.Truth.assertThat
-import java.util.concurrent.ExecutionException
-import java.util.concurrent.TimeUnit
-import org.junit.After
-import org.junit.Assert
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.ArgumentMatchers
-import org.mockito.ArgumentMatchers.anyInt
-import org.mockito.ArgumentMatchers.eq
-import org.mockito.Mockito
-import org.robolectric.ParameterizedRobolectricTestRunner
-import org.robolectric.Shadows
-import org.robolectric.annotation.Config
-import org.robolectric.annotation.internal.DoNotInstrument
-import org.robolectric.shadow.api.Shadow
-import org.robolectric.shadows.ShadowCameraCharacteristics
-import org.robolectric.shadows.ShadowCameraManager
-
-private const val FAKE_USE_CASE = 0
-private const val PREVIEW_USE_CASE = 1
-private const val IMAGE_CAPTURE_USE_CASE = 2
-private const val IMAGE_ANALYSIS_USE_CASE = 3
-private const val UNKNOWN_ASPECT_RATIO = -1
-private const val DEFAULT_CAMERA_ID = "0"
-private const val SENSOR_ORIENTATION_0 = 0
-private const val SENSOR_ORIENTATION_90 = 90
-private val LANDSCAPE_PIXEL_ARRAY_SIZE = Size(4032, 3024)
-private val PORTRAIT_PIXEL_ARRAY_SIZE = Size(3024, 4032)
-private val DISPLAY_SIZE = Size(720, 1280)
-private val DEFAULT_SUPPORTED_SIZES = arrayOf(
-    Size(4032, 3024), // 4:3
-    Size(3840, 2160), // 16:9
-    Size(1920, 1440), // 4:3
-    Size(1920, 1080), // 16:9
-    Size(1280, 960), // 4:3
-    Size(1280, 720), // 16:9
-    Size(960, 544), // a mod16 version of resolution with 16:9 aspect ratio.
-    Size(800, 450), // 16:9
-    Size(640, 480), // 4:3
-    Size(320, 240), // 4:3
-    Size(320, 180), // 16:9
-    Size(256, 144) // 16:9 For checkSmallSizesAreFilteredOut test.
-)
-
-/** Robolectric test for [SupportedOutputSizesCollector] class */
-@RunWith(ParameterizedRobolectricTestRunner::class)
-@DoNotInstrument
-@Config(minSdk = Build.VERSION_CODES.LOLLIPOP)
-class SupportedOutputSizesCollectorTest(
-    private val sizeCoordinate: SizeCoordinate
-) {
-    private val mockCamcorderProfileHelper = Mockito.mock(CamcorderProfileHelper::class.java)
-    private lateinit var cameraManagerCompat: CameraManagerCompat
-    private lateinit var cameraCharacteristicsCompat: CameraCharacteristicsCompat
-    private lateinit var displayInfoManager: DisplayInfoManager
-    private val context = ApplicationProvider.getApplicationContext<Context>()
-    private var cameraFactory: FakeCameraFactory? = null
-    private var useCaseConfigFactory: UseCaseConfigFactory? = null
-
-    @Suppress("DEPRECATION") // defaultDisplay
-    @Before
-    fun setUp() {
-        DisplayInfoManager.releaseInstance()
-        val windowManager = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
-        Shadows.shadowOf(windowManager.defaultDisplay).setRealWidth(DISPLAY_SIZE.width)
-        Shadows.shadowOf(windowManager.defaultDisplay).setRealHeight(DISPLAY_SIZE.height)
-        Mockito.`when`(
-            mockCamcorderProfileHelper.hasProfile(
-                ArgumentMatchers.anyInt(),
-                ArgumentMatchers.anyInt()
-            )
-        ).thenReturn(true)
-
-        displayInfoManager = DisplayInfoManager.getInstance(context)
-    }
-
-    @After
-    fun tearDown() {
-        CameraXUtil.shutdown()[10000, TimeUnit.MILLISECONDS]
-    }
-
-    @Test
-    fun getSupportedOutputSizes_aspectRatio4x3() {
-        setupCameraAndInitCameraX(
-            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
-        )
-        val supportedOutputSizesCollector = SupportedOutputSizesCollector(
-            DEFAULT_CAMERA_ID,
-            cameraCharacteristicsCompat,
-            displayInfoManager
-        )
-        val useCase = createUseCaseByResolutionSelector(
-            FAKE_USE_CASE,
-            preferredAspectRatio = AspectRatio.RATIO_4_3
-        )
-
-        val resultList = getSupportedOutputSizes(supportedOutputSizesCollector, useCase)
-        val expectedList = listOf(
-            // Matched preferred AspectRatio items, sorted by area size.
-            Size(4032, 3024),
-            Size(1920, 1440),
-            Size(1280, 960),
-            Size(640, 480),
-            Size(320, 240),
-            // Mismatched preferred AspectRatio items, sorted by area size.
-            Size(3840, 2160),
-            Size(1920, 1080),
-            Size(1280, 720),
-            Size(960, 544),
-            Size(800, 450),
-            Size(320, 180),
-            Size(256, 144)
-        )
-        assertThat(resultList).isEqualTo(expectedList)
-    }
-
-    @Test
-    fun getSupportedOutputSizes_aspectRatio16x9_InLimitedDevice() {
-        setupCameraAndInitCameraX(
-            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
-        )
-        val supportedOutputSizesCollector = SupportedOutputSizesCollector(
-            DEFAULT_CAMERA_ID,
-            cameraCharacteristicsCompat,
-            displayInfoManager
-        )
-        val useCase = createUseCaseByResolutionSelector(
-            FAKE_USE_CASE,
-            preferredAspectRatio = AspectRatio.RATIO_16_9
-        )
-
-        val resultList = getSupportedOutputSizes(supportedOutputSizesCollector, useCase)
-        val expectedList = listOf(
-            // Matched preferred AspectRatio items, sorted by area size.
-            Size(3840, 2160),
-            Size(1920, 1080),
-            Size(1280, 720),
-            Size(960, 544),
-            Size(800, 450),
-            Size(320, 180),
-            Size(256, 144),
-            // Mismatched preferred AspectRatio items, sorted by area size.
-            Size(4032, 3024),
-            Size(1920, 1440),
-            Size(1280, 960),
-            Size(640, 480),
-            Size(320, 240)
-        )
-        assertThat(resultList).isEqualTo(expectedList)
-    }
-
-    @Test
-    fun getSupportedOutputSizes_aspectRatio16x9_inLegacyDevice() {
-        setupCameraAndInitCameraX()
-        val supportedOutputSizesCollector = SupportedOutputSizesCollector(
-            DEFAULT_CAMERA_ID,
-            cameraCharacteristicsCompat,
-            displayInfoManager
-        )
-        val useCase = createUseCaseByResolutionSelector(
-            FAKE_USE_CASE,
-            preferredAspectRatio = AspectRatio.RATIO_16_9
-        )
-        val resultList = getSupportedOutputSizes(supportedOutputSizesCollector, useCase)
-        val expectedList: List<Size> = if (Build.VERSION.SDK_INT == 21) {
-            listOf(
-                // Matched maximum JPEG resolution AspectRatio items, sorted by area size.
-                Size(4032, 3024),
-                Size(1920, 1440),
-                Size(1280, 960),
-                Size(640, 480),
-                Size(320, 240),
-                // Non-matched items have been removed by OutputSizesCorrector due to
-                // TargetAspectRatio quirk.
-            )
-        } else {
-            listOf(
-                // Matched preferred AspectRatio items, sorted by area size.
-                Size(3840, 2160),
-                Size(1920, 1080),
-                Size(1280, 720),
-                Size(960, 544),
-                Size(800, 450),
-                Size(320, 180),
-                Size(256, 144),
-                // Mismatched preferred AspectRatio items, sorted by area size.
-                Size(4032, 3024),
-                Size(1920, 1440),
-                Size(1280, 960),
-                Size(640, 480),
-                Size(320, 240)
-            )
-        }
-        assertThat(resultList).isEqualTo(expectedList)
-    }
-
-    @Test
-    fun getSupportedOutputSizes_preferredResolution1920x1080_InLimitedDevice() {
-        setupCameraAndInitCameraX(
-            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
-        )
-        val supportedOutputSizesCollector = SupportedOutputSizesCollector(
-            DEFAULT_CAMERA_ID,
-            cameraCharacteristicsCompat,
-            displayInfoManager
-        )
-        val useCase = createUseCaseByResolutionSelector(
-            FAKE_USE_CASE,
-            preferredResolution = Size(1920, 1080),
-            sizeCoordinate = sizeCoordinate
-        )
-        val resultList = getSupportedOutputSizes(supportedOutputSizesCollector, useCase)
-        // The 4:3 default aspect ratio will make sizes of 4/3 have the 2nd highest priority just
-        // after the preferred resolution.
-        val expectedList =
-            listOf(
-                // Matched preferred resolution size will be put in first priority.
-                Size(1920, 1080),
-                // Matched default preferred AspectRatio items, sorted by area size.
-                Size(1920, 1440),
-                Size(1280, 960),
-                Size(640, 480),
-                Size(320, 240),
-                // Mismatched default preferred AspectRatio items, sorted by area size.
-                Size(1280, 720),
-                Size(960, 544),
-                Size(800, 450),
-                Size(320, 180),
-                Size(256, 144),
-            )
-        assertThat(resultList).isEqualTo(expectedList)
-    }
-
-    @Test
-    fun getSupportedOutputSizes_preferredResolution1920x1080_InLegacyDevice() {
-        setupCameraAndInitCameraX()
-        val supportedOutputSizesCollector = SupportedOutputSizesCollector(
-            DEFAULT_CAMERA_ID,
-            cameraCharacteristicsCompat,
-            displayInfoManager
-        )
-        val useCase = createUseCaseByResolutionSelector(
-            FAKE_USE_CASE,
-            preferredResolution = Size(1920, 1080),
-            sizeCoordinate = sizeCoordinate
-        )
-
-        val resultList = getSupportedOutputSizes(supportedOutputSizesCollector, useCase)
-        // The 4:3 default aspect ratio will make sizes of 4/3 have the 2nd highest priority just
-        // after the preferred resolution.
-        val expectedList = if (Build.VERSION.SDK_INT == 21) {
-                listOf(
-                    // Matched maximum JPEG resolution AspectRatio items, sorted by area size.
-                    Size(1920, 1440),
-                    Size(1280, 960),
-                    Size(640, 480),
-                    Size(320, 240),
-                    // Non-matched items have been removed by OutputSizesCorrector due to
-                    // TargetAspectRatio quirk.
-                )
-            } else {
-                // The 4:3 default aspect ratio will make sizes of 4/3 have the 2nd highest
-                // priority just after the preferred resolution size.
-                listOf(
-                    // Matched default preferred resolution size will be put in first priority.
-                    Size(1920, 1080),
-                    // Matched preferred AspectRatio items, sorted by area size.
-                    Size(1920, 1440),
-                    Size(1280, 960),
-                    Size(640, 480),
-                    Size(320, 240),
-                    // Mismatched preferred default AspectRatio items, sorted by area size.
-                    Size(1280, 720),
-                    Size(960, 544),
-                    Size(800, 450),
-                    Size(320, 180),
-                    Size(256, 144),
-                )
-            }
-        assertThat(resultList).isEqualTo(expectedList)
-    }
-
-    @Test
-    @Suppress("DEPRECATION") /* defaultDisplay */
-    fun getSupportedOutputSizes_smallDisplay_withMaxResolution1920x1080() {
-        // Sets up small display.
-        val windowManager = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
-        Shadows.shadowOf(windowManager.defaultDisplay).setRealWidth(240)
-        Shadows.shadowOf(windowManager.defaultDisplay).setRealHeight(320)
-        displayInfoManager = DisplayInfoManager.getInstance(context)
-
-        setupCameraAndInitCameraX(
-            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
-        )
-
-        val supportedOutputSizesCollector = SupportedOutputSizesCollector(
-            DEFAULT_CAMERA_ID,
-            cameraCharacteristicsCompat,
-            displayInfoManager
-        )
-        val useCase = createUseCaseByResolutionSelector(
-            PREVIEW_USE_CASE,
-            preferredAspectRatio = AspectRatio.RATIO_16_9,
-            maxResolution = Size(1920, 1080)
-        )
-        // Max resolution setting will remove sizes larger than 1920x1080. The auto-resolution
-        // mechanism will try to select the sizes which aspect ratio is nearest to the aspect ratio
-        // of target resolution in priority. Therefore, sizes of aspect ratio 16/9 will be in front
-        // of the returned sizes list and the list is sorted in descending order. Other items will
-        // be put in the following that are sorted by aspect ratio delta and then area size.
-        val resultList = getSupportedOutputSizes(supportedOutputSizesCollector, useCase)
-        val expectedList = listOf(
-            // Matched preferred AspectRatio items, sorted by area size.
-            Size(1920, 1080),
-            Size(1280, 720),
-            Size(960, 544),
-            Size(800, 450),
-            Size(320, 180),
-            Size(256, 144),
-
-            // Mismatched preferred AspectRatio items, sorted by area size.
-            Size(1280, 960),
-            Size(640, 480),
-            Size(320, 240)
-        )
-        assertThat(resultList).isEqualTo(expectedList)
-    }
-
-    @Test
-    fun getSupportedOutputSizes_preferredResolution1800x1440NearTo4x3() {
-        setupCameraAndInitCameraX(
-            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
-        )
-        val supportedOutputSizesCollector = SupportedOutputSizesCollector(
-            DEFAULT_CAMERA_ID,
-            cameraCharacteristicsCompat,
-            displayInfoManager
-        )
-        val useCase = createUseCaseByResolutionSelector(
-            FAKE_USE_CASE,
-            preferredResolution = Size(1800, 1440),
-            sizeCoordinate = sizeCoordinate
-        )
-        val resultList = getSupportedOutputSizes(supportedOutputSizesCollector, useCase)
-        val expectedList =
-            listOf(
-                // No matched preferred resolution size found.
-                // Matched default preferred AspectRatio items, sorted by area size.
-                Size(1920, 1440),
-                Size(1280, 960),
-                Size(640, 480),
-                Size(320, 240),
-                // Mismatched default preferred AspectRatio items, sorted by area size.
-                Size(3840, 2160),
-                Size(1920, 1080),
-                Size(1280, 720),
-                Size(960, 544),
-                Size(800, 450),
-                Size(320, 180),
-                Size(256, 144)
-            )
-        assertThat(resultList).isEqualTo(expectedList)
-    }
-
-    @Test
-    fun getSupportedOutputSizes_preferredResolution1280x600NearTo16x9() {
-        setupCameraAndInitCameraX(
-            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
-        )
-        val supportedOutputSizesCollector = SupportedOutputSizesCollector(
-            DEFAULT_CAMERA_ID,
-            cameraCharacteristicsCompat,
-            displayInfoManager
-        )
-        val useCase = createUseCaseByResolutionSelector(
-            FAKE_USE_CASE,
-            preferredResolution = Size(1280, 600),
-            sizeCoordinate = sizeCoordinate
-        )
-        val resultList = getSupportedOutputSizes(supportedOutputSizesCollector, useCase)
-        val expectedList = listOf(
-            // No matched preferred resolution size found.
-            // Matched default preferred AspectRatio items, sorted by area size.
-            Size(1280, 960),
-            Size(640, 480),
-            Size(320, 240),
-            // Mismatched default preferred AspectRatio items, sorted by area size.
-            Size(1280, 720),
-            Size(960, 544),
-            Size(800, 450),
-            Size(320, 180),
-            Size(256, 144)
-        )
-        assertThat(resultList).isEqualTo(expectedList)
-    }
-
-    @Test
-    fun getSupportedOutputSizes_maxResolution1280x720() {
-        setupCameraAndInitCameraX(
-            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
-        )
-        val supportedOutputSizesCollector = SupportedOutputSizesCollector(
-            DEFAULT_CAMERA_ID,
-            cameraCharacteristicsCompat,
-            displayInfoManager
-        )
-        val useCase = createUseCaseByResolutionSelector(
-            FAKE_USE_CASE,
-            maxResolution = Size(1280, 720)
-        )
-        val resultList = getSupportedOutputSizes(supportedOutputSizesCollector, useCase)
-        val expectedList = listOf(
-            // Matched default preferred AspectRatio items, sorted by area size.
-            Size(640, 480),
-            Size(320, 240),
-            // Mismatched default preferred AspectRatio items, sorted by area size.
-            Size(1280, 720),
-            Size(960, 544),
-            Size(800, 450),
-            Size(320, 180),
-            Size(256, 144)
-        )
-        assertThat(resultList).isEqualTo(expectedList)
-    }
-
-    @Test
-    fun getSupportedOutputSizes_maxResolution720x1280() {
-        setupCameraAndInitCameraX(
-            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
-        )
-        val supportedOutputSizesCollector = SupportedOutputSizesCollector(
-            DEFAULT_CAMERA_ID,
-            cameraCharacteristicsCompat,
-            displayInfoManager
-        )
-        val useCase = createUseCaseByResolutionSelector(
-            FAKE_USE_CASE,
-            maxResolution = Size(720, 1280)
-        )
-        val resultList = getSupportedOutputSizes(supportedOutputSizesCollector, useCase)
-        val expectedList = listOf(
-            // Matched default preferred AspectRatio items, sorted by area size.
-            Size(640, 480),
-            Size(320, 240),
-            // Mismatched default preferred AspectRatio items, sorted by area size.
-            Size(320, 180),
-            Size(256, 144)
-        )
-        assertThat(resultList).isEqualTo(expectedList)
-    }
-
-    @Test
-    fun getSupportedOutputSizes_defaultResolution1280x720_noTargetResolution() {
-        setupCameraAndInitCameraX(
-            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
-        )
-        val supportedOutputSizesCollector = SupportedOutputSizesCollector(
-            DEFAULT_CAMERA_ID,
-            cameraCharacteristicsCompat,
-            displayInfoManager
-        )
-        val useCase = createUseCaseByResolutionSelector(
-            FAKE_USE_CASE,
-            defaultResolution = Size(1280, 720)
-        )
-        val resultList = getSupportedOutputSizes(supportedOutputSizesCollector, useCase)
-        val expectedList = listOf(
-            // Matched default preferred AspectRatio items, sorted by area size.
-            Size(1280, 960),
-            Size(640, 480),
-            Size(320, 240),
-            // Mismatched default preferred AspectRatio items, sorted by area size.
-            Size(1280, 720),
-            Size(960, 544),
-            Size(800, 450),
-            Size(320, 180),
-            Size(256, 144)
-        )
-        assertThat(resultList).isEqualTo(expectedList)
-    }
-
-    @Test
-    fun getSupportedOutputSizes_defaultResolution1280x720_preferredResolution1920x1080() {
-        setupCameraAndInitCameraX(
-            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
-        )
-        val supportedOutputSizesCollector = SupportedOutputSizesCollector(
-            DEFAULT_CAMERA_ID,
-            cameraCharacteristicsCompat,
-            displayInfoManager
-        )
-        val useCase = createUseCaseByResolutionSelector(
-            FAKE_USE_CASE,
-            defaultResolution = Size(1280, 720),
-            preferredResolution = Size(1920, 1080),
-            sizeCoordinate = sizeCoordinate
-        )
-        val resultList = getSupportedOutputSizes(supportedOutputSizesCollector, useCase)
-        val expectedList = listOf(
-            // Matched preferred resolution size will be put in first priority.
-            Size(1920, 1080),
-            // Matched default preferred AspectRatio items, sorted by area size.
-            Size(1920, 1440),
-            Size(1280, 960),
-            Size(640, 480),
-            Size(320, 240),
-            // Mismatched default preferred AspectRatio items, sorted by area size.
-            Size(1280, 720),
-            Size(960, 544),
-            Size(800, 450),
-            Size(320, 180),
-            Size(256, 144)
-        )
-        assertThat(resultList).isEqualTo(expectedList)
-    }
-
-    @Test
-    fun getSupportedOutputSizes_fallbackToGuaranteedResolution_whenNotFulfillConditions() {
-        setupCameraAndInitCameraX(
-            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
-            supportedSizes = arrayOf(
-                Size(640, 480),
-                Size(320, 240),
-                Size(320, 180),
-                Size(256, 144)
-            )
-        )
-        val supportedOutputSizesCollector = SupportedOutputSizesCollector(
-            DEFAULT_CAMERA_ID,
-            cameraCharacteristicsCompat,
-            displayInfoManager
-        )
-        val useCase = createUseCaseByResolutionSelector(
-            FAKE_USE_CASE,
-            preferredResolution = Size(1920, 1080),
-            sizeCoordinate = sizeCoordinate
-        )
-        val resultList = getSupportedOutputSizes(supportedOutputSizesCollector, useCase)
-        val expectedList = listOf(
-            // No matched preferred resolution size found.
-            // Matched default preferred AspectRatio items, sorted by area size.
-            Size(640, 480),
-            Size(320, 240),
-            // Mismatched default preferred AspectRatio items, sorted by area size.
-            Size(320, 180),
-            Size(256, 144)
-        )
-        assertThat(resultList).isEqualTo(expectedList)
-    }
-
-    @Test
-    fun getSupportedOutputSizes_whenMaxSizeSmallerThanSmallTargetResolution() {
-        setupCameraAndInitCameraX(
-            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
-            supportedSizes = arrayOf(
-                Size(640, 480),
-                Size(320, 240),
-                Size(320, 180),
-                Size(256, 144)
-            )
-        )
-        val supportedOutputSizesCollector = SupportedOutputSizesCollector(
-            DEFAULT_CAMERA_ID,
-            cameraCharacteristicsCompat,
-            displayInfoManager
-        )
-        val useCase = createUseCaseByResolutionSelector(
-            FAKE_USE_CASE,
-            preferredResolution = Size(320, 240),
-            sizeCoordinate = sizeCoordinate,
-            maxResolution = Size(320, 180)
-        )
-        val resultList = getSupportedOutputSizes(supportedOutputSizesCollector, useCase)
-        val expectedList = listOf(Size(320, 180), Size(256, 144))
-        assertThat(resultList).isEqualTo(expectedList)
-    }
-
-    @Test
-    fun getSupportedOutputSizes_whenMaxSizeSmallerThanBigPreferredResolution() {
-        setupCameraAndInitCameraX(
-            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
-        )
-        val supportedOutputSizesCollector = SupportedOutputSizesCollector(
-            DEFAULT_CAMERA_ID,
-            cameraCharacteristicsCompat,
-            displayInfoManager
-        )
-        val useCase = createUseCaseByResolutionSelector(
-            FAKE_USE_CASE,
-            preferredResolution = Size(3840, 2160),
-            sizeCoordinate = sizeCoordinate,
-            maxResolution = Size(1920, 1080)
-        )
-        val resultList = getSupportedOutputSizes(supportedOutputSizesCollector, useCase)
-        val expectedList = listOf(
-            // No matched preferred resolution size found after filtering by max resolution setting.
-            // Matched default preferred AspectRatio items, sorted by area size.
-            Size(1280, 960),
-            Size(640, 480),
-            Size(320, 240),
-            // Mismatched default preferred AspectRatio items, sorted by area size.
-            Size(1920, 1080),
-            Size(1280, 720),
-            Size(960, 544),
-            Size(800, 450),
-            Size(320, 180),
-            Size(256, 144)
-        )
-        assertThat(resultList).isEqualTo(expectedList)
-    }
-
-    @Test
-    fun getSupportedOutputSizes_whenNoSizeBetweenMaxSizeAndPreferredResolution() {
-        setupCameraAndInitCameraX(
-            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
-            supportedSizes = arrayOf(
-                Size(640, 480),
-                Size(320, 240),
-                Size(320, 180),
-                Size(256, 144)
-            )
-        )
-        val supportedOutputSizesCollector = SupportedOutputSizesCollector(
-            DEFAULT_CAMERA_ID,
-            cameraCharacteristicsCompat,
-            displayInfoManager
-        )
-        val useCase = createUseCaseByResolutionSelector(
-            FAKE_USE_CASE,
-            preferredResolution = Size(320, 190),
-            sizeCoordinate = sizeCoordinate,
-            maxResolution = Size(320, 200)
-        )
-        val resultList = getSupportedOutputSizes(supportedOutputSizesCollector, useCase)
-        val expectedList = listOf(Size(320, 180), Size(256, 144))
-        assertThat(resultList).isEqualTo(expectedList)
-    }
-
-    @Test
-    fun getSupportedOutputSizes_whenPreferredResolutionSmallerThanAnySize() {
-        setupCameraAndInitCameraX(
-            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
-            supportedSizes = arrayOf(
-                Size(640, 480),
-                Size(320, 240),
-                Size(320, 180),
-                Size(256, 144)
-            )
-        )
-        val supportedOutputSizesCollector = SupportedOutputSizesCollector(
-            DEFAULT_CAMERA_ID,
-            cameraCharacteristicsCompat,
-            displayInfoManager
-        )
-        val useCase = createUseCaseByResolutionSelector(
-            FAKE_USE_CASE,
-            preferredResolution = Size(192, 144),
-            sizeCoordinate = sizeCoordinate
-        )
-        val resultList = getSupportedOutputSizes(supportedOutputSizesCollector, useCase)
-        val expectedList = listOf(Size(320, 240), Size(256, 144))
-        assertThat(resultList).isEqualTo(expectedList)
-    }
-
-    @Test
-    fun getSupportedOutputSizes_whenMaxResolutionSmallerThanAnySize() {
-        setupCameraAndInitCameraX(
-            supportedSizes = arrayOf(
-                Size(640, 480),
-                Size(320, 240),
-                Size(320, 180),
-                Size(256, 144)
-            )
-        )
-        val supportedOutputSizesCollector = SupportedOutputSizesCollector(
-            DEFAULT_CAMERA_ID,
-            cameraCharacteristicsCompat,
-            displayInfoManager
-        )
-        val useCase = createUseCaseByResolutionSelector(
-            FAKE_USE_CASE,
-            maxResolution = Size(192, 144)
-        )
-        // All sizes will be filtered out by the max resolution 192x144 setting and an
-        // IllegalArgumentException will be thrown.
-        Assert.assertThrows(IllegalArgumentException::class.java) {
-            getSupportedOutputSizes(supportedOutputSizesCollector, useCase)
-        }
-    }
-
-    @Test
-    fun getSupportedOutputSizes_whenMod16IsIgnoredForSmallSizes() {
-        setupCameraAndInitCameraX(
-            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
-            supportedSizes = arrayOf(
-                Size(640, 480),
-                Size(320, 240),
-                Size(320, 180),
-                Size(296, 144),
-                Size(256, 144)
-            )
-        )
-        val supportedOutputSizesCollector = SupportedOutputSizesCollector(
-            DEFAULT_CAMERA_ID,
-            cameraCharacteristicsCompat,
-            displayInfoManager
-        )
-        val useCase = createUseCaseByResolutionSelector(
-            FAKE_USE_CASE,
-            preferredResolution = Size(185, 90),
-            sizeCoordinate = sizeCoordinate
-        )
-        val resultList = getSupportedOutputSizes(supportedOutputSizesCollector, useCase)
-        val expectedList = listOf(
-            // No matched preferred resolution size found.
-            // Matched default preferred AspectRatio items, sorted by area size.
-            Size(320, 240),
-            // Mismatched default preferred AspectRatio items, sorted by area size.
-            Size(256, 144),
-            Size(296, 144)
-        )
-        assertThat(resultList).isEqualTo(expectedList)
-    }
-
-    @Test
-    fun getSupportedOutputSizes_whenOneMod16SizeClosestToTargetResolution() {
-        setupCameraAndInitCameraX(
-            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
-            supportedSizes = arrayOf(
-                Size(1920, 1080),
-                Size(1440, 1080),
-                Size(1280, 960),
-                Size(1280, 720),
-                Size(864, 480), // This is a 16:9 mod16 size that is closest to 2016x1080
-                Size(768, 432),
-                Size(640, 480),
-                Size(640, 360),
-                Size(480, 360),
-                Size(384, 288)
-            )
-        )
-        val supportedOutputSizesCollector = SupportedOutputSizesCollector(
-            DEFAULT_CAMERA_ID,
-            cameraCharacteristicsCompat,
-            displayInfoManager
-        )
-        val useCase = createUseCaseByResolutionSelector(
-            FAKE_USE_CASE,
-            preferredResolution = Size(1080, 2016),
-            sizeCoordinate = sizeCoordinate
-        )
-        val resultList = getSupportedOutputSizes(supportedOutputSizesCollector, useCase)
-        val expectedList = listOf(
-            // No matched preferred resolution size found.
-            // Matched default preferred AspectRatio items, sorted by area size.
-            Size(1440, 1080),
-            Size(1280, 960),
-            Size(640, 480),
-            Size(480, 360),
-            Size(384, 288),
-            // Mismatched default preferred AspectRatio items, sorted by area size.
-            Size(1920, 1080),
-            Size(1280, 720),
-            Size(864, 480),
-            Size(768, 432),
-            Size(640, 360)
-        )
-        assertThat(resultList).isEqualTo(expectedList)
-    }
-
-    @Test
-    fun getSupportedOutputSizesWithPortraitPixelArraySize_aspectRatio16x9() {
-        // Sets the sensor orientation as 0 and pixel array size as a portrait size to simulate a
-        // phone device which majorly supports portrait output sizes.
-        setupCameraAndInitCameraX(
-            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
-            sensorOrientation = SENSOR_ORIENTATION_0,
-            pixelArraySize = PORTRAIT_PIXEL_ARRAY_SIZE,
-            supportedSizes = arrayOf(
-                Size(1080, 1920),
-                Size(1080, 1440),
-                Size(960, 1280),
-                Size(720, 1280),
-                Size(960, 540),
-                Size(480, 640),
-                Size(640, 480),
-                Size(360, 480)
-            )
-        )
-        val supportedOutputSizesCollector = SupportedOutputSizesCollector(
-            DEFAULT_CAMERA_ID,
-            cameraCharacteristicsCompat,
-            displayInfoManager
-        )
-        val useCase = createUseCaseByResolutionSelector(
-            FAKE_USE_CASE,
-            preferredAspectRatio = AspectRatio.RATIO_16_9
-        )
-        val resultList = getSupportedOutputSizes(supportedOutputSizesCollector, useCase)
-        val expectedList = listOf(
-            // Matched preferred AspectRatio items, sorted by area size.
-            Size(1080, 1920),
-            Size(720, 1280),
-            // Mismatched preferred AspectRatio items, sorted by area size.
-            Size(1080, 1440),
-            Size(960, 1280),
-            Size(480, 640),
-            Size(360, 480),
-            Size(640, 480),
-            Size(960, 540)
-        )
-        assertThat(resultList).isEqualTo(expectedList)
-    }
-
-    @Test
-    fun getSupportedOutputSizesOnTabletWithPortraitPixelArraySize_aspectRatio16x9() {
-        // Sets the sensor orientation as 90 and pixel array size as a portrait size to simulate a
-        // tablet device which majorly supports portrait output sizes.
-        setupCameraAndInitCameraX(
-            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
-            sensorOrientation = SENSOR_ORIENTATION_90,
-            pixelArraySize = PORTRAIT_PIXEL_ARRAY_SIZE,
-            supportedSizes = arrayOf(
-                Size(1080, 1920),
-                Size(1080, 1440),
-                Size(960, 1280),
-                Size(720, 1280),
-                Size(960, 540),
-                Size(480, 640),
-                Size(640, 480),
-                Size(360, 480)
-            )
-        )
-        val supportedOutputSizesCollector = SupportedOutputSizesCollector(
-            DEFAULT_CAMERA_ID,
-            cameraCharacteristicsCompat,
-            displayInfoManager
-        )
-        val useCase = createUseCaseByResolutionSelector(
-            FAKE_USE_CASE,
-            preferredAspectRatio = AspectRatio.RATIO_16_9
-        )
-        // Due to the pixel array size is portrait, sizes of aspect ratio 9/16 will be in front of
-        // the returned sizes list and the list is sorted in descending order. Other items will be
-        // put in the following that are sorted by aspect ratio delta and then area size.
-        val resultList = getSupportedOutputSizes(supportedOutputSizesCollector, useCase)
-        val expectedList = listOf(
-            // Matched preferred AspectRatio items, sorted by area size.
-            Size(1080, 1920),
-            Size(720, 1280),
-            // Mismatched preferred AspectRatio items, sorted by aspect ratio delta then area size.
-            Size(1080, 1440),
-            Size(960, 1280),
-            Size(480, 640),
-            Size(360, 480),
-            Size(640, 480),
-            Size(960, 540)
-        )
-        assertThat(resultList).isEqualTo(expectedList)
-    }
-
-    @Test
-    fun getSupportedOutputSizesOnTablet_aspectRatio16x9() {
-        setupCameraAndInitCameraX(
-            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
-            sensorOrientation = SENSOR_ORIENTATION_0,
-            pixelArraySize = LANDSCAPE_PIXEL_ARRAY_SIZE
-        )
-        val supportedOutputSizesCollector = SupportedOutputSizesCollector(
-            DEFAULT_CAMERA_ID,
-            cameraCharacteristicsCompat,
-            displayInfoManager
-        )
-        val useCase = createUseCaseByResolutionSelector(
-            FAKE_USE_CASE,
-            preferredAspectRatio = AspectRatio.RATIO_16_9
-        )
-        val resultList = getSupportedOutputSizes(supportedOutputSizesCollector, useCase)
-        val expectedList = listOf(
-            // Matched preferred AspectRatio items, sorted by area size.
-            Size(3840, 2160),
-            Size(1920, 1080),
-            Size(1280, 720),
-            Size(960, 544),
-            Size(800, 450),
-            Size(320, 180),
-            Size(256, 144),
-            // Mismatched preferred AspectRatio items, sorted by area size.
-            Size(4032, 3024),
-            Size(1920, 1440),
-            Size(1280, 960),
-            Size(640, 480),
-            Size(320, 240)
-        )
-        assertThat(resultList).isEqualTo(expectedList)
-    }
-
-    @Test
-    fun getSupportedOutputSizesOnTabletWithPortraitSizes_aspectRatio16x9() {
-        setupCameraAndInitCameraX(
-            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
-            sensorOrientation = SENSOR_ORIENTATION_0, supportedSizes = arrayOf(
-                Size(1920, 1080),
-                Size(1440, 1080),
-                Size(1280, 960),
-                Size(1280, 720),
-                Size(540, 960),
-                Size(640, 480),
-                Size(480, 640),
-                Size(480, 360)
-            )
-        )
-        val supportedOutputSizesCollector = SupportedOutputSizesCollector(
-            DEFAULT_CAMERA_ID,
-            cameraCharacteristicsCompat,
-            displayInfoManager
-        )
-        val useCase = createUseCaseByResolutionSelector(
-            FAKE_USE_CASE,
-            preferredAspectRatio = AspectRatio.RATIO_16_9
-        )
-        val resultList = getSupportedOutputSizes(supportedOutputSizesCollector, useCase)
-        val expectedList = listOf(
-            // Matched preferred AspectRatio items, sorted by area size.
-            Size(1920, 1080),
-            Size(1280, 720),
-            // Mismatched preferred AspectRatio items, sorted by area size.
-            Size(1440, 1080),
-            Size(1280, 960),
-            Size(640, 480),
-            Size(480, 360),
-            Size(480, 640),
-            Size(540, 960)
-        )
-        assertThat(resultList).isEqualTo(expectedList)
-    }
-
-    @Config(minSdk = Build.VERSION_CODES.M)
-    @Test
-    fun getSupportedOutputSizes_whenHighResolutionIsEnabled_aspectRatio16x9() {
-        setupCameraAndInitCameraX(
-            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
-            capabilities = intArrayOf(
-                CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE
-            ),
-            supportedHighResolutionSizes = arrayOf(Size(8000, 6000), Size(8000, 4500))
-        )
-        val supportedOutputSizesCollector = SupportedOutputSizesCollector(
-            DEFAULT_CAMERA_ID,
-            cameraCharacteristicsCompat,
-            displayInfoManager
-        )
-
-        val useCase = createUseCaseByResolutionSelector(
-            FAKE_USE_CASE,
-            preferredAspectRatio = AspectRatio.RATIO_16_9,
-            highResolutionEnabled = true
-        )
-        val resultList = getSupportedOutputSizes(supportedOutputSizesCollector, useCase)
-        val expectedList = listOf(
-            // Matched preferred AspectRatio items, sorted by area size.
-            Size(8000, 4500),
-            Size(3840, 2160),
-            Size(1920, 1080),
-            Size(1280, 720),
-            Size(960, 544),
-            Size(800, 450),
-            Size(320, 180),
-            Size(256, 144),
-            // Mismatched preferred AspectRatio items, sorted by area size.
-            Size(8000, 6000),
-            Size(4032, 3024),
-            Size(1920, 1440),
-            Size(1280, 960),
-            Size(640, 480),
-            Size(320, 240)
-        )
-        assertThat(resultList).isEqualTo(expectedList)
-    }
-
-    @Config(minSdk = Build.VERSION_CODES.M)
-    @Test
-    fun highResolutionCanNotBeSelected_whenHighResolutionForceDisabled() {
-        setupCameraAndInitCameraX(
-            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
-            capabilities = intArrayOf(
-                CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE
-            ),
-            supportedHighResolutionSizes = arrayOf(Size(8000, 6000), Size(8000, 4500))
-        )
-        val supportedOutputSizesCollector = SupportedOutputSizesCollector(
-            DEFAULT_CAMERA_ID,
-            cameraCharacteristicsCompat,
-            displayInfoManager
-        )
-
-        val useCase = createUseCaseByResolutionSelector(
-            FAKE_USE_CASE,
-            preferredAspectRatio = AspectRatio.RATIO_16_9,
-            highResolutionEnabled = true,
-            highResolutionForceDisabled = true
-        )
-        val resultList = getSupportedOutputSizes(supportedOutputSizesCollector, useCase)
-        val expectedList = listOf(
-            // Matched preferred AspectRatio items, sorted by area size.
-            Size(3840, 2160),
-            Size(1920, 1080),
-            Size(1280, 720),
-            Size(960, 544),
-            Size(800, 450),
-            Size(320, 180),
-            Size(256, 144),
-            // Mismatched preferred AspectRatio items, sorted by area size.
-            Size(4032, 3024),
-            Size(1920, 1440),
-            Size(1280, 960),
-            Size(640, 480),
-            Size(320, 240)
-        )
-        assertThat(resultList).isEqualTo(expectedList)
-    }
-
-    /**
-     * Sets up camera according to the specified settings and initialize [CameraX].
-     *
-     * @param cameraId the camera id to be set up. Default value is [DEFAULT_CAMERA_ID].
-     * @param hardwareLevel the hardware level of the camera. Default value is
-     * [CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY].
-     * @param sensorOrientation the sensor orientation of the camera. Default value is
-     * [SENSOR_ORIENTATION_90].
-     * @param pixelArraySize the active pixel array size of the camera. Default value is
-     * [LANDSCAPE_PIXEL_ARRAY_SIZE].
-     * @param supportedSizes the supported sizes of the camera. Default value is
-     * [DEFAULT_SUPPORTED_SIZES].
-     * @param capabilities the capabilities of the camera. Default value is null.
-     */
-    private fun setupCameraAndInitCameraX(
-        cameraId: String = DEFAULT_CAMERA_ID,
-        hardwareLevel: Int = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY,
-        sensorOrientation: Int = SENSOR_ORIENTATION_90,
-        pixelArraySize: Size = LANDSCAPE_PIXEL_ARRAY_SIZE,
-        supportedSizes: Array<Size> = DEFAULT_SUPPORTED_SIZES,
-        supportedHighResolutionSizes: Array<Size>? = null,
-        capabilities: IntArray? = null
-    ) {
-        setupCamera(
-            cameraId,
-            hardwareLevel,
-            sensorOrientation,
-            pixelArraySize,
-            supportedSizes,
-            supportedHighResolutionSizes,
-            capabilities
-        )
-
-        @CameraSelector.LensFacing val lensFacingEnum = CameraUtil.getLensFacingEnumFromInt(
-            CameraCharacteristics.LENS_FACING_BACK
-        )
-        cameraManagerCompat = CameraManagerCompat.from(context)
-        val cameraInfo = FakeCameraInfoInternal(
-            cameraId,
-            sensorOrientation,
-            CameraCharacteristics.LENS_FACING_BACK
-        )
-
-        cameraFactory = FakeCameraFactory().apply {
-            insertCamera(lensFacingEnum, cameraId) {
-                FakeCamera(cameraId, null, cameraInfo)
-            }
-        }
-
-        cameraCharacteristicsCompat = cameraManagerCompat.getCameraCharacteristicsCompat(cameraId)
-
-        initCameraX()
-    }
-
-    /**
-     * Initializes the [CameraX].
-     */
-    private fun initCameraX() {
-        val surfaceManagerProvider =
-            CameraDeviceSurfaceManager.Provider { context, _, availableCameraIds ->
-                Camera2DeviceSurfaceManager(
-                    context,
-                    mockCamcorderProfileHelper,
-                    CameraManagerCompat.from(this@SupportedOutputSizesCollectorTest.context),
-                    availableCameraIds
-                )
-            }
-        val cameraXConfig = CameraXConfig.Builder.fromConfig(Camera2Config.defaultConfig())
-            .setDeviceSurfaceManagerProvider(surfaceManagerProvider)
-            .setCameraFactoryProvider { _, _, _ -> cameraFactory!! }
-            .build()
-        val cameraX: CameraX = try {
-            CameraXUtil.getOrCreateInstance(context) { cameraXConfig }.get()
-        } catch (e: ExecutionException) {
-            throw IllegalStateException("Unable to initialize CameraX for test.")
-        } catch (e: InterruptedException) {
-            throw IllegalStateException("Unable to initialize CameraX for test.")
-        }
-        useCaseConfigFactory = cameraX.defaultConfigFactory
-    }
-
-    /**
-     * Gets the supported output sizes by the converted ResolutionSelector use case config which
-     * will also be converted when a use case is bound to the lifecycle.
-     */
-    private fun getSupportedOutputSizes(
-        supportedOutputSizesCollector: SupportedOutputSizesCollector,
-        useCase: UseCase,
-        cameraId: String = DEFAULT_CAMERA_ID,
-        sensorOrientation: Int = SENSOR_ORIENTATION_90,
-        useCaseConfigFactory: UseCaseConfigFactory = this.useCaseConfigFactory!!
-    ): List<Size?> {
-        // Converts the use case config to new ResolutionSelector config
-        val useCaseToConfigMap = Configs.useCaseConfigMapWithDefaultSettingsFromUseCaseList(
-            cameraFactory!!.getCamera(cameraId).cameraInfoInternal,
-            listOf(useCase),
-            useCaseConfigFactory
-        )
-
-        val useCaseConfig = useCaseToConfigMap[useCase]!!
-        val resolutionSelector = (useCaseConfig as ImageOutputConfig).resolutionSelector
-        val imageFormat = useCaseConfig.inputFormat
-        val isHighResolutionDisabled = useCaseConfig.isHigResolutionDisabled(false)
-        val customizedSupportSizes = getCustomizedSupportSizesFromConfig(imageFormat, useCaseConfig)
-        val miniBoundingSize = SupportedOutputSizesCollector.getTargetSizeByResolutionSelector(
-            resolutionSelector,
-            Surface.ROTATION_0,
-            sensorOrientation,
-            CameraCharacteristics.LENS_FACING_BACK
-        ) ?: useCaseConfig.getDefaultResolution(null)
-
-        return supportedOutputSizesCollector.getSupportedOutputSizes(
-            resolutionSelector,
-            imageFormat,
-            miniBoundingSize,
-            isHighResolutionDisabled,
-            customizedSupportSizes
-        )
-    }
-
-    companion object {
-
-        /**
-         * Sets up camera according to the specified settings.
-         *
-         * @param cameraId the camera id to be set up. Default value is [DEFAULT_CAMERA_ID].
-         * @param hardwareLevel the hardware level of the camera. Default value is
-         * [CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY].
-         * @param sensorOrientation the sensor orientation of the camera. Default value is
-         * [SENSOR_ORIENTATION_90].
-         * @param pixelArraySize the active pixel array size of the camera. Default value is
-         * [LANDSCAPE_PIXEL_ARRAY_SIZE].
-         * @param supportedSizes the supported sizes of the camera. Default value is
-         * [DEFAULT_SUPPORTED_SIZES].
-         * @param capabilities the capabilities of the camera. Default value is null.
-         */
-        @JvmStatic
-        fun setupCamera(
-            cameraId: String = DEFAULT_CAMERA_ID,
-            hardwareLevel: Int = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY,
-            sensorOrientation: Int = SENSOR_ORIENTATION_90,
-            pixelArraySize: Size = LANDSCAPE_PIXEL_ARRAY_SIZE,
-            supportedSizes: Array<Size> = DEFAULT_SUPPORTED_SIZES,
-            supportedHighResolutionSizes: Array<Size>? = null,
-            capabilities: IntArray? = null
-        ) {
-            val mockMap = Mockito.mock(StreamConfigurationMap::class.java).also {
-                // Sets up the supported sizes
-                Mockito.`when`(it.getOutputSizes(ArgumentMatchers.anyInt()))
-                    .thenReturn(supportedSizes)
-                // ImageFormat.PRIVATE was supported since API level 23. Before that, the supported
-                // output sizes need to be retrieved via SurfaceTexture.class.
-                Mockito.`when`(it.getOutputSizes(SurfaceTexture::class.java))
-                    .thenReturn(supportedSizes)
-                // This is setup for the test to determine RECORD size from StreamConfigurationMap
-                Mockito.`when`(it.getOutputSizes(MediaRecorder::class.java))
-                    .thenReturn(supportedSizes)
-
-                // setup to return different minimum frame durations depending on resolution
-                // minimum frame durations were designated only for the purpose of testing
-                Mockito.`when`(it.getOutputMinFrameDuration(anyInt(), eq(Size(4032, 3024))))
-                    .thenReturn(50000000L) // 20 fps, size maximum
-
-                Mockito.`when`(it.getOutputMinFrameDuration(anyInt(), eq(Size(3840, 2160))))
-                    .thenReturn(40000000L) // 25, size record
-
-                Mockito.`when`(it.getOutputMinFrameDuration(anyInt(), eq(Size(1920, 1440))))
-                    .thenReturn(30000000L) // 30
-
-                Mockito.`when`(it.getOutputMinFrameDuration(anyInt(), eq(Size(1920, 1080))))
-                    .thenReturn(28000000L) // 35
-
-                Mockito.`when`(it.getOutputMinFrameDuration(anyInt(), eq(Size(1280, 960))))
-                    .thenReturn(25000000L) // 40
-
-                Mockito.`when`(it.getOutputMinFrameDuration(anyInt(), eq(Size(1280, 720))))
-                    .thenReturn(22000000L) // 45, size preview/display
-
-                Mockito.`when`(it.getOutputMinFrameDuration(anyInt(), eq(Size(960, 544))))
-                    .thenReturn(20000000L) // 50
-
-                Mockito.`when`(it.getOutputMinFrameDuration(anyInt(), eq(Size(800, 450))))
-                    .thenReturn(16666000L) // 60fps
-
-                Mockito.`when`(it.getOutputMinFrameDuration(anyInt(), eq(Size(640, 480))))
-                    .thenReturn(16666000L) // 60fps
-
-                // Sets up the supported high resolution sizes
-                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
-                    Mockito.`when`(it.getHighResolutionOutputSizes(ArgumentMatchers.anyInt()))
-                        .thenReturn(supportedHighResolutionSizes)
-                }
-            }
-
-            val characteristics = ShadowCameraCharacteristics.newCameraCharacteristics()
-            Shadow.extract<ShadowCameraCharacteristics>(characteristics).apply {
-                set(CameraCharacteristics.LENS_FACING, CameraCharacteristics.LENS_FACING_BACK)
-                set(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL, hardwareLevel)
-                set(CameraCharacteristics.SENSOR_ORIENTATION, sensorOrientation)
-                set(CameraCharacteristics.SENSOR_INFO_PIXEL_ARRAY_SIZE, pixelArraySize)
-                set(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP, mockMap)
-                capabilities?.let {
-                    set(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES, it)
-                }
-            }
-
-            val cameraManager = ApplicationProvider.getApplicationContext<Context>()
-                .getSystemService(Context.CAMERA_SERVICE) as CameraManager
-            (Shadow.extract<Any>(cameraManager) as ShadowCameraManager)
-                .addCamera(cameraId, characteristics)
-        }
-
-        /**
-         * Creates [Preview], [ImageCapture], [ImageAnalysis], [androidx.camera.core.VideoCapture] or
-         * FakeUseCase by the legacy or new ResolutionSelector API according to the specified settings.
-         *
-         * @param useCaseType Which of [Preview], [ImageCapture], [ImageAnalysis],
-         * [androidx.camera.core.VideoCapture] and FakeUseCase should be created.
-         * @param preferredAspectRatio the target aspect ratio setting. Default is UNKNOWN_ASPECT_RATIO
-         * and no target aspect ratio will be set to the created use case.
-         * @param preferredResolution the preferred resolution setting which should be specified in the
-         * camera sensor coordinate. The resolution will be transformed to set via
-         * [ResolutionSelector.Builder.setPreferredResolutionByViewSize] if size coordinate is
-         * [SizeCoordinate.ANDROID_VIEW]. Default is null.
-         * @param maxResolution the max resolution setting. Default is null.
-         * @param highResolutionEnabled the high resolution setting, Default is false.
-         * @param highResolutionForceDisabled the high resolution force disabled setting, Default
-         * is false. This will be set in the use case config to force disable high resolution.
-         * @param defaultResolution the default resolution setting. Default is null.
-         * @param supportedResolutions the customized supported resolutions. Default is null.
-         */
-        @JvmStatic
-        fun createUseCaseByResolutionSelector(
-            useCaseType: Int,
-            preferredAspectRatio: Int = UNKNOWN_ASPECT_RATIO,
-            preferredResolution: Size? = null,
-            sizeCoordinate: SizeCoordinate = SizeCoordinate.CAMERA_SENSOR,
-            maxResolution: Size? = null,
-            highResolutionEnabled: Boolean = false,
-            highResolutionForceDisabled: Boolean = false,
-            defaultResolution: Size? = null,
-            supportedResolutions: List<Pair<Int, Array<Size>>>? = null,
-            customOrderedResolutions: List<Size>? = null,
-        ): UseCase {
-            val builder = when (useCaseType) {
-                PREVIEW_USE_CASE -> Preview.Builder()
-                IMAGE_CAPTURE_USE_CASE -> ImageCapture.Builder()
-                IMAGE_ANALYSIS_USE_CASE -> ImageAnalysis.Builder()
-                else -> FakeUseCaseConfig.Builder(UseCaseConfigFactory.CaptureType.IMAGE_CAPTURE)
-            }
-
-            val resolutionSelectorBuilder = ResolutionSelector.Builder()
-
-            if (preferredAspectRatio != UNKNOWN_ASPECT_RATIO) {
-                resolutionSelectorBuilder.setPreferredAspectRatio(preferredAspectRatio)
-            }
-
-            preferredResolution?.let {
-                if (sizeCoordinate == SizeCoordinate.CAMERA_SENSOR) {
-                    resolutionSelectorBuilder.setPreferredResolution(it)
-                } else {
-                    val flippedResolution = Size(
-                        /* width= */ it.height,
-                        /* height= */ it.width
-                    )
-                    resolutionSelectorBuilder.setPreferredResolutionByViewSize(flippedResolution)
-                }
-            }
-
-            maxResolution?.let { resolutionSelectorBuilder.setMaxResolution(it) }
-            resolutionSelectorBuilder.setHighResolutionEnabled(highResolutionEnabled)
-
-            builder.setResolutionSelector(resolutionSelectorBuilder.build())
-            builder.setHighResolutionDisabled(highResolutionForceDisabled)
-
-            defaultResolution?.let { builder.setDefaultResolution(it) }
-            supportedResolutions?.let { builder.setSupportedResolutions(it) }
-            customOrderedResolutions?.let { builder.setCustomOrderedResolutions(it) }
-            return builder.build()
-        }
-
-        @JvmStatic
-        fun getCustomizedSupportSizesFromConfig(
-            imageFormat: Int,
-            config: ImageOutputConfig
-        ): Array<Size>? {
-            var outputSizes: Array<Size>? = null
-
-            // Try to retrieve customized supported resolutions from config.
-            val formatResolutionsPairList = config.getSupportedResolutions(null)
-            if (formatResolutionsPairList != null) {
-                for (formatResolutionPair in formatResolutionsPairList) {
-                    if (formatResolutionPair.first == imageFormat) {
-                        outputSizes = formatResolutionPair.second
-                        break
-                    }
-                }
-            }
-            return outputSizes
-        }
-
-        @JvmStatic
-        @ParameterizedRobolectricTestRunner.Parameters(name = "sizeCoordinate = {0}")
-        fun data() = listOf(
-            SizeCoordinate.CAMERA_SENSOR,
-            SizeCoordinate.ANDROID_VIEW
-        )
-    }
-}
\ No newline at end of file
diff --git a/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/SupportedSizeConstraintsTest.java b/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/SupportedSizeConstraintsTest.java
index 3ed7e19..7505ee1 100644
--- a/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/SupportedSizeConstraintsTest.java
+++ b/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/SupportedSizeConstraintsTest.java
@@ -35,7 +35,9 @@
 
 import androidx.annotation.NonNull;
 import androidx.camera.camera2.Camera2Config;
+import androidx.camera.camera2.internal.compat.CameraCharacteristicsCompat;
 import androidx.camera.camera2.internal.compat.CameraManagerCompat;
+import androidx.camera.camera2.internal.compat.StreamConfigurationMapCompat;
 import androidx.camera.camera2.internal.compat.workaround.ExcludedSupportedSizesContainer;
 import androidx.camera.core.CameraUnavailableException;
 import androidx.camera.core.CameraXConfig;
@@ -45,8 +47,6 @@
 import androidx.camera.testing.CameraXUtil;
 import androidx.camera.testing.fakes.FakeCamera;
 import androidx.camera.testing.fakes.FakeCameraFactory;
-import androidx.camera.testing.fakes.FakeUseCase;
-import androidx.camera.testing.fakes.FakeUseCaseConfig;
 import androidx.test.core.app.ApplicationProvider;
 
 import org.codehaus.plexus.util.ReflectionUtils;
@@ -152,12 +152,13 @@
         // mSupportedSizes modified unexpectedly.
         assertThat(Arrays.asList(mSupportedSizes)).containsAtLeastElementsIn(excludedSizes);
 
-        // Make the fake use case have JPEG format since those sizes are excluded for JPEG format.
-        final FakeUseCase useCase = new FakeUseCaseConfig.Builder()
-                .setBufferFormat(ImageFormat.JPEG)
-                .build();
-        final List<Size> resultList = supportedSurfaceCombination.getSupportedOutputSizes(
-                useCase.getCurrentConfig());
+        // These sizes should be excluded when retrieving output sizes via
+        // StreamConfigurationMapCompat#getOutputSizes()
+        CameraCharacteristicsCompat characteristicsCompat =
+                mCameraManagerCompat.getCameraCharacteristicsCompat(BACK_CAMERA_ID);
+        StreamConfigurationMapCompat mapCompat =
+                characteristicsCompat.getStreamConfigurationMapCompat();
+        final List<Size> resultList = Arrays.asList(mapCompat.getOutputSizes(ImageFormat.JPEG));
         assertThat(resultList).containsNoneIn(excludedSizes);
     }
 
diff --git a/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/SupportedSurfaceCombinationTest.kt b/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/SupportedSurfaceCombinationTest.kt
index 51f2a79..ed4755d 100644
--- a/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/SupportedSurfaceCombinationTest.kt
+++ b/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/SupportedSurfaceCombinationTest.kt
@@ -1,4 +1,3 @@
-
 /*
  * Copyright 2022 The Android Open Source Project
  *
@@ -20,78 +19,50 @@
 import android.content.Context
 import android.content.pm.PackageManager.FEATURE_CAMERA_CONCURRENT
 import android.graphics.ImageFormat
+import android.graphics.SurfaceTexture
 import android.hardware.camera2.CameraCharacteristics
+import android.hardware.camera2.CameraManager
 import android.hardware.camera2.CameraMetadata
+import android.hardware.camera2.params.StreamConfigurationMap
 import android.media.CamcorderProfile
 import android.media.CamcorderProfile.QUALITY_1080P
 import android.media.CamcorderProfile.QUALITY_2160P
 import android.media.CamcorderProfile.QUALITY_480P
 import android.media.CamcorderProfile.QUALITY_720P
+import android.media.MediaRecorder
 import android.os.Build
-import android.util.Pair
 import android.util.Range
-import android.util.Rational
 import android.util.Size
-import android.view.Surface
 import android.view.WindowManager
-import androidx.annotation.NonNull
 import androidx.camera.camera2.Camera2Config
-import androidx.camera.camera2.internal.SupportedOutputSizesCollectorTest.Companion.createUseCaseByResolutionSelector
-import androidx.camera.camera2.internal.SupportedOutputSizesCollectorTest.Companion.setupCamera
-import androidx.camera.camera2.internal.compat.CameraAccessExceptionCompat
 import androidx.camera.camera2.internal.compat.CameraManagerCompat
-import androidx.camera.core.AspectRatio
 import androidx.camera.core.CameraSelector.LensFacing
-import androidx.camera.core.CameraUnavailableException
 import androidx.camera.core.CameraX
 import androidx.camera.core.CameraXConfig
-import androidx.camera.core.ImageAnalysis
-import androidx.camera.core.ImageCapture
-import androidx.camera.core.Preview
-import androidx.camera.core.SurfaceRequest
 import androidx.camera.core.UseCase
 import androidx.camera.core.impl.AttachedSurfaceInfo
 import androidx.camera.core.impl.CameraDeviceSurfaceManager
-import androidx.camera.core.impl.CameraFactory
-import androidx.camera.core.impl.MutableStateObservable
-import androidx.camera.core.impl.SizeCoordinate
-import androidx.camera.core.impl.StreamSpec
+import androidx.camera.core.impl.ImageFormatConstants.INTERNAL_DEFINED_IMAGE_FORMAT_PRIVATE
 import androidx.camera.core.impl.SurfaceCombination
 import androidx.camera.core.impl.SurfaceConfig
 import androidx.camera.core.impl.SurfaceConfig.ConfigSize
 import androidx.camera.core.impl.SurfaceConfig.ConfigType
 import androidx.camera.core.impl.UseCaseConfig
-import androidx.camera.core.impl.UseCaseConfig.OPTION_TARGET_FRAME_RATE
 import androidx.camera.core.impl.UseCaseConfigFactory
-import androidx.camera.core.impl.utils.AspectRatioUtil.ASPECT_RATIO_4_3
-import androidx.camera.core.impl.utils.AspectRatioUtil.hasMatchingAspectRatio
-import androidx.camera.core.impl.utils.CompareSizesByArea
-import androidx.camera.core.impl.utils.executor.CameraXExecutors
+import androidx.camera.core.impl.UseCaseConfigFactory.CaptureType
 import androidx.camera.core.internal.utils.SizeUtil.RESOLUTION_1440P
 import androidx.camera.core.internal.utils.SizeUtil.RESOLUTION_720P
 import androidx.camera.core.internal.utils.SizeUtil.RESOLUTION_VGA
 import androidx.camera.testing.CameraUtil
 import androidx.camera.testing.CameraXUtil
-import androidx.camera.testing.Configs
 import androidx.camera.testing.EncoderProfilesUtil
-import androidx.camera.testing.SurfaceTextureProvider
-import androidx.camera.testing.SurfaceTextureProvider.SurfaceTextureCallback
 import androidx.camera.testing.fakes.FakeCamera
 import androidx.camera.testing.fakes.FakeCameraFactory
 import androidx.camera.testing.fakes.FakeCameraInfoInternal
 import androidx.camera.testing.fakes.FakeEncoderProfilesProvider
 import androidx.camera.testing.fakes.FakeUseCaseConfig
-import androidx.camera.video.FallbackStrategy
-import androidx.camera.video.MediaSpec
-import androidx.camera.video.Quality
-import androidx.camera.video.QualitySelector
-import androidx.camera.video.VideoCapture
-import androidx.camera.video.VideoOutput
-import androidx.camera.video.VideoOutput.SourceState
-import androidx.camera.video.VideoSpec
 import androidx.test.core.app.ApplicationProvider
 import com.google.common.truth.Truth.assertThat
-import java.util.Arrays
 import java.util.concurrent.ExecutionException
 import java.util.concurrent.TimeUnit
 import org.codehaus.plexus.util.ReflectionUtils
@@ -103,30 +74,22 @@
 import org.mockito.ArgumentMatchers
 import org.mockito.Mockito
 import org.robolectric.RobolectricTestRunner
-import org.robolectric.Shadows
 import org.robolectric.Shadows.shadowOf
 import org.robolectric.annotation.Config
 import org.robolectric.annotation.internal.DoNotInstrument
+import org.robolectric.shadow.api.Shadow
+import org.robolectric.shadows.ShadowCameraCharacteristics
+import org.robolectric.shadows.ShadowCameraManager
 
-private const val FAKE_USE_CASE = 0
-private const val PREVIEW_USE_CASE = 1
-private const val IMAGE_CAPTURE_USE_CASE = 2
-private const val IMAGE_ANALYSIS_USE_CASE = 3
-private const val UNKNOWN_ROTATION = -1
-private const val UNKNOWN_ASPECT_RATIO = -1
 private const val DEFAULT_CAMERA_ID = "0"
 private const val EXTERNAL_CAMERA_ID = "0-external"
-private const val SENSOR_ORIENTATION_0 = 0
 private const val SENSOR_ORIENTATION_90 = 90
-private val ASPECT_RATIO_16_9 = Rational(16, 9)
 private val LANDSCAPE_PIXEL_ARRAY_SIZE = Size(4032, 3024)
-private val PORTRAIT_PIXEL_ARRAY_SIZE = Size(3024, 4032)
 private val DISPLAY_SIZE = Size(720, 1280)
 private val PREVIEW_SIZE = Size(1280, 720)
 private val RECORD_SIZE = Size(3840, 2160)
 private val MAXIMUM_SIZE = Size(4032, 3024)
 private val LEGACY_VIDEO_MAXIMUM_SIZE = Size(1920, 1080)
-private val MOD16_SIZE = Size(960, 544)
 private val DEFAULT_SUPPORTED_SIZES = arrayOf(
     Size(4032, 3024), // 4:3
     Size(3840, 2160), // 16:9
@@ -137,9 +100,6 @@
     Size(960, 544), // a mod16 version of resolution with 16:9 aspect ratio.
     Size(800, 450), // 16:9
     Size(640, 480), // 4:3
-    Size(320, 240), // 4:3
-    Size(320, 180), // 16:9
-    Size(256, 144) // 16:9 For checkSmallSizesAreFilteredOut test.
 )
 
 /** Robolectric test for [SupportedSurfaceCombination] class */
@@ -169,101 +129,15 @@
     private val context = ApplicationProvider.getApplicationContext<Context>()
     private var cameraFactory: FakeCameraFactory? = null
     private var useCaseConfigFactory: UseCaseConfigFactory? = null
-
-    private val legacyUseCaseCreator = object : UseCaseCreator {
-        override fun createUseCase(
-            useCaseType: Int,
-            targetRotation: Int,
-            preferredAspectRatio: Int,
-            preferredResolution: Size?,
-            targetFrameRate: Range<Int>?,
-            surfaceOccupancyPriority: Int,
-            maxResolution: Size?,
-            highResolutionEnabled: Boolean,
-            defaultResolution: Size?,
-            supportedResolutions: List<Pair<Int, Array<Size>>>?,
-            customOrderedResolutions: List<Size>?,
-        ): UseCase {
-            return createUseCaseByLegacyApi(
-                useCaseType,
-                targetRotation,
-                preferredAspectRatio,
-                preferredResolution,
-                targetFrameRate,
-                surfaceOccupancyPriority,
-                maxResolution,
-                defaultResolution,
-                supportedResolutions,
-                customOrderedResolutions,
-            )
-        }
-    }
-
-    private val resolutionSelectorUseCaseCreator = object : UseCaseCreator {
-        override fun createUseCase(
-            useCaseType: Int,
-            targetRotation: Int,
-            preferredAspectRatio: Int,
-            preferredResolution: Size?,
-            targetFrameRate: Range<Int>?,
-            surfaceOccupancyPriority: Int,
-            maxResolution: Size?,
-            highResolutionEnabled: Boolean,
-            defaultResolution: Size?,
-            supportedResolutions: List<Pair<Int, Array<Size>>>?,
-            customOrderedResolutions: List<Size>?,
-        ): UseCase {
-            return createUseCaseByResolutionSelector(
-                useCaseType,
-                preferredAspectRatio,
-                preferredResolution,
-                sizeCoordinate = SizeCoordinate.CAMERA_SENSOR,
-                maxResolution,
-                highResolutionEnabled,
-                highResolutionForceDisabled = false,
-                defaultResolution,
-                supportedResolutions,
-                customOrderedResolutions,
-            )
-        }
-    }
-
-    private val viewSizeResolutionSelectorUseCaseCreator = object : UseCaseCreator {
-        override fun createUseCase(
-            useCaseType: Int,
-            targetRotation: Int,
-            preferredAspectRatio: Int,
-            preferredResolution: Size?,
-            targetFrameRate: Range<Int>?,
-            surfaceOccupancyPriority: Int,
-            maxResolution: Size?,
-            highResolutionEnabled: Boolean,
-            defaultResolution: Size?,
-            supportedResolutions: List<Pair<Int, Array<Size>>>?,
-            customOrderedResolutions: List<Size>?,
-        ): UseCase {
-            return createUseCaseByResolutionSelector(
-                useCaseType,
-                preferredAspectRatio,
-                preferredResolution,
-                sizeCoordinate = SizeCoordinate.ANDROID_VIEW,
-                maxResolution,
-                highResolutionEnabled,
-                highResolutionForceDisabled = false,
-                defaultResolution,
-                supportedResolutions,
-                customOrderedResolutions
-            )
-        }
-    }
+    private lateinit var cameraDeviceSurfaceManager: CameraDeviceSurfaceManager
 
     @Suppress("DEPRECATION") // defaultDisplay
     @Before
     fun setUp() {
         DisplayInfoManager.releaseInstance()
         val windowManager = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
-        Shadows.shadowOf(windowManager.defaultDisplay).setRealWidth(DISPLAY_SIZE.width)
-        Shadows.shadowOf(windowManager.defaultDisplay).setRealHeight(DISPLAY_SIZE.height)
+        shadowOf(windowManager.defaultDisplay).setRealWidth(DISPLAY_SIZE.width)
+        shadowOf(windowManager.defaultDisplay).setRealHeight(DISPLAY_SIZE.height)
         Mockito.`when`(
             mockCamcorderProfileHelper.hasProfile(
                 ArgumentMatchers.anyInt(),
@@ -282,6 +156,12 @@
         CameraXUtil.shutdown()[10000, TimeUnit.MILLISECONDS]
     }
 
+    // //////////////////////////////////////////////////////////////////////////////////////////
+    //
+    // Surface combination support tests for guaranteed configurations
+    //
+    // //////////////////////////////////////////////////////////////////////////////////////////
+
     @Test
     fun checkLegacySurfaceCombinationSupportedInLegacyDevice() {
         setupCameraAndInitCameraX()
@@ -556,731 +436,11 @@
         }
     }
 
-    @Test
-    fun checkTargetAspectRatioInLegacyDevice_LegacyApi() {
-        checkTargetAspectRatioInLegacyDevice(legacyUseCaseCreator)
-    }
-
-    @Test
-    fun checkTargetAspectRatioInLegacyDevice_ResolutionSelector() {
-        checkTargetAspectRatioInLegacyDevice(resolutionSelectorUseCaseCreator)
-    }
-
-    private fun checkTargetAspectRatioInLegacyDevice(useCaseCreator: UseCaseCreator) {
-        setupCameraAndInitCameraX()
-        val supportedSurfaceCombination = SupportedSurfaceCombination(
-            context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
-        )
-        val targetAspectRatio = ASPECT_RATIO_16_9
-        val useCase = useCaseCreator.createUseCase(
-            FAKE_USE_CASE,
-            preferredAspectRatio = AspectRatio.RATIO_16_9
-        )
-        val maxJpegSize = supportedSurfaceCombination.getMaxOutputSizeByFormat(ImageFormat.JPEG)
-        val maxJpegAspectRatio = Rational(maxJpegSize.width, maxJpegSize.height)
-        val suggestedStreamSpecMap = getSuggestedStreamSpecMap(
-            false, supportedSurfaceCombination, useCase)
-        val selectedSize = suggestedStreamSpecMap[useCase]!!.resolution
-        val resultAspectRatio = Rational(selectedSize.width, selectedSize.height)
-        // The targetAspectRatio value will only be set to the same aspect ratio as maximum
-        // supported jpeg size in Legacy + API 21 combination. For other combinations, it should
-        // keep the original targetAspectRatio set for the use case.
-        if (Build.VERSION.SDK_INT == 21) {
-            // Checks targetAspectRatio and maxJpegAspectRatio, which is the ratio of maximum size
-            // in the mSupportedSizes, are not equal to make sure this test case is valid.
-            assertThat(targetAspectRatio).isNotEqualTo(maxJpegAspectRatio)
-            assertThat(resultAspectRatio).isEqualTo(maxJpegAspectRatio)
-        } else {
-            // Checks no correction is needed.
-            assertThat(resultAspectRatio).isEqualTo(targetAspectRatio)
-        }
-    }
-
-    @Test
-    fun checkResolutionForMixedUseCase_AfterBindToLifecycle_InLegacyDevice_LegacyApi() {
-        checkResolutionForMixedUseCase_AfterBindToLifecycle_InLegacyDevice(legacyUseCaseCreator)
-    }
-
-    @Test
-    fun checkResolutionForMixedUseCase_AfterBindToLifecycle_InLegacyDevice_ResolutionSelector() {
-        checkResolutionForMixedUseCase_AfterBindToLifecycle_InLegacyDevice(
-            resolutionSelectorUseCaseCreator
-        )
-    }
-
-    private fun checkResolutionForMixedUseCase_AfterBindToLifecycle_InLegacyDevice(
-        useCaseCreator: UseCaseCreator
-    ) {
-        setupCameraAndInitCameraX()
-        val supportedSurfaceCombination = SupportedSurfaceCombination(
-            context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
-        )
-        // The test case make sure the selected result is expected after the regular flow.
-        val targetAspectRatio = ASPECT_RATIO_16_9
-        val preview = useCaseCreator.createUseCase(
-            PREVIEW_USE_CASE,
-            preferredAspectRatio = AspectRatio.RATIO_16_9
-        ) as Preview
-        preview.setSurfaceProvider(
-            CameraXExecutors.directExecutor(),
-            SurfaceTextureProvider.createSurfaceTextureProvider(
-                Mockito.mock(
-                    SurfaceTextureCallback::class.java
-                )
-            )
-        )
-        val imageCapture = useCaseCreator.createUseCase(
-            IMAGE_CAPTURE_USE_CASE,
-            preferredAspectRatio = AspectRatio.RATIO_16_9
-        )
-        val imageAnalysis = useCaseCreator.createUseCase(
-            IMAGE_ANALYSIS_USE_CASE,
-            preferredAspectRatio = AspectRatio.RATIO_16_9
-        )
-        val maxJpegSize = supportedSurfaceCombination.getMaxOutputSizeByFormat(ImageFormat.JPEG)
-        val maxJpegAspectRatio = Rational(maxJpegSize.width, maxJpegSize.height)
-        val suggestedStreamSpecMap = getSuggestedStreamSpecMap(
-            false,
-            supportedSurfaceCombination,
-            preview,
-            imageCapture, imageAnalysis
-        )
-        val previewResolution = suggestedStreamSpecMap[preview]!!.resolution
-        val imageCaptureResolution = suggestedStreamSpecMap[imageCapture]!!.resolution
-        val imageAnalysisResolution = suggestedStreamSpecMap[imageAnalysis]!!.resolution
-        // The targetAspectRatio value will only be set to the same aspect ratio as maximum
-        // supported jpeg size in Legacy + API 21 combination. For other combinations, it should
-        // keep the original targetAspectRatio set for the use case.
-        if (Build.VERSION.SDK_INT == 21) {
-            // Checks targetAspectRatio and maxJpegAspectRatio, which is the ratio of maximum size
-            // in the mSupportedSizes, are not equal to make sure this test case is valid.
-            assertThat(targetAspectRatio).isNotEqualTo(maxJpegAspectRatio)
-            assertThat(hasMatchingAspectRatio(previewResolution, maxJpegAspectRatio)).isTrue()
-            assertThat(
-                hasMatchingAspectRatio(
-                    imageCaptureResolution,
-                    maxJpegAspectRatio
-                )
-            ).isTrue()
-            assertThat(
-                hasMatchingAspectRatio(
-                    imageAnalysisResolution,
-                    maxJpegAspectRatio
-                )
-            ).isTrue()
-        } else {
-            // Checks no correction is needed.
-            assertThat(
-                hasMatchingAspectRatio(
-                    previewResolution,
-                    targetAspectRatio
-                )
-            ).isTrue()
-            assertThat(
-                hasMatchingAspectRatio(
-                    imageCaptureResolution,
-                    targetAspectRatio
-                )
-            ).isTrue()
-            assertThat(
-                hasMatchingAspectRatio(
-                    imageAnalysisResolution,
-                    targetAspectRatio
-                )
-            ).isTrue()
-        }
-    }
-
-    @Test
-    fun checkDefaultAspectRatioAndResolutionForMixedUseCase_LegacyApi() {
-        checkDefaultAspectRatioAndResolutionForMixedUseCase(legacyUseCaseCreator)
-    }
-
-    @Test
-    fun checkDefaultAspectRatioAndResolutionForMixedUseCase_ResolutionSelector() {
-        checkDefaultAspectRatioAndResolutionForMixedUseCase(resolutionSelectorUseCaseCreator)
-    }
-
-    private fun checkDefaultAspectRatioAndResolutionForMixedUseCase(
-        useCaseCreator: UseCaseCreator
-    ) {
-        setupCameraAndInitCameraX(
-            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
-        )
-        val supportedSurfaceCombination = SupportedSurfaceCombination(
-            context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
-        )
-        val preview = useCaseCreator.createUseCase(PREVIEW_USE_CASE) as Preview
-        preview.setSurfaceProvider(
-            CameraXExecutors.directExecutor(),
-            SurfaceTextureProvider.createSurfaceTextureProvider(
-                Mockito.mock(
-                    SurfaceTextureCallback::class.java
-                )
-            )
-        )
-        val imageCapture = useCaseCreator.createUseCase(IMAGE_CAPTURE_USE_CASE)
-        val imageAnalysis = useCaseCreator.createUseCase(IMAGE_ANALYSIS_USE_CASE)
-
-        // Preview/ImageCapture/ImageAnalysis' default config settings that will be applied after
-        // bound to lifecycle. Calling bindToLifecycle here to make sure sizes matching to
-        // default aspect ratio will be selected.
-        val suggestedStreamSpecMap = getSuggestedStreamSpecMap(
-            false,
-            supportedSurfaceCombination, preview,
-            imageCapture, imageAnalysis
-        )
-        val previewSize = suggestedStreamSpecMap[preview]!!.resolution
-        val imageCaptureSize = suggestedStreamSpecMap[imageCapture]!!.resolution
-        val imageAnalysisSize = suggestedStreamSpecMap[imageAnalysis]!!.resolution
-
-        val previewAspectRatio = Rational(previewSize.width, previewSize.height)
-        val imageCaptureAspectRatio = Rational(imageCaptureSize.width, imageCaptureSize.height)
-        val imageAnalysisAspectRatio = Rational(imageAnalysisSize.width, imageAnalysisSize.height)
-
-        // Checks the default aspect ratio.
-        assertThat(previewAspectRatio).isEqualTo(ASPECT_RATIO_4_3)
-        assertThat(imageCaptureAspectRatio).isEqualTo(ASPECT_RATIO_4_3)
-        assertThat(imageAnalysisAspectRatio).isEqualTo(ASPECT_RATIO_4_3)
-
-        // Checks the default resolution.
-        assertThat(imageAnalysisSize).isEqualTo(RESOLUTION_VGA)
-    }
-
-    @Test
-    fun checkSmallSizesAreFilteredOutByDefaultSize480p() {
-        setupCameraAndInitCameraX(
-            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
-        )
-        val supportedSurfaceCombination = SupportedSurfaceCombination(
-            context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
-        )
-        /* This test case is for b/139018208 that get small resolution 144x256 with below
-        conditions:
-        1. The target aspect ratio is set to the screen size 1080 x 2220 (9:18.5).
-        2. The camera doesn't provide any 9:18.5 resolution and the size 144x256(9:16)
-         is considered the 9:18.5 mod16 version.
-        3. There is no other bigger resolution matched the target aspect ratio.
-        */
-        val displayWidth = 1080
-        val displayHeight = 2220
-        val preview = createUseCaseByLegacyApi(
-            PREVIEW_USE_CASE,
-            targetResolution = Size(displayHeight, displayWidth)
-        )
-        val suggestedStreamSpecMap = getSuggestedStreamSpecMap(
-            false,
-            supportedSurfaceCombination, preview)
-        // Checks the preconditions.
-        val preconditionSize = Size(256, 144)
-        val targetRatio = Rational(displayHeight, displayWidth)
-        assertThat(listOf(*DEFAULT_SUPPORTED_SIZES)).contains(preconditionSize)
-        DEFAULT_SUPPORTED_SIZES.forEach {
-            assertThat(Rational(it.width, it.height)).isNotEqualTo(targetRatio)
-        }
-        // Checks the mechanism has filtered out the sizes which are smaller than default size 480p.
-        val previewSize = suggestedStreamSpecMap[preview]
-        assertThat(previewSize).isNotEqualTo(preconditionSize)
-    }
-
-    @Test
-    fun checkAllSupportedSizesCanBeSelected_LegacyApi() {
-        checkAllSupportedSizesCanBeSelected(legacyUseCaseCreator)
-    }
-
-    @Test
-    fun checkAllSupportedSizesCanBeSelected_ResolutionSelector_SensorSize() {
-        checkAllSupportedSizesCanBeSelected(resolutionSelectorUseCaseCreator)
-    }
-
-    @Test
-    fun checkAllSupportedSizesCanBeSelected_ResolutionSelector_ViewSize() {
-        checkAllSupportedSizesCanBeSelected(viewSizeResolutionSelectorUseCaseCreator)
-    }
-
-    private fun checkAllSupportedSizesCanBeSelected(useCaseCreator: UseCaseCreator) {
-        setupCameraAndInitCameraX(
-            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
-        )
-        val supportedSurfaceCombination = SupportedSurfaceCombination(
-            context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
-        )
-        // Sets each of mSupportedSizes as target resolution and also sets target rotation as
-        // Surface.ROTATION to make it aligns the sensor direction and then exactly the same size
-        // will be selected as the result. This test can also verify that size smaller than
-        // 640x480 can be selected after set as target resolution.
-        DEFAULT_SUPPORTED_SIZES.forEach {
-            val imageCapture = useCaseCreator.createUseCase(
-                IMAGE_CAPTURE_USE_CASE,
-                Surface.ROTATION_90,
-                preferredResolution = it
-            )
-            val suggestedStreamSpecMap =
-                getSuggestedStreamSpecMap(
-                    false, supportedSurfaceCombination, imageCapture)
-            assertThat(it).isEqualTo(suggestedStreamSpecMap[imageCapture]!!.resolution)
-        }
-    }
-
-    @Test
-    fun checkCorrectAspectRatioNotMatchedSizeCanBeSelected_LegacyApi() {
-        // Sets target resolution as 1280x640, all supported resolutions will be put into
-        // aspect ratio not matched list. Then, 1280x720 will be the nearest matched one.
-        // Finally, checks whether 1280x720 is selected or not.
-        checkCorrectAspectRatioNotMatchedSizeCanBeSelected(legacyUseCaseCreator, Size(1280, 720))
-    }
-
-    // 1280x640 is not included in the supported sizes list. So, the smallest size of the
-    // default aspect ratio 4:3 which is 1280x960 will be finally selected.
-    @Test
-    fun checkCorrectAspectRatioNotMatchedSizeCanBeSelected_ResolutionSelector_SensorSize() {
-        checkCorrectAspectRatioNotMatchedSizeCanBeSelected(
-            resolutionSelectorUseCaseCreator,
-            Size(1280, 960)
-        )
-    }
-
-    @Test
-    fun checkCorrectAspectRatioNotMatchedSizeCanBeSelected_ResolutionSelector_ViewSize() {
-        checkCorrectAspectRatioNotMatchedSizeCanBeSelected(
-            viewSizeResolutionSelectorUseCaseCreator,
-            Size(1280, 960)
-        )
-    }
-
-    private fun checkCorrectAspectRatioNotMatchedSizeCanBeSelected(
-        useCaseCreator: UseCaseCreator,
-        expectedResult: Size
-    ) {
-        setupCameraAndInitCameraX(
-            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
-        )
-        val supportedSurfaceCombination = SupportedSurfaceCombination(
-            context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
-        )
-        // Sets target resolution as 1280x640, all supported resolutions will be put into aspect
-        // ratio not matched list. Then, 1280x720 will be the nearest matched one. Finally,
-        // checks whether 1280x720 is selected or not.
-        val resolution = Size(1280, 640)
-        val useCase = useCaseCreator.createUseCase(
-            FAKE_USE_CASE,
-            Surface.ROTATION_90,
-            preferredResolution = resolution
-        )
-        val suggestedStreamSpecMap = getSuggestedStreamSpecMap(
-            false, supportedSurfaceCombination, useCase)
-        assertThat(suggestedStreamSpecMap[useCase]!!.resolution).isEqualTo(expectedResult)
-    }
-
-    @Test
-    fun suggestedStreamSpecsForMixedUseCaseNotSupportedInLegacyDevice_LegacyApi() {
-        suggestedStreamSpecsForMixedUseCaseNotSupportedInLegacyDevice(legacyUseCaseCreator)
-    }
-
-    @Test
-    fun suggestedStreamSpecsForMixedUseCaseNotSupportedInLegacyDevice_ResolutionSelector() {
-        suggestedStreamSpecsForMixedUseCaseNotSupportedInLegacyDevice(
-            resolutionSelectorUseCaseCreator
-        )
-    }
-
-    private fun suggestedStreamSpecsForMixedUseCaseNotSupportedInLegacyDevice(
-        useCaseCreator: UseCaseCreator
-    ) {
-        setupCameraAndInitCameraX(
-            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY
-        )
-        val supportedSurfaceCombination = SupportedSurfaceCombination(
-            context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
-        )
-        val imageCapture = useCaseCreator.createUseCase(
-            IMAGE_CAPTURE_USE_CASE,
-            preferredAspectRatio = AspectRatio.RATIO_16_9
-        )
-        val videoCapture = createVideoCapture()
-        val preview = useCaseCreator.createUseCase(
-            PREVIEW_USE_CASE,
-            preferredAspectRatio = AspectRatio.RATIO_16_9
-        )
-        // An IllegalArgumentException will be thrown because a LEGACY level device can't support
-        // ImageCapture + VideoCapture + Preview
-        assertThrows(IllegalArgumentException::class.java) {
-            getSuggestedStreamSpecMap(
-                false,
-                supportedSurfaceCombination,
-                imageCapture,
-                videoCapture,
-                preview
-            )
-        }
-    }
-
-    @Test
-    fun suggestedStreamSpecsForCustomizeResolutionsNotSupportedInLegacyDevice_LegacyApi() {
-        suggestedStreamSpecsForCustomizeResolutionsNotSupportedInLegacyDevice(legacyUseCaseCreator)
-    }
-
-    @Test
-    fun suggestedStreamSpecsForCustomizeResolutionsNotSupportedInLegacyDevice_ResolutionSelector() {
-        suggestedStreamSpecsForCustomizeResolutionsNotSupportedInLegacyDevice(
-            resolutionSelectorUseCaseCreator
-        )
-    }
-
-    private fun suggestedStreamSpecsForCustomizeResolutionsNotSupportedInLegacyDevice(
-        useCaseCreator: UseCaseCreator
-    ) {
-        setupCameraAndInitCameraX(
-            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY
-        )
-        val supportedSurfaceCombination = SupportedSurfaceCombination(
-            context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
-        )
-        // Legacy camera only support (PRIV, PREVIEW) + (PRIV, PREVIEW)
-        val previewResolutionsPairs = listOf(
-            Pair.create(ImageFormat.PRIVATE, arrayOf(PREVIEW_SIZE))
-        )
-        val videoCapture: VideoCapture<TestVideoOutput> = createVideoCapture(Quality.UHD)
-        val preview = useCaseCreator.createUseCase(
-            PREVIEW_USE_CASE,
-            supportedResolutions = previewResolutionsPairs
-        )
-        // An IllegalArgumentException will be thrown because the VideoCapture requests to only
-        // support a RECORD size but the configuration can't be supported on a LEGACY level device.
-        assertThrows(IllegalArgumentException::class.java) {
-            getSuggestedStreamSpecMap(
-                false, supportedSurfaceCombination, videoCapture, preview)
-        }
-    }
-
-    @Test
-    fun getsuggestedStreamSpecsForMixedUseCaseInLimitedDevice_LegacyApi() {
-        getsuggestedStreamSpecsForMixedUseCaseInLimitedDevice(legacyUseCaseCreator)
-    }
-
-    @Test
-    fun getsuggestedStreamSpecsForMixedUseCaseInLimitedDevice_ResolutionSelector() {
-        getsuggestedStreamSpecsForMixedUseCaseInLimitedDevice(resolutionSelectorUseCaseCreator)
-    }
-
-    private fun getsuggestedStreamSpecsForMixedUseCaseInLimitedDevice(
-        useCaseCreator: UseCaseCreator
-    ) {
-        setupCameraAndInitCameraX(
-            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
-        )
-        val supportedSurfaceCombination = SupportedSurfaceCombination(
-            context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
-        )
-        val imageCapture = useCaseCreator.createUseCase(
-            IMAGE_CAPTURE_USE_CASE,
-            preferredAspectRatio = AspectRatio.RATIO_16_9
-        )
-        val videoCapture = createVideoCapture(Quality.HIGHEST)
-        val preview = useCaseCreator.createUseCase(
-            PREVIEW_USE_CASE,
-            preferredAspectRatio = AspectRatio.RATIO_16_9
-        )
-        val suggestedStreamSpecMap = getSuggestedStreamSpecMap(
-            false,
-            supportedSurfaceCombination,
-            imageCapture,
-            videoCapture,
-            preview
-        )
-        // (PRIV, PREVIEW) + (PRIV, RECORD) + (JPEG, RECORD)
-        assertThat(suggestedStreamSpecMap[imageCapture]!!.resolution).isEqualTo(RECORD_SIZE)
-        assertThat(suggestedStreamSpecMap[videoCapture]!!.resolution).isEqualTo(RECORD_SIZE)
-        assertThat(suggestedStreamSpecMap[preview]!!.resolution).isEqualTo(PREVIEW_SIZE)
-    }
-
-    // For the use case in b/230651237,
-    // QualitySelector.from(Quality.UHD, FallbackStrategy.lowerQualityOrHigherThan(Quality.UHD).
-    // VideoCapture should have higher priority to choose size than ImageCapture.
-    @Test
-    @Throws(CameraUnavailableException::class)
-    fun getsuggestedStreamSpecsInFullDevice_videoHasHigherPriorityThanImage_LegacyApi() {
-        getsuggestedStreamSpecsInFullDevice_videoHasHigherPriorityThanImage(
-            legacyUseCaseCreator)
-    }
-
-    @Test
-    @Throws(CameraUnavailableException::class)
-    fun getsuggestedStreamSpecsInFullDevice_videoHasHigherPriorityThanImage_ResolutionSelector() {
-        getsuggestedStreamSpecsInFullDevice_videoHasHigherPriorityThanImage(
-            resolutionSelectorUseCaseCreator
-        )
-    }
-
-    private fun getsuggestedStreamSpecsInFullDevice_videoHasHigherPriorityThanImage(
-        useCaseCreator: UseCaseCreator
-    ) {
-        setupCameraAndInitCameraX(
-            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL
-        )
-        val supportedSurfaceCombination = SupportedSurfaceCombination(
-            context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
-        )
-        val imageCapture = useCaseCreator.createUseCase(
-            IMAGE_CAPTURE_USE_CASE,
-            preferredAspectRatio = AspectRatio.RATIO_16_9
-        )
-        val videoCapture = createVideoCapture(QualitySelector.from(
-            Quality.UHD,
-            FallbackStrategy.lowerQualityOrHigherThan(Quality.UHD)
-        ))
-        val preview = useCaseCreator.createUseCase(
-            PREVIEW_USE_CASE,
-            preferredAspectRatio = AspectRatio.RATIO_16_9
-        )
-        val suggestedStreamSpecMap = getSuggestedStreamSpecMap(
-            false,
-            supportedSurfaceCombination,
-            imageCapture,
-            videoCapture,
-            preview
-        )
-        // There are two possible combinations in Full level device
-        // (PRIV, PREVIEW) + (PRIV, RECORD) + (JPEG, RECORD) => should be applied
-        // (PRIV, PREVIEW) + (PRIV, PREVIEW) + (JPEG, MAXIMUM)
-        assertThat(suggestedStreamSpecMap[imageCapture]!!.resolution).isEqualTo(RECORD_SIZE)
-        assertThat(suggestedStreamSpecMap[videoCapture]!!.resolution).isEqualTo(RECORD_SIZE)
-        assertThat(suggestedStreamSpecMap[preview]!!.resolution).isEqualTo(PREVIEW_SIZE)
-    }
-
-    @Test
-    fun imageCaptureCanGetMaxSizeInFullDevice_videoRecordSizeLowPriority_LegacyApi() {
-        imageCaptureCanGetMaxSizeInFullDevice_videoRecordSizeLowPriority(
-            legacyUseCaseCreator)
-    }
-
-    @Test
-    fun imageCaptureCanGetMaxSizeInFullDevice_videoRecordSizeLowPriority_ResolutionSelector() {
-        imageCaptureCanGetMaxSizeInFullDevice_videoRecordSizeLowPriority(
-            resolutionSelectorUseCaseCreator
-        )
-    }
-
-    private fun imageCaptureCanGetMaxSizeInFullDevice_videoRecordSizeLowPriority(
-        useCaseCreator: UseCaseCreator
-    ) {
-        setupCameraAndInitCameraX(
-            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL
-        )
-        val supportedSurfaceCombination = SupportedSurfaceCombination(
-            context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
-        )
-        val imageCapture = useCaseCreator.createUseCase(
-            IMAGE_CAPTURE_USE_CASE,
-            preferredAspectRatio = AspectRatio.RATIO_4_3 // mMaximumSize(4032x3024) is 4:3
-        )
-        val videoCapture = createVideoCapture(
-            QualitySelector.fromOrderedList(
-                listOf<Quality>(Quality.HD, Quality.FHD, Quality.UHD)
-            )
-        )
-        val preview = useCaseCreator.createUseCase(
-            PREVIEW_USE_CASE,
-            preferredAspectRatio = AspectRatio.RATIO_16_9
-        )
-        val suggestedStreamSpecMap = getSuggestedStreamSpecMap(
-            false,
-            supportedSurfaceCombination,
-            imageCapture,
-            videoCapture,
-            preview
-        )
-        // There are two possible combinations in Full level device
-        // (PRIV, PREVIEW) + (PRIV, RECORD) + (JPEG, RECORD)
-        // (PRIV, PREVIEW) + (PRIV, PREVIEW) + (JPEG, MAXIMUM) => should be applied
-        assertThat(suggestedStreamSpecMap[imageCapture]!!.resolution).isEqualTo(MAXIMUM_SIZE)
-        // Quality.HD
-        assertThat(suggestedStreamSpecMap[videoCapture]!!.resolution).isEqualTo(PREVIEW_SIZE)
-        assertThat(suggestedStreamSpecMap[preview]!!.resolution).isEqualTo(PREVIEW_SIZE)
-    }
-
-    @Test
-    fun getsuggestedStreamSpecsWithSameSupportedListForDifferentUseCases_LegacyApi() {
-        getsuggestedStreamSpecsWithSameSupportedListForDifferentUseCases(
-            legacyUseCaseCreator,
-            DISPLAY_SIZE
-        )
-    }
-
-    @Test
-    fun getsuggestedStreamSpecsWithSameSupportedListForDifferentUseCases_RS_SensorSize() {
-        getsuggestedStreamSpecsWithSameSupportedListForDifferentUseCases(
-            resolutionSelectorUseCaseCreator,
-            PREVIEW_SIZE
-        )
-    }
-
-    @Test
-    fun getsuggestedStreamSpecsWithSameSupportedListForDifferentUseCases_RS_ViewSize() {
-        getsuggestedStreamSpecsWithSameSupportedListForDifferentUseCases(
-            viewSizeResolutionSelectorUseCaseCreator,
-            PREVIEW_SIZE
-        )
-    }
-
-    private fun getsuggestedStreamSpecsWithSameSupportedListForDifferentUseCases(
-        useCaseCreator: UseCaseCreator,
-        preferredResolution: Size
-    ) {
-        setupCameraAndInitCameraX(
-            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL,
-            supportedSizes = arrayOf(
-                Size(4032, 3024), // 4:3
-                Size(3840, 2160), // 16:9
-                Size(1920, 1440), // 4:3
-                Size(1920, 1080), // 16:9
-                Size(1280, 960), // 4:3
-                Size(1280, 720), // 16:9
-                Size(1280, 720), // duplicate the size since Nexus 5X emulator has the case.
-                Size(960, 544), // a mod16 version of resolution with 16:9 aspect ratio.
-                Size(800, 450), // 16:9
-                Size(640, 480), // 4:3
-                Size(320, 240), // 4:3
-                Size(320, 180), // 16:9
-                Size(256, 144) // 16:9 For checkSmallSizesAreFilteredOut test.
-            )
-        )
-        val supportedSurfaceCombination = SupportedSurfaceCombination(
-            context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
-        )
-        /* This test case is for b/132603284 that divide by zero issue crash happened in below
-        conditions:
-        1. There are duplicated two 1280x720 supported sizes for ImageCapture and Preview.
-        2. supportedOutputSizes for ImageCapture and Preview in
-        SupportedSurfaceCombination#getAllPossibleSizeArrangements are the same.
-        */
-        val imageCapture = useCaseCreator.createUseCase(
-            IMAGE_CAPTURE_USE_CASE,
-            preferredResolution = preferredResolution
-        )
-        val preview = useCaseCreator.createUseCase(
-            PREVIEW_USE_CASE,
-            preferredResolution = preferredResolution
-        )
-        val imageAnalysis = useCaseCreator.createUseCase(
-            IMAGE_ANALYSIS_USE_CASE,
-            preferredResolution = preferredResolution
-        )
-
-        val suggestedStreamSpecMap = getSuggestedStreamSpecMap(
-            false,
-            supportedSurfaceCombination,
-            imageCapture,
-            imageAnalysis,
-            preview
-        )
-        assertThat(suggestedStreamSpecMap[imageCapture]!!.resolution).isEqualTo(PREVIEW_SIZE)
-        assertThat(suggestedStreamSpecMap[imageAnalysis]!!.resolution).isEqualTo(PREVIEW_SIZE)
-        assertThat(suggestedStreamSpecMap[preview]!!.resolution).isEqualTo(PREVIEW_SIZE)
-    }
-
-    @Test
-    fun setTargetAspectRatioForMixedUseCases_LegacyApi() {
-        setTargetAspectRatioForMixedUseCases(legacyUseCaseCreator)
-    }
-
-    @Test
-    fun setTargetAspectRatioForMixedUseCases_ResolutionSelector() {
-        setTargetAspectRatioForMixedUseCases(resolutionSelectorUseCaseCreator)
-    }
-
-    private fun setTargetAspectRatioForMixedUseCases(useCaseCreator: UseCaseCreator) {
-        setupCameraAndInitCameraX(
-            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL
-        )
-        val supportedSurfaceCombination = SupportedSurfaceCombination(
-            context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
-        )
-        val preview = useCaseCreator.createUseCase(
-            PREVIEW_USE_CASE,
-            preferredAspectRatio = AspectRatio.RATIO_16_9
-        )
-        val imageCapture = useCaseCreator.createUseCase(
-            IMAGE_CAPTURE_USE_CASE,
-            preferredAspectRatio = AspectRatio.RATIO_16_9
-        )
-        val imageAnalysis = useCaseCreator.createUseCase(
-            IMAGE_ANALYSIS_USE_CASE,
-            preferredAspectRatio = AspectRatio.RATIO_16_9
-        )
-        val suggestedStreamSpecMap = getSuggestedStreamSpecMap(
-            false,
-            supportedSurfaceCombination,
-            preview,
-            imageCapture,
-            imageAnalysis
-        )
-        assertThat(
-            hasMatchingAspectRatio(
-                suggestedStreamSpecMap[preview]!!.resolution,
-                ASPECT_RATIO_16_9
-            )
-        ).isTrue()
-        assertThat(
-            hasMatchingAspectRatio(
-                suggestedStreamSpecMap[imageCapture]!!.resolution,
-                ASPECT_RATIO_16_9
-            )
-        ).isTrue()
-        assertThat(
-            hasMatchingAspectRatio(
-                suggestedStreamSpecMap[imageAnalysis]!!.resolution,
-                ASPECT_RATIO_16_9
-            )
-        ).isTrue()
-    }
-
-    @Test
-    fun getsuggestedStreamSpecsForCustomizedSupportedResolutions_LegacyApi() {
-        getsuggestedStreamSpecsForCustomizedSupportedResolutions(legacyUseCaseCreator)
-    }
-
-    @Test
-    fun getsuggestedStreamSpecsForCustomizedSupportedResolutions_ResolutionSelector() {
-        getsuggestedStreamSpecsForCustomizedSupportedResolutions(resolutionSelectorUseCaseCreator)
-    }
-
-    private fun getsuggestedStreamSpecsForCustomizedSupportedResolutions(
-        useCaseCreator: UseCaseCreator
-    ) {
-        setupCameraAndInitCameraX(
-            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
-        )
-        val supportedSurfaceCombination = SupportedSurfaceCombination(
-            context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
-        )
-        val formatResolutionsPairList = arrayListOf<Pair<Int, Array<Size>>>().apply {
-            add(Pair.create(ImageFormat.JPEG, arrayOf(RESOLUTION_VGA)))
-            add(Pair.create(ImageFormat.YUV_420_888, arrayOf(RESOLUTION_VGA)))
-            add(Pair.create(ImageFormat.PRIVATE, arrayOf(RESOLUTION_VGA)))
-        }
-        // Sets use cases customized supported resolutions to 640x480 only.
-        val imageCapture = useCaseCreator.createUseCase(
-            IMAGE_CAPTURE_USE_CASE,
-            supportedResolutions = formatResolutionsPairList
-        )
-        val videoCapture = createVideoCapture(Quality.SD)
-        val preview = useCaseCreator.createUseCase(
-            PREVIEW_USE_CASE,
-            supportedResolutions = formatResolutionsPairList
-        )
-        val suggestedStreamSpecMap = getSuggestedStreamSpecMap(
-            false,
-            supportedSurfaceCombination,
-            imageCapture,
-            videoCapture,
-            preview
-        )
-        // Checks all resolutions in suggested stream specs will become 640x480.
-        assertThat(suggestedStreamSpecMap[imageCapture]?.resolution).isEqualTo(RESOLUTION_VGA)
-        assertThat(suggestedStreamSpecMap[videoCapture]?.resolution).isEqualTo(RESOLUTION_VGA)
-        assertThat(suggestedStreamSpecMap[preview]?.resolution).isEqualTo(RESOLUTION_VGA)
-    }
+    // //////////////////////////////////////////////////////////////////////////////////////////
+    //
+    // Surface config transformation tests
+    //
+    // //////////////////////////////////////////////////////////////////////////////////////////
 
     @Test
     fun transformSurfaceConfigWithYUVAnalysisSize() {
@@ -1484,6 +644,947 @@
         assertThat(surfaceConfig).isEqualTo(expectedSurfaceConfig)
     }
 
+    // //////////////////////////////////////////////////////////////////////////////////////////
+    //
+    // Resolution selection tests for LEGACY-level guaranteed configurations
+    //
+    // //////////////////////////////////////////////////////////////////////////////////////////
+
+    /**
+     * PRIV/MAXIMUM
+     */
+    @Test
+    fun canSelectCorrectSize_singlePrivStream_inLegacyDevice() {
+        val privUseCase = createUseCase(CaptureType.VIDEO_CAPTURE)
+        val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+            put(privUseCase, MAXIMUM_SIZE)
+        }
+        getSuggestedSpecsAndVerify(useCaseExpectedResultMap)
+    }
+
+    /**
+     * JPEG/MAXIMUM
+     */
+    @Test
+    fun canSelectCorrectSize_singleJpegStream_inLegacyDevice() {
+        val jpegUseCase = createUseCase(CaptureType.IMAGE_CAPTURE)
+        val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+            put(jpegUseCase, MAXIMUM_SIZE)
+        }
+        getSuggestedSpecsAndVerify(useCaseExpectedResultMap)
+    }
+
+    /**
+     * YUV/MAXIMUM
+     */
+    @Test
+    fun canSelectCorrectSize_singleYuvStream_inLegacyDevice() {
+        val yuvUseCase = createUseCase(CaptureType.IMAGE_ANALYSIS)
+        val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+            put(yuvUseCase, MAXIMUM_SIZE)
+        }
+        getSuggestedSpecsAndVerify(useCaseExpectedResultMap)
+    }
+
+    /**
+     * PRIV/PREVIEW + JPEG/MAXIMUM
+     */
+    @Test
+    fun canSelectCorrectSizes_privPlusJpeg_inLegacyDevice() {
+        val privUseCase = createUseCase(CaptureType.PREVIEW)
+        val jpegUseCase = createUseCase(CaptureType.IMAGE_CAPTURE)
+        val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+            put(privUseCase, PREVIEW_SIZE)
+            put(jpegUseCase, MAXIMUM_SIZE)
+        }
+        getSuggestedSpecsAndVerify(useCaseExpectedResultMap)
+    }
+
+    /**
+     * YUV/PREVIEW + JPEG/MAXIMUM
+     */
+    @Test
+    fun canSelectCorrectSizes_yuvPlusJpeg_inLegacyDevice() {
+        val yuvUseCase = createUseCase(CaptureType.IMAGE_ANALYSIS) // YUV
+        val jpegUseCase = createUseCase(CaptureType.IMAGE_CAPTURE) // JPEG
+        val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+            put(yuvUseCase, PREVIEW_SIZE)
+            put(jpegUseCase, MAXIMUM_SIZE)
+        }
+        getSuggestedSpecsAndVerify(useCaseExpectedResultMap)
+    }
+
+    /**
+     * PRIV/PREVIEW + PRIV/PREVIEW
+     */
+    @Test
+    fun canSelectCorrectSizes_privPlusPriv_inLegacyDevice() {
+        val privUseCase1 = createUseCase(CaptureType.PREVIEW) // PRIV
+        val privUseCase2 = createUseCase(CaptureType.VIDEO_CAPTURE) // PRIV
+        val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+            put(privUseCase1, PREVIEW_SIZE)
+            put(privUseCase2, PREVIEW_SIZE)
+        }
+        getSuggestedSpecsAndVerify(useCaseExpectedResultMap)
+    }
+
+    /**
+     * PRIV/PREVIEW + YUV/PREVIEW
+     */
+    @Test
+    fun canSelectCorrectSizes_privPlusYuv_inLegacyDevice() {
+        val privUseCase = createUseCase(CaptureType.PREVIEW) // PRIV
+        val yuvUseCase = createUseCase(CaptureType.IMAGE_ANALYSIS) // YUV
+        val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+            put(privUseCase, PREVIEW_SIZE)
+            put(yuvUseCase, PREVIEW_SIZE)
+        }
+        getSuggestedSpecsAndVerify(useCaseExpectedResultMap)
+    }
+
+    /**
+     * PRIV/PREVIEW + YUV/PREVIEW + JPEG/MAXIMUM
+     */
+    @Test
+    fun canSelectCorrectSizes_privPlusYuvPlusJpeg_inLegacyDevice() {
+        val privUseCase = createUseCase(CaptureType.PREVIEW) // PRIV
+        val yuvUseCase = createUseCase(CaptureType.IMAGE_ANALYSIS) // YUV
+        val jpegUseCase = createUseCase(CaptureType.IMAGE_CAPTURE) // JPEG
+        val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+            put(privUseCase, PREVIEW_SIZE)
+            put(yuvUseCase, PREVIEW_SIZE)
+            put(jpegUseCase, MAXIMUM_SIZE)
+        }
+        getSuggestedSpecsAndVerify(useCaseExpectedResultMap)
+    }
+
+    /**
+     * Unsupported PRIV + JPEG + PRIV for legacy level devices
+     */
+    @Test
+    fun throwsException_unsupportedConfiguration_inLegacyDevice() {
+        val privUseCase1 = createUseCase(CaptureType.PREVIEW) // PRIV
+        val jpegUseCase = createUseCase(CaptureType.IMAGE_CAPTURE) // JPEG
+        val privUseCas2 = createUseCase(CaptureType.VIDEO_CAPTURE) // PRIV
+        val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+            put(privUseCase1, RESOLUTION_VGA)
+            put(jpegUseCase, RESOLUTION_VGA)
+            put(privUseCas2, RESOLUTION_VGA)
+        }
+        assertThrows(IllegalArgumentException::class.java) {
+            getSuggestedSpecsAndVerify(useCaseExpectedResultMap)
+        }
+    }
+
+    // //////////////////////////////////////////////////////////////////////////////////////////
+    //
+    // Resolution selection tests for LIMITED-level guaranteed configurations
+    //
+    // //////////////////////////////////////////////////////////////////////////////////////////
+
+    /**
+     * PRIV/PREVIEW + PRIV/RECORD
+     */
+    @Test
+    fun canSelectCorrectSizes_privPlusPriv_inLimitedDevice() {
+        val privUseCase1 = createUseCase(CaptureType.VIDEO_CAPTURE) // PRIV
+        val privUseCas2 = createUseCase(CaptureType.PREVIEW) // PRIV
+        val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+            put(privUseCase1, RECORD_SIZE)
+            put(privUseCas2, PREVIEW_SIZE)
+        }
+        getSuggestedSpecsAndVerify(
+            useCaseExpectedResultMap,
+            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
+        )
+    }
+
+    /**
+     * PRIV/PREVIEW + YUV/RECORD
+     */
+    @Test
+    fun canSelectCorrectSizes_privPlusYuv_inLimitedDevice() {
+        val privUseCase = createUseCase(CaptureType.PREVIEW) // PRIV
+        val yuvUseCase = createUseCase(CaptureType.IMAGE_ANALYSIS) // YUV
+        val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+            put(privUseCase, PREVIEW_SIZE)
+            put(yuvUseCase, RECORD_SIZE)
+        }
+        getSuggestedSpecsAndVerify(
+            useCaseExpectedResultMap,
+            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
+        )
+    }
+
+    /**
+     * YUV/PREVIEW + YUV/RECORD
+     */
+    @Test
+    fun canSelectCorrectSizes_yuvPlusYuv_inLimitedDevice() {
+        val yuvUseCase1 = createUseCase(CaptureType.IMAGE_ANALYSIS) // YUV
+        val yuvUseCase2 = createUseCase(CaptureType.IMAGE_ANALYSIS) // YUV
+        val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+            put(yuvUseCase1, RECORD_SIZE)
+            put(yuvUseCase2, PREVIEW_SIZE)
+        }
+        getSuggestedSpecsAndVerify(
+            useCaseExpectedResultMap,
+            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
+        )
+    }
+
+    /**
+     * PRIV/PREVIEW + PRIV/RECORD + JPEG/RECORD
+     */
+    @Test
+    fun canSelectCorrectSizes_privPlusPrivPlusJpeg_inLimitedDevice() {
+        val privUseCase1 = createUseCase(CaptureType.VIDEO_CAPTURE) // PRIV
+        val privUseCase2 = createUseCase(CaptureType.PREVIEW) // PRIV
+        val jpegUseCase = createUseCase(CaptureType.IMAGE_CAPTURE) // JPEG
+        val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+            put(privUseCase1, RECORD_SIZE)
+            put(privUseCase2, PREVIEW_SIZE)
+            put(jpegUseCase, RECORD_SIZE)
+        }
+        getSuggestedSpecsAndVerify(
+            useCaseExpectedResultMap,
+            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
+        )
+    }
+
+    /**
+     * PRIV/PREVIEW + YUV/RECORD + JPEG/RECORD
+     */
+    @Test
+    fun canSelectCorrectSizes_privPlusYuvPlusJpeg_inLimitedDevice() {
+        val privUseCase = createUseCase(CaptureType.PREVIEW) // PRIV
+        val yuvUseCase = createUseCase(CaptureType.IMAGE_ANALYSIS) // YUV
+        val jpegUseCase = createUseCase(CaptureType.IMAGE_CAPTURE) // JPEG
+        val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+            put(privUseCase, PREVIEW_SIZE)
+            put(yuvUseCase, RECORD_SIZE)
+            put(jpegUseCase, RECORD_SIZE)
+        }
+        getSuggestedSpecsAndVerify(
+            useCaseExpectedResultMap,
+            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
+        )
+    }
+
+    /**
+     * YUV/PREVIEW + YUV/PREVIEW + JPEG/MAXIMUM
+     */
+    @Test
+    fun canSelectCorrectSizes_yuvPlusYuvPlusJpeg_inLimitedDevice() {
+        val yuvUseCase1 = createUseCase(CaptureType.IMAGE_ANALYSIS) // YUV
+        val yuvUseCase2 = createUseCase(CaptureType.IMAGE_ANALYSIS) // YUV
+        val jpegUseCase = createUseCase(CaptureType.IMAGE_CAPTURE) // JPEG
+        val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+            put(yuvUseCase1, PREVIEW_SIZE)
+            put(yuvUseCase2, PREVIEW_SIZE)
+            put(jpegUseCase, MAXIMUM_SIZE)
+        }
+        getSuggestedSpecsAndVerify(
+            useCaseExpectedResultMap,
+            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
+        )
+    }
+
+    /**
+     * Unsupported YUV + PRIV + YUV for limited level devices
+     */
+    @Test
+    fun throwsException_unsupportedConfiguration_inLimitedDevice() {
+        val yuvUseCase1 = createUseCase(CaptureType.IMAGE_ANALYSIS) // YUV
+        val privUseCase = createUseCase(CaptureType.PREVIEW) // PRIV
+        val yuvUseCase2 = createUseCase(CaptureType.IMAGE_ANALYSIS) // YUV
+        val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+            put(yuvUseCase1, RESOLUTION_VGA)
+            put(privUseCase, RESOLUTION_VGA)
+            put(yuvUseCase2, RESOLUTION_VGA)
+        }
+        assertThrows(IllegalArgumentException::class.java) {
+            getSuggestedSpecsAndVerify(
+                useCaseExpectedResultMap,
+                hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
+            )
+        }
+    }
+
+    // //////////////////////////////////////////////////////////////////////////////////////////
+    //
+    // Resolution selection tests for FULL-level guaranteed configurations
+    //
+    // //////////////////////////////////////////////////////////////////////////////////////////
+
+    /**
+     * PRIV/PREVIEW + PRIV/MAXIMUM
+     */
+    @Test
+    fun canSelectCorrectSizes_privPlusPriv_inFullDevice() {
+        val privUseCase1 = createUseCase(CaptureType.VIDEO_CAPTURE) // PRIV
+        val privUseCase2 = createUseCase(CaptureType.PREVIEW) // PRIV
+        val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+            put(privUseCase1, MAXIMUM_SIZE)
+            put(privUseCase2, PREVIEW_SIZE)
+        }
+        getSuggestedSpecsAndVerify(
+            useCaseExpectedResultMap,
+            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL
+        )
+    }
+
+    /**
+     * PRIV/PREVIEW + YUV/MAXIMUM
+     */
+    @Test
+    fun canSelectCorrectSizes_privPlusYuv_inFullDevice() {
+        val privUseCase = createUseCase(CaptureType.PREVIEW) // PRIV
+        val yuvUseCase = createUseCase(CaptureType.IMAGE_ANALYSIS) // YUV
+        val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+            put(privUseCase, PREVIEW_SIZE)
+            put(yuvUseCase, MAXIMUM_SIZE)
+        }
+        getSuggestedSpecsAndVerify(
+            useCaseExpectedResultMap,
+            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL
+        )
+    }
+
+    /**
+     * YUV/PREVIEW + YUV/MAXIMUM
+     */
+    @Test
+    fun canSelectCorrectSizes_yuvPlusYuv_inFullDevice() {
+        val yuvUseCase1 = createUseCase(CaptureType.IMAGE_ANALYSIS) // YUV
+        val yuvUseCase2 = createUseCase(CaptureType.IMAGE_ANALYSIS) // YUV
+        val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+            put(yuvUseCase1, MAXIMUM_SIZE)
+            put(yuvUseCase2, PREVIEW_SIZE)
+        }
+        getSuggestedSpecsAndVerify(
+            useCaseExpectedResultMap,
+            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL
+        )
+    }
+
+    /**
+     * PRIV/PREVIEW + PRIV/PREVIEW + JPEG/MAXIMUM
+     */
+    @Test
+    fun canSelectCorrectSizes_privPlusPrivPlusJpeg_inFullDevice() {
+        val jpegUseCase = createUseCase(CaptureType.IMAGE_CAPTURE) // JPEG
+        val privUseCase1 = createUseCase(CaptureType.VIDEO_CAPTURE) // PRIV
+        val privUseCase2 = createUseCase(CaptureType.PREVIEW) // PRIV
+        val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+            put(jpegUseCase, MAXIMUM_SIZE)
+            put(privUseCase1, PREVIEW_SIZE)
+            put(privUseCase2, PREVIEW_SIZE)
+        }
+        getSuggestedSpecsAndVerify(
+            useCaseExpectedResultMap,
+            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL
+        )
+    }
+
+    /**
+     * YUV/VGA + PRIV/PREVIEW + YUV/MAXIMUM
+     */
+    @Test
+    fun canSelectCorrectSizes_yuvPlusPrivPlusYuv_inFullDevice() {
+        val privUseCase = createUseCase(CaptureType.PREVIEW) // PRIV
+        val yuvUseCase1 = createUseCase(CaptureType.IMAGE_ANALYSIS) // YUV
+        val yuvUseCase2 = createUseCase(CaptureType.IMAGE_ANALYSIS) // YUV
+        val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+            put(privUseCase, PREVIEW_SIZE)
+            put(yuvUseCase1, MAXIMUM_SIZE)
+            put(yuvUseCase2, RESOLUTION_VGA)
+        }
+        getSuggestedSpecsAndVerify(
+            useCaseExpectedResultMap,
+            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL
+        )
+    }
+
+    /**
+     * YUV/VGA + YUV/PREVIEW + YUV/MAXIMUM
+     */
+    @Test
+    fun canSelectCorrectSizes_yuvPlusYuvPlusYuv_inFullDevice() {
+        val yuvUseCase1 = createUseCase(CaptureType.IMAGE_ANALYSIS) // YUV
+        val yuvUseCase2 = createUseCase(CaptureType.IMAGE_ANALYSIS) // YUV
+        val yuvUseCase3 = createUseCase(CaptureType.IMAGE_ANALYSIS) // YUV
+        val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+            put(yuvUseCase1, MAXIMUM_SIZE)
+            put(yuvUseCase2, PREVIEW_SIZE)
+            put(yuvUseCase3, RESOLUTION_VGA)
+        }
+        getSuggestedSpecsAndVerify(
+            useCaseExpectedResultMap,
+            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL
+        )
+    }
+
+    /**
+     * Unsupported PRIV + PRIV + YUV + RAW for full level devices
+     */
+    @Test
+    fun throwsException_unsupportedConfiguration_inFullDevice() {
+        val privUseCase1 = createUseCase(CaptureType.VIDEO_CAPTURE) // PRIV
+        val privUseCase2 = createUseCase(CaptureType.PREVIEW) // PRIV
+        val yuvUseCase = createUseCase(CaptureType.IMAGE_ANALYSIS) // YUV
+        val rawUseCase = createRawUseCase() // RAW
+        val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+            put(privUseCase1, RESOLUTION_VGA)
+            put(privUseCase2, RESOLUTION_VGA)
+            put(yuvUseCase, RESOLUTION_VGA)
+            put(rawUseCase, RESOLUTION_VGA)
+        }
+        assertThrows(IllegalArgumentException::class.java) {
+            getSuggestedSpecsAndVerify(
+                useCaseExpectedResultMap,
+                hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
+            )
+        }
+    }
+
+    // //////////////////////////////////////////////////////////////////////////////////////////
+    //
+    // Resolution selection tests for Level-3 guaranteed configurations
+    //
+    // //////////////////////////////////////////////////////////////////////////////////////////
+
+    /**
+     * PRIV/PREVIEW + PRIV/VGA + YUV/MAXIMUM + RAW/MAXIMUM
+     */
+    @Test
+    fun canSelectCorrectSizes_privPlusPrivPlusYuvPlusRaw_inLevel3Device() {
+        val privUseCase1 = createUseCase(CaptureType.VIDEO_CAPTURE) // PRIV
+        val privUseCase2 = createUseCase(CaptureType.PREVIEW) // PRIV
+        val yuvUseCase = createUseCase(CaptureType.IMAGE_ANALYSIS) // YUV
+        val rawUseCase = createRawUseCase() // RAW
+        val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+            put(privUseCase1, PREVIEW_SIZE)
+            put(privUseCase2, RESOLUTION_VGA)
+            put(yuvUseCase, MAXIMUM_SIZE)
+            put(rawUseCase, MAXIMUM_SIZE)
+        }
+        getSuggestedSpecsAndVerify(
+            useCaseExpectedResultMap,
+            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_3
+        )
+    }
+
+    /**
+     * PRIV/PREVIEW + PRIV/VGA + JPEG/MAXIMUM + RAW/MAXIMUM
+     */
+    @Test
+    fun canSelectCorrectSizes_privPlusPrivPlusJpegPlusRaw_inLevel3Device() {
+        val privUseCase1 = createUseCase(CaptureType.VIDEO_CAPTURE) // PRIV
+        val privUseCase2 = createUseCase(CaptureType.PREVIEW) // PRIV
+        val jpegUseCase = createUseCase(CaptureType.IMAGE_CAPTURE) // JPEG
+        val rawUseCase = createRawUseCase() // RAW
+        val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+            put(privUseCase1, PREVIEW_SIZE)
+            put(privUseCase2, RESOLUTION_VGA)
+            put(jpegUseCase, MAXIMUM_SIZE)
+            put(rawUseCase, MAXIMUM_SIZE)
+        }
+        getSuggestedSpecsAndVerify(
+            useCaseExpectedResultMap,
+            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_3
+        )
+    }
+
+    /**
+     * Unsupported PRIV + YUV + YUV + RAW for level-3 devices
+     */
+    @Test
+    fun throwsException_unsupportedConfiguration_inLevel3Device() {
+        val privUseCase = createUseCase(CaptureType.PREVIEW) // PRIV
+        val yuvUseCase1 = createUseCase(CaptureType.IMAGE_ANALYSIS) // YUV
+        val yuvUseCase2 = createUseCase(CaptureType.IMAGE_ANALYSIS) // YUV
+        val rawUseCase = createRawUseCase() // RAW
+        val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+            put(privUseCase, RESOLUTION_VGA)
+            put(yuvUseCase1, RESOLUTION_VGA)
+            put(yuvUseCase2, RESOLUTION_VGA)
+            put(rawUseCase, RESOLUTION_VGA)
+        }
+        assertThrows(IllegalArgumentException::class.java) {
+            getSuggestedSpecsAndVerify(
+                useCaseExpectedResultMap,
+                hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_3
+            )
+        }
+    }
+
+    // //////////////////////////////////////////////////////////////////////////////////////////
+    //
+    // Resolution selection tests for Burst-capability guaranteed configurations
+    //
+    // //////////////////////////////////////////////////////////////////////////////////////////
+
+    /**
+     * PRIV/PREVIEW + PRIV/MAXIMUM
+     */
+    @Test
+    fun canSelectCorrectSizes_privPlusPriv_inLimitedDevice_withBurstCapability() {
+        val privUseCase1 = createUseCase(CaptureType.VIDEO_CAPTURE) // PRIV
+        val privUseCase2 = createUseCase(CaptureType.PREVIEW) // PRIV
+        val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+            put(privUseCase1, MAXIMUM_SIZE)
+            put(privUseCase2, PREVIEW_SIZE)
+        }
+        getSuggestedSpecsAndVerify(
+            useCaseExpectedResultMap,
+            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
+            capabilities = intArrayOf(
+                CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE
+            )
+        )
+    }
+
+    /**
+     * PRIV/PREVIEW + YUV/MAXIMUM
+     */
+    @Test
+    fun canSelectCorrectSizes_privPlusYuv_inLimitedDevice_withBurstCapability() {
+        val privUseCase = createUseCase(CaptureType.PREVIEW) // PRIV
+        val yuvUseCase = createUseCase(CaptureType.IMAGE_ANALYSIS) // YUV
+        val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+            put(privUseCase, PREVIEW_SIZE)
+            put(yuvUseCase, MAXIMUM_SIZE)
+        }
+        getSuggestedSpecsAndVerify(
+            useCaseExpectedResultMap,
+            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
+            capabilities = intArrayOf(
+                CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE
+            )
+        )
+    }
+
+    /**
+     * YUV/PREVIEW + YUV/MAXIMUM
+     */
+    @Test
+    fun canSelectCorrectSizes_yuvPlusYuv_inLimitedDevice_withBurstCapability() {
+        val yuvUseCase1 = createUseCase(CaptureType.IMAGE_ANALYSIS) // YUV
+        val yuvUseCase2 = createUseCase(CaptureType.IMAGE_ANALYSIS) // YUV
+        val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+            put(yuvUseCase1, MAXIMUM_SIZE)
+            put(yuvUseCase2, PREVIEW_SIZE)
+        }
+        getSuggestedSpecsAndVerify(
+            useCaseExpectedResultMap,
+            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
+            capabilities = intArrayOf(
+                CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE
+            )
+        )
+    }
+
+    // //////////////////////////////////////////////////////////////////////////////////////////
+    //
+    // Resolution selection tests for RAW-capability guaranteed configurations
+    //
+    // //////////////////////////////////////////////////////////////////////////////////////////
+
+    /**
+     * RAW/MAX
+     */
+    @Test
+    fun canSelectCorrectSizes_singleRawStream_inLimitedDevice_withRawCapability() {
+        val rawUseCase = createRawUseCase() // RAW
+        val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+            put(rawUseCase, MAXIMUM_SIZE)
+        }
+        getSuggestedSpecsAndVerify(
+            useCaseExpectedResultMap,
+            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
+            capabilities = intArrayOf(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW)
+        )
+    }
+
+    /**
+     * PRIV/PREVIEW + RAW/MAXIMUM
+     */
+    @Test
+    fun canSelectCorrectSizes_privPlusRAW_inLimitedDevice_withRawCapability() {
+        val privUseCase = createUseCase(CaptureType.PREVIEW) // PRIV
+        val rawUseCase = createRawUseCase() // RAW
+        val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+            put(privUseCase, PREVIEW_SIZE)
+            put(rawUseCase, MAXIMUM_SIZE)
+        }
+        getSuggestedSpecsAndVerify(
+            useCaseExpectedResultMap,
+            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
+            capabilities = intArrayOf(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW)
+        )
+    }
+
+    /**
+     * PRIV/PREVIEW + PRIV/PREVIEW + RAW/MAXIMUM
+     */
+    @Test
+    fun canSelectCorrectSizes_privPlusPrivPlusRAW_inLimitedDevice_withRawCapability() {
+        val privUseCase1 = createUseCase(CaptureType.VIDEO_CAPTURE) // PRIV
+        val privUseCase2 = createUseCase(CaptureType.PREVIEW) // PRIV
+        val rawUseCase = createRawUseCase() // RAW
+        val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+            put(privUseCase1, PREVIEW_SIZE)
+            put(privUseCase2, PREVIEW_SIZE)
+            put(rawUseCase, MAXIMUM_SIZE)
+        }
+        getSuggestedSpecsAndVerify(
+            useCaseExpectedResultMap,
+            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
+            capabilities = intArrayOf(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW)
+        )
+    }
+
+    /**
+     * PRIV/PREVIEW + YUV/PREVIEW + RAW/MAXIMUM
+     */
+    @Test
+    fun canSelectCorrectSizes_privPlusYuvPlusRAW_inLimitedDevice_withRawCapability() {
+        val privUseCase = createUseCase(CaptureType.PREVIEW) // PRIV
+        val yuvUseCase = createUseCase(CaptureType.IMAGE_ANALYSIS) // YUV
+        val rawUseCase = createRawUseCase() // RAW
+        val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+            put(privUseCase, PREVIEW_SIZE)
+            put(yuvUseCase, PREVIEW_SIZE)
+            put(rawUseCase, MAXIMUM_SIZE)
+        }
+        getSuggestedSpecsAndVerify(
+            useCaseExpectedResultMap,
+            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
+            capabilities = intArrayOf(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW)
+        )
+    }
+
+    /**
+     * YUV/PREVIEW + YUV/PREVIEW + RAW/MAXIMUM
+     */
+    @Test
+    fun canSelectCorrectSizes_yuvPlusYuvPlusRAW_inLimitedDevice_withRawCapability() {
+        val yuvUseCase1 = createUseCase(CaptureType.IMAGE_ANALYSIS) // YUV
+        val yuvUseCase2 = createUseCase(CaptureType.IMAGE_ANALYSIS) // YUV
+        val rawUseCase = createRawUseCase() // RAW
+        val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+            put(yuvUseCase1, PREVIEW_SIZE)
+            put(yuvUseCase2, PREVIEW_SIZE)
+            put(rawUseCase, MAXIMUM_SIZE)
+        }
+        getSuggestedSpecsAndVerify(
+            useCaseExpectedResultMap,
+            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
+            capabilities = intArrayOf(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW)
+        )
+    }
+
+    /**
+     * PRIV/PREVIEW + JPEG/MAXIMUM + RAW/MAXIMUM
+     */
+    @Test
+    fun canSelectCorrectSizes_privPlusJpegPlusRAW_inLimitedDevice_withRawCapability() {
+        val privUseCase = createUseCase(CaptureType.PREVIEW) // PRIV
+        val jpegUseCase = createUseCase(CaptureType.IMAGE_CAPTURE) // JPEG
+        val rawUseCase = createRawUseCase() // RAW
+        val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+            put(privUseCase, PREVIEW_SIZE)
+            put(jpegUseCase, MAXIMUM_SIZE)
+            put(rawUseCase, MAXIMUM_SIZE)
+        }
+        getSuggestedSpecsAndVerify(
+            useCaseExpectedResultMap,
+            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
+            capabilities = intArrayOf(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW)
+        )
+    }
+
+    /**
+     * YUV/PREVIEW + JPEG/MAXIMUM + RAW/MAXIMUM
+     */
+    @Test
+    fun canSelectCorrectSizes_yuvPlusJpegPlusRAW_inLimitedDevice_withRawCapability() {
+        val yuvUseCase = createUseCase(CaptureType.IMAGE_ANALYSIS) // YUV
+        val jpegUseCase = createUseCase(CaptureType.IMAGE_CAPTURE) // JPEG
+        val rawUseCase = createRawUseCase() // RAW
+        val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+            put(yuvUseCase, PREVIEW_SIZE)
+            put(jpegUseCase, MAXIMUM_SIZE)
+            put(rawUseCase, MAXIMUM_SIZE)
+        }
+        getSuggestedSpecsAndVerify(
+            useCaseExpectedResultMap,
+            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
+            capabilities = intArrayOf(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW)
+        )
+    }
+
+    private fun getSuggestedSpecsAndVerify(
+        useCasesExpectedResultMap: Map<UseCase, Size>,
+        attachedSurfaceInfoList: List<AttachedSurfaceInfo> = emptyList(),
+        hardwareLevel: Int = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY,
+        capabilities: IntArray? = null,
+        compareWithAtMost: Boolean = false
+    ) {
+        setupCameraAndInitCameraX(
+            hardwareLevel = hardwareLevel,
+            capabilities = capabilities
+        )
+        val supportedSurfaceCombination = SupportedSurfaceCombination(
+            context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
+        )
+
+        val useCaseConfigMap = getUseCaseToConfigMap(useCasesExpectedResultMap.keys.toList())
+        val useCaseConfigToOutputSizesMap =
+            getUseCaseConfigToOutputSizesMap(useCaseConfigMap.values.toList())
+        val suggestedStreamSpecs = supportedSurfaceCombination.getSuggestedStreamSpecifications(
+            false,
+            attachedSurfaceInfoList,
+            useCaseConfigToOutputSizesMap
+        )
+
+        useCasesExpectedResultMap.keys.forEach {
+            val resultSize = suggestedStreamSpecs[useCaseConfigMap[it]]!!.resolution
+            val expectedSize = useCasesExpectedResultMap[it]!!
+            if (!compareWithAtMost) {
+                assertThat(resultSize).isEqualTo(expectedSize)
+            } else {
+                assertThat(sizeIsAtMost(resultSize, expectedSize)).isTrue()
+            }
+        }
+    }
+
+    private fun getUseCaseToConfigMap(useCases: List<UseCase>): Map<UseCase, UseCaseConfig<*>> {
+        val useCaseConfigMap = mutableMapOf<UseCase, UseCaseConfig<*>>().apply {
+            useCases.forEach {
+                put(it, it.currentConfig)
+            }
+        }
+        return useCaseConfigMap
+    }
+
+    private fun getUseCaseConfigToOutputSizesMap(
+        useCaseConfigs: List<UseCaseConfig<*>>
+    ): Map<UseCaseConfig<*>, List<Size>> {
+        val resultMap = mutableMapOf<UseCaseConfig<*>, List<Size>>().apply {
+            useCaseConfigs.forEach {
+                put(it, DEFAULT_SUPPORTED_SIZES.toList())
+            }
+        }
+
+        return resultMap
+    }
+
+    /**
+     * Helper function that returns whether size is <= maxSize
+     *
+     */
+    private fun sizeIsAtMost(size: Size, maxSize: Size): Boolean {
+        return (size.height * size.width) <= (maxSize.height * maxSize.width)
+    }
+
+    // //////////////////////////////////////////////////////////////////////////////////////////
+    //
+    // Resolution selection tests for FPS settings
+    //
+    // //////////////////////////////////////////////////////////////////////////////////////////
+
+    @Test
+    fun getSupportedOutputSizes_single_valid_targetFPS() {
+        // a valid target means the device is capable of that fps
+        val useCase = createUseCase(CaptureType.PREVIEW, targetFrameRate = Range<Int>(25, 30))
+        val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+            put(useCase, Size(3840, 2160))
+        }
+        getSuggestedSpecsAndVerify(
+            useCaseExpectedResultMap,
+            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
+        )
+    }
+
+    @Test
+    fun getSupportedOutputSizes_single_invalid_targetFPS() {
+        // an invalid target means the device would neve be able to reach that fps
+        val useCase = createUseCase(CaptureType.PREVIEW, targetFrameRate = Range<Int>(65, 70))
+        val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+            put(useCase, Size(800, 450))
+        }
+        getSuggestedSpecsAndVerify(
+            useCaseExpectedResultMap,
+            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
+        )
+    }
+
+    @Test
+    fun getSupportedOutputSizes_multiple_targetFPS_first_is_larger() {
+        // a valid target means the device is capable of that fps
+        val useCase1 = createUseCase(CaptureType.PREVIEW, targetFrameRate = Range<Int>(30, 35))
+        val useCase2 = createUseCase(CaptureType.PREVIEW, targetFrameRate = Range<Int>(15, 25))
+        val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+            // both selected size should be no larger than 1920 x 1445
+            put(useCase1, Size(1920, 1445))
+            put(useCase2, Size(1920, 1445))
+        }
+        getSuggestedSpecsAndVerify(
+            useCaseExpectedResultMap,
+            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL,
+            compareWithAtMost = true
+        )
+    }
+
+    @Test
+    fun getSupportedOutputSizes_multiple_targetFPS_first_is_smaller() {
+        // a valid target means the device is capable of that fps
+        val useCase1 = createUseCase(CaptureType.PREVIEW, targetFrameRate = Range<Int>(30, 35))
+        val useCase2 = createUseCase(CaptureType.PREVIEW, targetFrameRate = Range<Int>(45, 50))
+        val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+            // both selected size should be no larger than 1920 x 1440
+            put(useCase1, Size(1920, 1440))
+            put(useCase2, Size(1920, 1440))
+        }
+        getSuggestedSpecsAndVerify(
+            useCaseExpectedResultMap,
+            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
+            compareWithAtMost = true
+        )
+    }
+
+    @Test
+    fun getSupportedOutputSizes_multiple_targetFPS_intersect() {
+        // first and second new use cases have target fps that intersect each other
+        val useCase1 = createUseCase(CaptureType.PREVIEW, targetFrameRate = Range<Int>(30, 40))
+        val useCase2 = createUseCase(CaptureType.PREVIEW, targetFrameRate = Range<Int>(35, 45))
+        val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+            // effective target fps becomes 35-40
+            // both selected size should be no larger than 1920 x 1080
+            put(useCase1, Size(1920, 1080))
+            put(useCase2, Size(1920, 1080))
+        }
+        getSuggestedSpecsAndVerify(
+            useCaseExpectedResultMap,
+            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
+            compareWithAtMost = true
+        )
+    }
+
+    @Test
+    fun getSupportedOutputSizes_multiple_cases_first_has_targetFPS() {
+        // first new use case has a target fps, second new use case does not
+        val useCase1 = createUseCase(CaptureType.PREVIEW, targetFrameRate = Range<Int>(30, 35))
+        val useCase2 = createUseCase(CaptureType.PREVIEW)
+        val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+            // both selected size should be no larger than 1920 x 1440
+            put(useCase1, Size(1920, 1440))
+            put(useCase2, Size(1920, 1440))
+        }
+        getSuggestedSpecsAndVerify(
+            useCaseExpectedResultMap,
+            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
+            compareWithAtMost = true
+        )
+    }
+
+    @Test
+    fun getSupportedOutputSizes_multiple_cases_second_has_targetFPS() {
+        // second new use case does not have a target fps, first new use case does not
+        val useCase1 = createUseCase(CaptureType.PREVIEW)
+        val useCase2 = createUseCase(CaptureType.PREVIEW, targetFrameRate = Range<Int>(30, 35))
+        val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+            // both selected size should be no larger than 1920 x 1440
+            put(useCase1, Size(1920, 1440))
+            put(useCase2, Size(1920, 1440))
+        }
+        getSuggestedSpecsAndVerify(
+            useCaseExpectedResultMap,
+            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
+            compareWithAtMost = true
+        )
+    }
+
+    @Test
+    fun getSupportedOutputSizes_attached_with_targetFPS_no_new_targetFPS() {
+        // existing surface with target fps + new use case without a target fps
+        val useCase = createUseCase(CaptureType.PREVIEW)
+        val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+            // size should be no larger than 1280 x 960
+            put(useCase, Size(1280, 960))
+        }
+        // existing surface w/ target fps
+        val attachedSurfaceInfo = AttachedSurfaceInfo.create(
+            SurfaceConfig.create(
+                ConfigType.JPEG,
+                ConfigSize.PREVIEW
+            ), ImageFormat.JPEG,
+            Size(1280, 720), Range(40, 50)
+        )
+        getSuggestedSpecsAndVerify(
+            useCaseExpectedResultMap,
+            attachedSurfaceInfoList = listOf(attachedSurfaceInfo),
+            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
+            compareWithAtMost = true
+        )
+    }
+
+    @Test
+    fun getSupportedOutputSizes_attached_with_targetFPS_and_new_targetFPS_no_intersect() {
+        // existing surface with target fps + new use case with target fps that does not intersect
+        val useCase = createUseCase(CaptureType.PREVIEW, targetFrameRate = Range<Int>(30, 35))
+        val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+            // size of new surface should be no larger than 1280 x 960
+            put(useCase, Size(1280, 960))
+        }
+        // existing surface w/ target fps
+        val attachedSurfaceInfo = AttachedSurfaceInfo.create(
+            SurfaceConfig.create(
+                ConfigType.JPEG,
+                ConfigSize.PREVIEW
+            ), ImageFormat.JPEG,
+            Size(1280, 720), Range(40, 50)
+        )
+        getSuggestedSpecsAndVerify(
+            useCaseExpectedResultMap,
+            attachedSurfaceInfoList = listOf(attachedSurfaceInfo),
+            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
+            compareWithAtMost = true
+        )
+    }
+
+    @Test
+    fun getSupportedOutputSizes_attached_with_targetFPS_and_new_targetFPS_with_intersect() {
+        // existing surface with target fps + new use case with target fps that intersect each other
+        val useCase = createUseCase(CaptureType.PREVIEW, targetFrameRate = Range<Int>(45, 50))
+        val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
+            // size of new surface should be no larger than 1280 x 720
+            put(useCase, Size(1280, 720))
+        }
+        // existing surface w/ target fps
+        val attachedSurfaceInfo = AttachedSurfaceInfo.create(
+            SurfaceConfig.create(
+                ConfigType.JPEG,
+                ConfigSize.PREVIEW
+            ), ImageFormat.JPEG,
+            Size(1280, 720), Range(40, 50)
+        )
+        getSuggestedSpecsAndVerify(
+            useCaseExpectedResultMap,
+            attachedSurfaceInfoList = listOf(attachedSurfaceInfo),
+            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
+            compareWithAtMost = true
+        )
+    }
+
+    // //////////////////////////////////////////////////////////////////////////////////////////
+    //
+    // Other tests
+    //
+    // //////////////////////////////////////////////////////////////////////////////////////////
+
     @Test
     fun getMaximumSizeForImageFormat() {
         setupCameraAndInitCameraX()
@@ -1498,1043 +1599,6 @@
     }
 
     @Test
-    fun isAspectRatioMatchWithSupportedMod16Resolution_LegacyApi() {
-        isAspectRatioMatchWithSupportedMod16Resolution(legacyUseCaseCreator)
-    }
-
-    @Test
-    fun isAspectRatioMatchWithSupportedMod16Resolution_ResolutionSelector() {
-        isAspectRatioMatchWithSupportedMod16Resolution(resolutionSelectorUseCaseCreator)
-    }
-
-    private fun isAspectRatioMatchWithSupportedMod16Resolution(useCaseCreator: UseCaseCreator) {
-        setupCameraAndInitCameraX(
-            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
-        )
-        val supportedSurfaceCombination = SupportedSurfaceCombination(
-            context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
-        )
-        val useCase = useCaseCreator.createUseCase(
-            FAKE_USE_CASE,
-            Surface.ROTATION_90,
-            preferredAspectRatio = AspectRatio.RATIO_16_9,
-            preferredResolution = MOD16_SIZE
-        )
-        val suggestedStreamSpecMap = getSuggestedStreamSpecMap(
-            false, supportedSurfaceCombination, useCase)
-        assertThat(suggestedStreamSpecMap[useCase]?.resolution).isEqualTo(MOD16_SIZE)
-    }
-
-    @Test
-    fun sortByCompareSizesByArea_canSortSizesCorrectly() {
-        val sizes = arrayOfNulls<Size>(DEFAULT_SUPPORTED_SIZES.size)
-        // Generates a unsorted array from mSupportedSizes.
-        val centerIndex = DEFAULT_SUPPORTED_SIZES.size / 2
-        // Puts 2nd half sizes in the front
-        for (i in centerIndex until DEFAULT_SUPPORTED_SIZES.size) {
-            sizes[i - centerIndex] = DEFAULT_SUPPORTED_SIZES[i]
-        }
-        // Puts 1st half sizes inversely in the tail
-        for (j in centerIndex - 1 downTo 0) {
-            sizes[DEFAULT_SUPPORTED_SIZES.size - j - 1] = DEFAULT_SUPPORTED_SIZES[j]
-        }
-        // The testing sizes array will be equal to mSupportedSizes after sorting.
-        Arrays.sort(sizes, CompareSizesByArea(true))
-        assertThat(listOf(*sizes)).isEqualTo(listOf(*DEFAULT_SUPPORTED_SIZES))
-    }
-
-    @Test
-    fun getSupportedOutputSizes_noConfigSettings() {
-        setupCameraAndInitCameraX(
-            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
-        )
-        val supportedSurfaceCombination = SupportedSurfaceCombination(
-            context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
-        )
-        val useCase = createUseCaseByLegacyApi(FAKE_USE_CASE)
-        // There is default minimum size 640x480 setting. Sizes smaller than 640x480 will be
-        // removed. No any aspect ratio related setting. The returned sizes list will be sorted in
-        // descending order.
-        val resultList = getSupportedOutputSizes(supportedSurfaceCombination, useCase)
-        val expectedList = listOf(
-            Size(4032, 3024),
-            Size(3840, 2160),
-            Size(1920, 1440),
-            Size(1920, 1080),
-            Size(1280, 960),
-            Size(1280, 720),
-            Size(960, 544),
-            Size(800, 450),
-            Size(640, 480)
-        )
-        assertThat(resultList).isEqualTo(expectedList)
-    }
-
-    @Test
-    fun getSupportedOutputSizes_aspectRatio4x3() {
-        setupCameraAndInitCameraX(
-            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
-        )
-        val supportedSurfaceCombination = SupportedSurfaceCombination(
-            context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
-        )
-        val useCase = createUseCaseByLegacyApi(
-            FAKE_USE_CASE,
-            targetAspectRatio = AspectRatio.RATIO_4_3
-        )
-        // There is default minimum size 640x480 setting. Sizes smaller than 640x480 will be
-        // removed. Sizes of aspect ratio 4/3 will be in front of the returned sizes list and the
-        // list is sorted in descending order. Other items will be put in the following that are
-        // sorted by aspect ratio delta and then area size.
-        val resultList = getSupportedOutputSizes(supportedSurfaceCombination, useCase)
-        val expectedList = listOf(
-            // Matched AspectRatio items, sorted by area size.
-            Size(4032, 3024),
-            Size(1920, 1440),
-            Size(1280, 960),
-            Size(640, 480),
-            // Mismatched AspectRatio items, sorted by aspect ratio delta then area size.
-            Size(3840, 2160),
-            Size(1920, 1080),
-            Size(1280, 720),
-            Size(960, 544),
-            Size(800, 450)
-        )
-        assertThat(resultList).isEqualTo(expectedList)
-    }
-
-    @Test
-    fun getSupportedOutputSizes_aspectRatio16x9_InLimitedDevice() {
-        setupCameraAndInitCameraX(
-            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
-        )
-        val supportedSurfaceCombination = SupportedSurfaceCombination(
-            context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
-        )
-        val useCase = createUseCaseByLegacyApi(
-            FAKE_USE_CASE,
-            targetAspectRatio = AspectRatio.RATIO_16_9
-        )
-        // There is default minimum size 640x480 setting. Sizes smaller than 640x480 will be
-        // removed. Sizes of aspect ratio 16/9 will be in front of the returned sizes list and the
-        // list is sorted in descending order. Other items will be put in the following that are
-        // sorted by aspect ratio delta and then area size.
-        val resultList = getSupportedOutputSizes(supportedSurfaceCombination, useCase)
-        val expectedList = listOf(
-            // Matched AspectRatio items, sorted by area size.
-            Size(3840, 2160),
-            Size(1920, 1080),
-            Size(1280, 720),
-            Size(960, 544),
-            Size(800, 450),
-            // Mismatched AspectRatio items, sorted by aspect ratio delta then area size.
-            Size(4032, 3024),
-            Size(1920, 1440),
-            Size(1280, 960),
-            Size(640, 480)
-        )
-        assertThat(resultList).isEqualTo(expectedList)
-    }
-
-    @Test
-    fun getSupportedOutputSizes_aspectRatio16x9_inLegacyDevice() {
-        setupCameraAndInitCameraX()
-        val supportedSurfaceCombination = SupportedSurfaceCombination(
-            context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
-        )
-        val useCase = createUseCaseByLegacyApi(
-            FAKE_USE_CASE,
-            targetAspectRatio = AspectRatio.RATIO_16_9
-        )
-        val resultList = getSupportedOutputSizes(supportedSurfaceCombination, useCase)
-        // There is default minimum size 640x480 setting. Sizes smaller than 640x480 will be
-        // removed.
-        val expectedList: List<Size> = if (Build.VERSION.SDK_INT == 21) {
-            // Sizes with the same aspect ratio as maximum JPEG resolution will be in front of
-            // the returned sizes list and the list is sorted in descending order. Other items
-            // will be put in the following that are sorted by aspect ratio delta and then area
-            // size.
-            listOf(
-                // Matched the same AspectRatio as maximum JPEG items, sorted by aspect ratio
-                // delta then area size.
-                Size(4032, 3024),
-                Size(1920, 1440),
-                Size(1280, 960),
-                Size(640, 480)
-                // Non-matched items have been removed by OutputSizesCorrector due to
-                // TargetAspectRatio quirk.
-            )
-        } else {
-            // Sizes of aspect ratio 16/9 will be in front of the returned sizes list and the
-            // list is sorted in descending order. Other items will be put in the following that
-            // are sorted by aspect ratio delta and then area size.
-            listOf(
-                // Matched AspectRatio items, sorted by area size.
-                Size(3840, 2160),
-                Size(1920, 1080),
-                Size(1280, 720),
-                Size(960, 544),
-                Size(800, 450),
-                // Mismatched AspectRatio items, sorted by aspect ratio delta then area size.
-                Size(4032, 3024),
-                Size(1920, 1440),
-                Size(1280, 960),
-                Size(640, 480)
-            )
-        }
-        assertThat(resultList).isEqualTo(expectedList)
-    }
-
-    @Test
-    fun getSupportedOutputSizes_targetResolution1080x1920InRotation0_InLimitedDevice() {
-        setupCameraAndInitCameraX(
-            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
-        )
-        val supportedSurfaceCombination = SupportedSurfaceCombination(
-            context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
-        )
-        val useCase = createUseCaseByLegacyApi(
-            FAKE_USE_CASE,
-            targetResolution = Size(1080, 1920)
-        )
-        // Unnecessary big enough sizes will be removed from the result list. There is default
-        // minimum size 640x480 setting. Sizes smaller than 640x480 will also be removed. The
-        // target resolution will be calibrated by default target rotation 0 degree. The
-        // auto-resolution mechanism will try to select the sizes which aspect ratio is nearest
-        // to the aspect ratio of target resolution in priority. Therefore, sizes of aspect ratio
-        // 16/9 will be in front of the returned sizes list and the list is sorted in descending
-        // order. Other items will be put in the following that are sorted by aspect ratio delta
-        // and then area size.
-        val resultList = getSupportedOutputSizes(supportedSurfaceCombination, useCase)
-        val expectedList = listOf(
-            // Matched AspectRatio items, sorted by area size.
-            Size(1920, 1080),
-            Size(1280, 720),
-            Size(960, 544),
-            Size(800, 450),
-            // Mismatched AspectRatio items, sorted by aspect ratio delta then area size.
-            Size(1920, 1440),
-            Size(1280, 960),
-            Size(640, 480)
-        )
-        assertThat(resultList).isEqualTo(expectedList)
-    }
-
-    @Test
-    fun getSupportedOutputSizes_targetResolution1080x1920InRotation0_InLegacyDevice() {
-        setupCameraAndInitCameraX()
-        val supportedSurfaceCombination = SupportedSurfaceCombination(
-            context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
-        )
-        val useCase = createUseCaseByLegacyApi(
-            FAKE_USE_CASE,
-            targetResolution = Size(1080, 1920)
-        )
-        // Unnecessary big enough sizes will be removed from the result list. There is default
-        // minimum size 640x480 setting. Sizes smaller than 640x480 will also be removed.
-        val resultList = getSupportedOutputSizes(supportedSurfaceCombination, useCase)
-        val expectedList: List<Size> = if (Build.VERSION.SDK_INT == 21) {
-            // Sizes with the same aspect ratio as maximum JPEG resolution will be in front of
-            // the returned sizes list and the list is sorted in descending order. Other items
-            // will be put in the following that are sorted by aspect ratio delta and then area
-            // size.
-            listOf(
-                // Matched the same AspectRatio as maximum JPEG items, sorted by aspect ratio
-                // delta then area size.
-                Size(1920, 1440),
-                Size(1280, 960),
-                Size(640, 480)
-                // Non-matched items have been removed by OutputSizesCorrector due to
-                // TargetAspectRatio quirk.
-            )
-        } else {
-            // The target resolution will be calibrated by default target rotation 0 degree. The
-            // auto-resolution mechanism will try to select the sizes which aspect ratio is
-            // nearest to the aspect ratio of target resolution in priority. Therefore, sizes of
-            // aspect ratio 16/9 will be in front of the returned sizes list and the list is
-            // sorted in descending order. Other items will be put in the following that are
-            // sorted by aspect ratio delta and then area size.
-            listOf(
-                // Matched AspectRatio items, sorted by area size.
-                Size(1920, 1080),
-                Size(1280, 720),
-                Size(960, 544),
-                Size(800, 450),
-                // Mismatched AspectRatio items, sorted by aspect ratio delta then area size.
-                Size(1920, 1440),
-                Size(1280, 960),
-                Size(640, 480)
-            )
-        }
-        assertThat(resultList).isEqualTo(expectedList)
-    }
-
-    @Test
-    fun getSupportedOutputSizes_targetResolutionLargerThan640x480() {
-        setupCameraAndInitCameraX(
-            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
-        )
-        val supportedSurfaceCombination = SupportedSurfaceCombination(
-            context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
-        )
-        val useCase = createUseCaseByLegacyApi(
-            FAKE_USE_CASE,
-            targetRotation = Surface.ROTATION_90,
-            targetResolution = Size(1280, 960)
-        )
-        // Unnecessary big enough sizes will be removed from the result list. There is default
-        // minimum size 640x480 setting. Target resolution larger than 640x480 won't overwrite
-        // minimum size setting. Sizes smaller than 640x480 will be removed. The auto-resolution
-        // mechanism will try to select the sizes which aspect ratio is nearest to the aspect
-        // ratio of target resolution in priority. Therefore, sizes of aspect ratio 4/3 will be
-        // in front of the returned sizes list and the list is sorted in descending order. Other
-        // items will be put in the following that are sorted by aspect ratio delta and then area
-        // size.
-        val resultList = getSupportedOutputSizes(supportedSurfaceCombination, useCase)
-        val expectedList = listOf(
-            // Matched AspectRatio items, sorted by area size.
-            Size(1280, 960),
-            Size(640, 480),
-            // Mismatched AspectRatio items, sorted by aspect ratio delta then area size.
-            Size(1920, 1080),
-            Size(1280, 720),
-            Size(960, 544),
-            Size(800, 450)
-        )
-        assertThat(resultList).isEqualTo(expectedList)
-    }
-
-    @Test
-    fun getSupportedOutputSizes_targetResolutionSmallerThan640x480() {
-        setupCameraAndInitCameraX(
-            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
-        )
-        val supportedSurfaceCombination = SupportedSurfaceCombination(
-            context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
-        )
-        val useCase = createUseCaseByLegacyApi(
-            FAKE_USE_CASE,
-            targetRotation = Surface.ROTATION_90,
-            targetResolution = Size(320, 240)
-        )
-        // Unnecessary big enough sizes will be removed from the result list. Minimum size will
-        // be overwritten as 320x240. Sizes smaller than 320x240 will also be removed. The
-        // auto-resolution mechanism will try to select the sizes which aspect ratio is nearest
-        // to the aspect ratio of target resolution in priority. Therefore, sizes of aspect ratio
-        // 4/3 will be in front of the returned sizes list and the list is sorted in descending
-        // order. Other items will be put in the following that are sorted by aspect ratio delta
-        // and then area size.
-        val resultList = getSupportedOutputSizes(supportedSurfaceCombination, useCase)
-        val expectedList = listOf(
-            // Matched AspectRatio items, sorted by area size.
-            Size(320, 240),
-            // Mismatched AspectRatio items, sorted by aspect ratio delta then area size.
-            Size(800, 450)
-        )
-        assertThat(resultList).isEqualTo(expectedList)
-    }
-
-    @Test
-    fun getSupportedOutputSizes_maxResolutionSmallerThan640x480() {
-        setupCameraAndInitCameraX(
-            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
-        )
-        val supportedSurfaceCombination = SupportedSurfaceCombination(
-            context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
-        )
-        val useCase = createUseCaseByLegacyApi(
-            FAKE_USE_CASE,
-            maxResolution = Size(320, 240)
-        )
-        // Minimum size bound will be removed due to small max resolution setting.
-        val resultList = getSupportedOutputSizes(supportedSurfaceCombination, useCase)
-        val expectedList = Arrays.asList(
-            *arrayOf(
-                Size(320, 240),
-                Size(320, 180),
-                Size(256, 144)
-            )
-        )
-        assertThat(resultList).isEqualTo(expectedList)
-    }
-
-    @Test
-    fun getSupportedOutputSizes_targetResolution1800x1440NearTo4x3() {
-        setupCameraAndInitCameraX(
-            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
-        )
-        val supportedSurfaceCombination = SupportedSurfaceCombination(
-            context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
-        )
-        val useCase = createUseCaseByLegacyApi(
-            FAKE_USE_CASE,
-            targetRotation = Surface.ROTATION_90,
-            targetResolution = Size(1800, 1440)
-        )
-        // Unnecessary big enough sizes will be removed from the result list. There is default
-        // minimum size 640x480 setting. Sizes smaller than 640x480 will also be removed. The
-        // auto-resolution mechanism will try to select the sizes which aspect ratio is nearest
-        // to the aspect ratio of target resolution in priority. Size 1800x1440 is near to 4/3
-        // therefore, sizes of aspect ratio 4/3 will be in front of the returned sizes list and
-        // the list is sorted in descending order.
-        val resultList = getSupportedOutputSizes(supportedSurfaceCombination, useCase)
-        val expectedList = listOf(
-            // Sizes of 4/3 are near to aspect ratio of 1800/1440
-            Size(1920, 1440),
-            Size(1280, 960),
-            Size(640, 480),
-            // Sizes of 16/9 are far to aspect ratio of 1800/1440
-            Size(3840, 2160),
-            Size(1920, 1080),
-            Size(1280, 720),
-            Size(960, 544),
-            Size(800, 450)
-        )
-        assertThat(resultList).isEqualTo(expectedList)
-    }
-
-    @Test
-    fun getSupportedOutputSizes_targetResolution1280x600NearTo16x9() {
-        setupCameraAndInitCameraX(
-            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
-        )
-        val supportedSurfaceCombination = SupportedSurfaceCombination(
-            context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
-        )
-        val useCase = createUseCaseByLegacyApi(
-            FAKE_USE_CASE,
-            targetRotation = Surface.ROTATION_90,
-            targetResolution = Size(1280, 600)
-        )
-        // Unnecessary big enough sizes will be removed from the result list. There is default
-        // minimum size 640x480 setting. Sizes smaller than 640x480 will also be removed. The
-        // auto-resolution mechanism will try to select the sizes which aspect ratio is nearest
-        // to the aspect ratio of target resolution in priority. Size 1280x600 is near to 16/9,
-        // therefore, sizes of aspect ratio 16/9 will be in front of the returned sizes list and
-        // the list is sorted in descending order.
-        val resultList = getSupportedOutputSizes(supportedSurfaceCombination, useCase)
-        val expectedList = listOf(
-            // Sizes of 16/9 are near to aspect ratio of 1280/600
-            Size(1280, 720),
-            Size(960, 544),
-            Size(800, 450),
-            // Sizes of 4/3 are far to aspect ratio of 1280/600
-            Size(1280, 960),
-            Size(640, 480)
-        )
-        assertThat(resultList).isEqualTo(expectedList)
-    }
-
-    @Test
-    fun getSupportedOutputSizes_maxResolution1280x720() {
-        setupCameraAndInitCameraX(
-            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
-        )
-        val supportedSurfaceCombination = SupportedSurfaceCombination(
-            context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
-        )
-        val useCase = createUseCaseByLegacyApi(
-            FAKE_USE_CASE,
-            maxResolution = Size(1280, 720)
-        )
-        // There is default minimum size 640x480 setting. Sizes smaller than 640x480 or
-        // larger than 1280x720 will be removed. The returned sizes list will be sorted in
-        // descending order.
-        val resultList = getSupportedOutputSizes(supportedSurfaceCombination, useCase)
-        val expectedList = listOf(Size(1280, 720), Size(960, 544), Size(800, 450), Size(640, 480))
-        assertThat(resultList).isEqualTo(expectedList)
-    }
-
-    @Test
-    fun getSupportedOutputSizes_setCustomOrderedResolutions() {
-        setupCameraAndInitCameraX(
-            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
-        )
-        val supportedSurfaceCombination = SupportedSurfaceCombination(
-            context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
-        )
-        val customOrderedResolutions = listOf(
-            Size(640, 480),
-            Size(1280, 720),
-            Size(1920, 1080),
-            Size(3840, 2160),
-        )
-        val useCase = createUseCaseByLegacyApi(
-            FAKE_USE_CASE,
-            customOrderedResolutions = customOrderedResolutions,
-            maxResolution = Size(1920, 1440),
-            defaultResolution = Size(1280, 720),
-            supportedResolutions = listOf(
-                Pair.create(
-                    ImageFormat.PRIVATE, arrayOf(
-                        Size(800, 450),
-                        Size(640, 480),
-                        Size(320, 240),
-                    )
-                )
-            )
-        )
-        // Custom ordered resolutions is fully respected, meaning it will not be sorted or filtered
-        // by other configurations such as max/default/target/supported resolutions.
-        val resultList = getSupportedOutputSizes(supportedSurfaceCombination, useCase)
-        assertThat(resultList).containsExactlyElementsIn(customOrderedResolutions).inOrder()
-    }
-
-    @Test
-    fun previewCanSelectResolutionLargerThanDisplay_withMaxResolution_LegacyApi() {
-        previewCanSelectResolutionLargerThanDisplay_withMaxResolution(legacyUseCaseCreator)
-    }
-
-    @Test
-    fun previewCanSelectResolutionLargerThanDisplay_withMaxResolution_ResolutionSelector() {
-        previewCanSelectResolutionLargerThanDisplay_withMaxResolution(
-            resolutionSelectorUseCaseCreator
-        )
-    }
-
-    private fun previewCanSelectResolutionLargerThanDisplay_withMaxResolution(
-        useCaseCreator: UseCaseCreator
-    ) {
-        setupCameraAndInitCameraX(
-            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
-        )
-        val supportedSurfaceCombination = SupportedSurfaceCombination(
-            context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
-        )
-        // The max resolution is expressed in the sensor coordinate.
-        val useCase = useCaseCreator.createUseCase(
-            PREVIEW_USE_CASE,
-            maxResolution = MAXIMUM_SIZE
-        )
-        val suggestedStreamSpecMap = getSuggestedStreamSpecMap(
-            false, supportedSurfaceCombination, useCase)
-        // Checks mMaximumSize is final selected for the use case.
-        assertThat(suggestedStreamSpecMap[useCase]?.resolution).isEqualTo(MAXIMUM_SIZE)
-    }
-
-    @Test
-    fun getSupportedOutputSizes_defaultResolution1280x720_noTargetResolution() {
-        setupCameraAndInitCameraX(
-            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
-        )
-        val supportedSurfaceCombination = SupportedSurfaceCombination(
-            context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
-        )
-        val useCase = createUseCaseByLegacyApi(
-            FAKE_USE_CASE,
-            defaultResolution = Size(1280, 720)
-        )
-        // There is default minimum size 640x480 setting. Sizes smaller than 640x480 will be
-        // removed. If there is no target resolution setting, it will be overwritten by default
-        // resolution as 1280x720. Unnecessary big enough sizes will also be removed. The
-        // returned sizes list will be sorted in descending order.
-        val resultList = getSupportedOutputSizes(supportedSurfaceCombination, useCase)
-        val expectedList = listOf(Size(1280, 720), Size(960, 544), Size(800, 450), Size(640, 480))
-        assertThat(resultList).isEqualTo(expectedList)
-    }
-
-    @Test
-    fun getSupportedOutputSizes_defaultResolution1280x720_targetResolution1920x1080() {
-        setupCameraAndInitCameraX(
-            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
-        )
-        val supportedSurfaceCombination = SupportedSurfaceCombination(
-            context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
-        )
-        val useCase = createUseCaseByLegacyApi(
-            FAKE_USE_CASE,
-            targetRotation = Surface.ROTATION_90,
-            defaultResolution = Size(1280, 720),
-            targetResolution = Size(1920, 1080)
-        )
-        // There is default minimum size 640x480 setting. Sizes smaller than 640x480 will be
-        // removed. There is target resolution 1920x1080, it won't be overwritten by default
-        // resolution 1280x720. Unnecessary big enough sizes will also be removed. Sizes of
-        // aspect ratio 16/9 will be in front of the returned sizes list and the list is sorted
-        // in descending order.  Other items will be put in the following that are sorted by
-        // aspect ratio delta and then area size.
-        val resultList = getSupportedOutputSizes(supportedSurfaceCombination, useCase)
-        val expectedList = listOf(
-            // Matched AspectRatio items, sorted by area size.
-            Size(1920, 1080),
-            Size(1280, 720),
-            Size(960, 544),
-            Size(800, 450),
-            // Mismatched AspectRatio items, sorted by aspect ratio delta then area size.
-            Size(1920, 1440),
-            Size(1280, 960),
-            Size(640, 480)
-        )
-        assertThat(resultList).isEqualTo(expectedList)
-    }
-
-    @Test
-    fun getSupportedOutputSizes_fallbackToGuaranteedResolution_whenNotFulfillConditions() {
-        setupCameraAndInitCameraX(
-            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
-            supportedSizes = arrayOf(
-                Size(640, 480),
-                Size(320, 240),
-                Size(320, 180),
-                Size(256, 144)
-            )
-        )
-        val supportedSurfaceCombination = SupportedSurfaceCombination(
-            context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
-        )
-        val useCase = createUseCaseByLegacyApi(
-            FAKE_USE_CASE,
-            targetRotation = Surface.ROTATION_90,
-            targetResolution = Size(1920, 1080)
-        )
-        // There is default minimum size 640x480 setting. Sizes smaller than 640x480 will be
-        // removed. There is target resolution 1920x1080 (16:9). Even 640x480 does not match 16:9
-        // requirement, it will still be returned to use.
-        val resultList = getSupportedOutputSizes(supportedSurfaceCombination, useCase)
-        val expectedList = listOf(Size(640, 480))
-        assertThat(resultList).isEqualTo(expectedList)
-    }
-
-    @Test
-    fun getSupportedOutputSizes_whenMaxSizeSmallerThanDefaultMiniSize() {
-        setupCameraAndInitCameraX(
-            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
-            supportedSizes = arrayOf(
-                Size(640, 480),
-                Size(320, 240),
-                Size(320, 180),
-                Size(256, 144)
-            )
-        )
-        val supportedSurfaceCombination = SupportedSurfaceCombination(
-            context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
-        )
-        val useCase = createUseCaseByLegacyApi(
-            FAKE_USE_CASE,
-            maxResolution = Size(320, 240)
-        )
-        // There is default minimum size 640x480 setting. Originally, sizes smaller than 640x480
-        // will be removed. Due to maximal size bound is smaller than the default minimum size
-        // bound and it is also smaller than 640x480, the default minimum size bound will be
-        // ignored. Then, sizes equal to or smaller than 320x240 will be kept in the result list.
-        val resultList = getSupportedOutputSizes(supportedSurfaceCombination, useCase)
-        val expectedList = listOf(Size(320, 240), Size(320, 180), Size(256, 144))
-        assertThat(resultList).isEqualTo(expectedList)
-    }
-
-    @Test
-    fun getSupportedOutputSizes_whenMaxSizeSmallerThanSmallTargetResolution() {
-        setupCameraAndInitCameraX(
-            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
-            supportedSizes = arrayOf(
-                Size(640, 480),
-                Size(320, 240),
-                Size(320, 180),
-                Size(256, 144)
-            )
-        )
-        val supportedSurfaceCombination = SupportedSurfaceCombination(
-            context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
-        )
-        val useCase = createUseCaseByLegacyApi(
-            FAKE_USE_CASE,
-            targetRotation = Surface.ROTATION_90,
-            targetResolution = Size(320, 240),
-            maxResolution = Size(320, 180)
-        )
-        // The default minimum size 640x480 will be overwritten by the target resolution 320x240.
-        // Originally, sizes smaller than 320x240 will be removed. Due to maximal size bound is
-        // smaller than the minimum size bound and it is also smaller than 640x480, the minimum
-        // size bound will be ignored. Then, sizes equal to or smaller than 320x180 will be kept
-        // in the result list.
-        val resultList = getSupportedOutputSizes(supportedSurfaceCombination, useCase)
-        val expectedList = listOf(Size(320, 180), Size(256, 144))
-        assertThat(resultList).isEqualTo(expectedList)
-    }
-
-    @Test
-    fun getSupportedOutputSizes_whenBothMaxAndTargetResolutionsSmallerThan640x480() {
-        setupCameraAndInitCameraX(
-            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
-            supportedSizes = arrayOf(
-                Size(640, 480),
-                Size(320, 240),
-                Size(320, 180),
-                Size(256, 144)
-            )
-        )
-        val supportedSurfaceCombination = SupportedSurfaceCombination(
-            context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
-        )
-        val useCase = createUseCaseByLegacyApi(
-            FAKE_USE_CASE,
-            targetRotation = Surface.ROTATION_90,
-            targetResolution = Size(320, 180),
-            maxResolution = Size(320, 240)
-        )
-        // The default minimum size 640x480 will be overwritten by the target resolution 320x180.
-        // Originally, sizes smaller than 320x180 will be removed. Due to maximal size bound is
-        // smaller than the minimum size bound and it is also smaller than 640x480, the minimum
-        // size bound will be ignored. Then, all sizes equal to or smaller than 320x320 will be
-        // kept in the result list.
-        val resultList = getSupportedOutputSizes(supportedSurfaceCombination, useCase)
-        val expectedList = listOf(Size(320, 180), Size(256, 144), Size(320, 240))
-        assertThat(resultList).isEqualTo(expectedList)
-    }
-
-    @Test
-    fun getSupportedOutputSizes_whenMaxSizeSmallerThanBigTargetResolution() {
-        setupCameraAndInitCameraX(
-            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
-        )
-        val supportedSurfaceCombination = SupportedSurfaceCombination(
-            context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
-        )
-        val useCase = createUseCaseByLegacyApi(
-            FAKE_USE_CASE,
-            targetRotation = Surface.ROTATION_90,
-            targetResolution = Size(3840, 2160),
-            maxResolution = Size(1920, 1080)
-        )
-        // Because the target size 3840x2160 is larger than 640x480, it won't overwrite the
-        // default minimum size 640x480. Sizes smaller than 640x480 will be removed. The
-        // auto-resolution mechanism will try to select the sizes which aspect ratio is nearest
-        // to the aspect ratio of target resolution in priority. Therefore, sizes of aspect ratio
-        // 16/9 will be in front of the returned sizes list and the list is sorted in descending
-        // order. Other items will be put in the following that are sorted by aspect ratio delta
-        // and then area size.
-        val resultList = getSupportedOutputSizes(supportedSurfaceCombination, useCase)
-        val expectedList = listOf(
-            // Matched AspectRatio items, sorted by area size.
-            Size(1920, 1080),
-            Size(1280, 720),
-            Size(960, 544),
-            Size(800, 450),
-            // Mismatched AspectRatio items, sorted by aspect ratio delta then area size.
-            Size(1280, 960),
-            Size(640, 480)
-        )
-        assertThat(resultList).isEqualTo(expectedList)
-    }
-
-    @Test
-    fun getSupportedOutputSizes_whenNoSizeBetweenMaxSizeAndTargetResolution() {
-        setupCameraAndInitCameraX(
-            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
-            supportedSizes = arrayOf(
-                Size(640, 480),
-                Size(320, 240),
-                Size(320, 180),
-                Size(256, 144)
-            )
-        )
-        val supportedSurfaceCombination = SupportedSurfaceCombination(
-            context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
-        )
-        val useCase = createUseCaseByLegacyApi(
-            FAKE_USE_CASE,
-            targetRotation = Surface.ROTATION_90,
-            targetResolution = Size(320, 190),
-            maxResolution = Size(320, 200)
-        )
-        // The default minimum size 640x480 will be overwritten by the target resolution 320x190.
-        // Originally, sizes smaller than 320x190 will be removed. Due to there is no available
-        // size between the maximal size and the minimum size bound and the maximal size is
-        // smaller than 640x480, the default minimum size bound will be ignored. Then, sizes
-        // equal to or smaller than 320x200 will be kept in the result list.
-        val resultList = getSupportedOutputSizes(supportedSurfaceCombination, useCase)
-        val expectedList = listOf(Size(320, 180), Size(256, 144))
-        assertThat(resultList).isEqualTo(expectedList)
-    }
-
-    @Test
-    fun getSupportedOutputSizes_whenTargetResolutionSmallerThanAnySize() {
-        setupCameraAndInitCameraX(
-            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
-            supportedSizes = arrayOf(
-                Size(640, 480),
-                Size(320, 240),
-                Size(320, 180),
-                Size(256, 144)
-            )
-        )
-        val supportedSurfaceCombination = SupportedSurfaceCombination(
-            context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
-        )
-        val useCase = createUseCaseByLegacyApi(
-            FAKE_USE_CASE,
-            targetRotation = Surface.ROTATION_90,
-            targetResolution = Size(192, 144)
-        )
-        // The default minimum size 640x480 will be overwritten by the target resolution 192x144.
-        // Because 192x144 is smaller than any size in the supported list, no one will be
-        // filtered out by it. The result list will only keep one big enough size of aspect ratio
-        // 4:3 and 16:9.
-        val resultList = getSupportedOutputSizes(supportedSurfaceCombination, useCase)
-        val expectedList = listOf(Size(320, 240), Size(256, 144))
-        assertThat(resultList).isEqualTo(expectedList)
-    }
-
-    @Test
-    fun getSupportedOutputSizes_whenMaxResolutionSmallerThanAnySize() {
-        setupCameraAndInitCameraX(
-            supportedSizes = arrayOf(
-                Size(640, 480),
-                Size(320, 240),
-                Size(320, 180),
-                Size(256, 144)
-            )
-        )
-        val supportedSurfaceCombination = SupportedSurfaceCombination(
-            context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
-        )
-        val useCase = createUseCaseByLegacyApi(
-            FAKE_USE_CASE,
-            maxResolution = Size(192, 144)
-        )
-        // All sizes will be filtered out by the max resolution 192x144 setting and an
-        // IllegalArgumentException will be thrown.
-        assertThrows(IllegalArgumentException::class.java) {
-            getSupportedOutputSizes(supportedSurfaceCombination, useCase)
-        }
-    }
-
-    @Test
-    fun getSupportedOutputSizes_whenMod16IsIgnoredForSmallSizes() {
-        setupCameraAndInitCameraX(
-            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
-            supportedSizes = arrayOf(
-                Size(640, 480),
-                Size(320, 240),
-                Size(320, 180),
-                Size(296, 144),
-                Size(256, 144)
-            )
-        )
-        val supportedSurfaceCombination = SupportedSurfaceCombination(
-            context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
-        )
-        val useCase = createUseCaseByLegacyApi(
-            FAKE_USE_CASE,
-            targetRotation = Surface.ROTATION_90,
-            targetResolution = Size(185, 90)
-        )
-        // The default minimum size 640x480 will be overwritten by the target resolution 185x90
-        // (18.5:9). If mod 16 calculation is not ignored for the sizes smaller than 640x480, the
-        // size 256x144 will be considered to match 18.5:9 and then become the first item in the
-        // result list. After ignoring mod 16 calculation for small sizes, 256x144 will still be
-        // kept as a 16:9 resolution as the result.
-        val resultList = getSupportedOutputSizes(supportedSurfaceCombination, useCase)
-        val expectedList = listOf(Size(296, 144), Size(256, 144), Size(320, 240))
-        assertThat(resultList).isEqualTo(expectedList)
-    }
-
-    @Test
-    fun getSupportedOutputSizes_whenOneMod16SizeClosestToTargetResolution() {
-        setupCameraAndInitCameraX(
-            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
-            supportedSizes = arrayOf(
-                Size(1920, 1080),
-                Size(1440, 1080),
-                Size(1280, 960),
-                Size(1280, 720),
-                Size(864, 480), // This is a 16:9 mod16 size that is closest to 2016x1080
-                Size(768, 432),
-                Size(640, 480),
-                Size(640, 360),
-                Size(480, 360),
-                Size(384, 288)
-            )
-        )
-        val supportedSurfaceCombination = SupportedSurfaceCombination(
-            context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
-        )
-        val useCase = createUseCaseByLegacyApi(
-            FAKE_USE_CASE,
-            targetResolution = Size(1080, 2016)
-        )
-        val resultList = getSupportedOutputSizes(supportedSurfaceCombination, useCase)
-        val expectedList = listOf(
-            Size(1920, 1080),
-            Size(1280, 720),
-            Size(864, 480),
-            Size(768, 432),
-            Size(1440, 1080),
-            Size(1280, 960),
-            Size(640, 480)
-        )
-        assertThat(resultList).isEqualTo(expectedList)
-    }
-
-    @Test
-    fun getSupportedOutputSizesWithPortraitPixelArraySize_aspectRatio16x9() {
-        // Sets the sensor orientation as 0 and pixel array size as a portrait size to simulate a
-        // phone device which majorly supports portrait output sizes.
-        setupCameraAndInitCameraX(
-            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
-            sensorOrientation = SENSOR_ORIENTATION_0,
-            pixelArraySize = PORTRAIT_PIXEL_ARRAY_SIZE,
-            supportedSizes = arrayOf(
-                Size(1080, 1920),
-                Size(1080, 1440),
-                Size(960, 1280),
-                Size(720, 1280),
-                Size(1280, 720),
-                Size(480, 640),
-                Size(640, 480),
-                Size(360, 480)
-            )
-        )
-        val supportedSurfaceCombination = SupportedSurfaceCombination(
-            context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
-        )
-        val useCase = createUseCaseByLegacyApi(
-            FAKE_USE_CASE,
-            targetAspectRatio = AspectRatio.RATIO_16_9
-        )
-        // There is default minimum size 640x480 setting. Sizes smaller than 640x480 will be
-        // removed. Due to the pixel array size is portrait, sizes of aspect ratio 9/16 will be in
-        // front of the returned sizes list and the list is sorted in descending order. Other
-        // items will be put in the following that are sorted by aspect ratio delta and then area
-        // size.
-        val resultList = getSupportedOutputSizes(supportedSurfaceCombination, useCase)
-        val expectedList = listOf(
-            // Matched AspectRatio items, sorted by area size.
-            Size(1080, 1920),
-            Size(720, 1280),
-            // Mismatched AspectRatio items, sorted by aspect ratio delta then area size.
-            Size(1080, 1440),
-            Size(960, 1280),
-            Size(480, 640),
-            Size(640, 480),
-            Size(1280, 720)
-        )
-        assertThat(resultList).isEqualTo(expectedList)
-    }
-
-    @Test
-    fun getSupportedOutputSizesOnTabletWithPortraitPixelArraySize_aspectRatio16x9() {
-        // Sets the sensor orientation as 90 and pixel array size as a portrait size to simulate a
-        // tablet device which majorly supports portrait output sizes.
-        setupCameraAndInitCameraX(
-            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
-            sensorOrientation = SENSOR_ORIENTATION_90,
-            pixelArraySize = PORTRAIT_PIXEL_ARRAY_SIZE,
-            supportedSizes = arrayOf(
-                Size(1080, 1920),
-                Size(1080, 1440),
-                Size(960, 1280),
-                Size(720, 1280),
-                Size(1280, 720),
-                Size(480, 640),
-                Size(640, 480),
-                Size(360, 480)
-            )
-        )
-        val supportedSurfaceCombination = SupportedSurfaceCombination(
-            context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
-        )
-        val useCase = createUseCaseByLegacyApi(
-            FAKE_USE_CASE,
-            targetAspectRatio = AspectRatio.RATIO_16_9
-        )
-        // There is default minimum size 640x480 setting. Sizes smaller than 640x480 will be
-        // removed. Due to the pixel array size is portrait, sizes of aspect ratio 9/16 will be in
-        // front of the returned sizes list and the list is sorted in descending order. Other
-        // items will be put in the following that are sorted by aspect ratio delta and then area
-        // size.
-        val resultList = getSupportedOutputSizes(supportedSurfaceCombination, useCase)
-        val expectedList = listOf(
-            // Matched AspectRatio items, sorted by area size.
-            Size(1080, 1920),
-            Size(720, 1280),
-            // Mismatched AspectRatio items, sorted by aspect ratio delta then area size.
-            Size(1080, 1440),
-            Size(960, 1280),
-            Size(480, 640),
-            Size(640, 480),
-            Size(1280, 720)
-        )
-        assertThat(resultList).isEqualTo(expectedList)
-    }
-
-    @Test
-    fun getSupportedOutputSizesOnTablet_aspectRatio16x9() {
-        setupCameraAndInitCameraX(
-            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
-            sensorOrientation = SENSOR_ORIENTATION_0,
-            pixelArraySize = LANDSCAPE_PIXEL_ARRAY_SIZE
-        )
-        val supportedSurfaceCombination = SupportedSurfaceCombination(
-            context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
-        )
-        val useCase = createUseCaseByLegacyApi(
-            FAKE_USE_CASE,
-            targetAspectRatio = AspectRatio.RATIO_16_9
-        )
-        // There is default minimum size 640x480 setting. Sizes smaller than 640x480 will be
-        // removed. Sizes of aspect ratio 16/9 will be in front of the returned sizes list and the
-        // list is sorted in descending order. Other items will be put in the following that are
-        // sorted by aspect ratio delta and then area size.
-        val resultList = getSupportedOutputSizes(supportedSurfaceCombination, useCase)
-        val expectedList = listOf(
-            // Matched AspectRatio items, sorted by area size.
-            Size(3840, 2160),
-            Size(1920, 1080),
-            Size(1280, 720),
-            Size(960, 544),
-            Size(800, 450),
-            // Mismatched AspectRatio items, sorted by aspect ratio delta then area size.
-            Size(4032, 3024),
-            Size(1920, 1440),
-            Size(1280, 960),
-            Size(640, 480)
-        )
-        assertThat(resultList).isEqualTo(expectedList)
-    }
-
-    @Test
-    fun getSupportedOutputSizesOnTabletWithPortraitSizes_aspectRatio16x9() {
-        setupCameraAndInitCameraX(
-            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
-            sensorOrientation = SENSOR_ORIENTATION_0, supportedSizes = arrayOf(
-                Size(1920, 1080),
-                Size(1440, 1080),
-                Size(1280, 960),
-                Size(1280, 720),
-                Size(720, 1280),
-                Size(640, 480),
-                Size(480, 640),
-                Size(480, 360)
-            )
-        )
-        val supportedSurfaceCombination = SupportedSurfaceCombination(
-            context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
-        )
-        val useCase = createUseCaseByLegacyApi(
-            FAKE_USE_CASE,
-            targetAspectRatio = AspectRatio.RATIO_16_9
-        )
-        // There is default minimum size 640x480 setting. Sizes smaller than 640x480 will be
-        // removed. Sizes of aspect ratio 16/9 will be in front of the returned sizes list and the
-        // list is sorted in descending order. Other items will be put in the following that are
-        // sorted by aspect ratio delta and then area size.
-        val resultList = getSupportedOutputSizes(supportedSurfaceCombination, useCase)
-        val expectedList = listOf(
-            // Matched AspectRatio items, sorted by area size.
-            Size(1920, 1080),
-            Size(1280, 720),
-            // Mismatched AspectRatio items, sorted by aspect ratio delta then area size.
-            Size(1440, 1080),
-            Size(1280, 960),
-            Size(640, 480),
-            Size(480, 640),
-            Size(720, 1280)
-        )
-        assertThat(resultList).isEqualTo(expectedList)
-    }
-
-    @Test
     fun determineRecordSizeFromStreamConfigurationMap() {
         // Setup camera with non-integer camera Id
         setupCameraAndInitCameraX(cameraId = EXTERNAL_CAMERA_ID)
@@ -2549,652 +1613,6 @@
         )
     }
 
-    @Test
-    fun canGet640x480_whenAnotherGroupMatchedInMod16Exists_LegacyApi() {
-        canGet640x480_whenAnotherGroupMatchedInMod16Exists(legacyUseCaseCreator)
-    }
-
-    @Test
-    fun canGet640x480_whenAnotherGroupMatchedInMod16Exists_RS_SensorSize() {
-        canGet640x480_whenAnotherGroupMatchedInMod16Exists(resolutionSelectorUseCaseCreator)
-    }
-
-    @Test
-    fun canGet640x480_whenAnotherGroupMatchedInMod16Exists_RS_ViewSize() {
-        canGet640x480_whenAnotherGroupMatchedInMod16Exists(viewSizeResolutionSelectorUseCaseCreator)
-    }
-
-    private fun canGet640x480_whenAnotherGroupMatchedInMod16Exists(useCaseCreator: UseCaseCreator) {
-        setupCameraAndInitCameraX(
-            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
-            supportedSizes = arrayOf(
-                Size(4000, 3000),
-                Size(3840, 2160),
-                Size(1920, 1080),
-                Size(1024, 738), // This will create a 512/269 aspect ratio group that
-                // 640x480 will be considered to match in mod16 condition.
-                Size(800, 600),
-                Size(640, 480),
-                Size(320, 240)
-            )
-        )
-        val supportedSurfaceCombination = SupportedSurfaceCombination(
-            context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
-        )
-        // Sets the target resolution as 640x480 with target rotation as ROTATION_90 because the
-        // sensor orientation is 90.
-        val useCase = useCaseCreator.createUseCase(
-            FAKE_USE_CASE,
-            targetRotation = Surface.ROTATION_90,
-            preferredResolution = RESOLUTION_VGA
-        )
-        val suggestedStreamSpecMap = getSuggestedStreamSpecMap(
-            false, supportedSurfaceCombination, useCase)
-        // Checks 640x480 is final selected for the use case.
-        assertThat(suggestedStreamSpecMap[useCase]?.resolution).isEqualTo(RESOLUTION_VGA)
-    }
-
-    @Test
-    fun canGetSupportedSizeSmallerThan640x480_whenLargerMaxResolutionIsSet_LegacyApi() {
-        canGetSupportedSizeSmallerThan640x480_whenLargerMaxResolutionIsSet(legacyUseCaseCreator)
-    }
-
-    @Test
-    fun canGetSupportedSizeSmallerThan640x480_whenLargerMaxResolutionIsSet_ResolutionSelector() {
-        canGetSupportedSizeSmallerThan640x480_whenLargerMaxResolutionIsSet(
-            resolutionSelectorUseCaseCreator
-        )
-    }
-
-    private fun canGetSupportedSizeSmallerThan640x480_whenLargerMaxResolutionIsSet(
-        useCaseCreator: UseCaseCreator
-    ) {
-        setupCameraAndInitCameraX(
-            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
-            supportedSizes = arrayOf(Size(480, 480))
-        )
-        val supportedSurfaceCombination = SupportedSurfaceCombination(
-            context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
-        )
-        // Sets the max resolution as 720x1280
-        val useCase = useCaseCreator.createUseCase(
-            FAKE_USE_CASE,
-            maxResolution = DISPLAY_SIZE
-        )
-        val suggestedStreamSpecMap = getSuggestedStreamSpecMap(
-            false, supportedSurfaceCombination, useCase)
-        // Checks 480x480 is final selected for the use case.
-        assertThat(suggestedStreamSpecMap[useCase]?.resolution).isEqualTo(Size(480, 480))
-    }
-
-    @Test
-    fun previewSizeIsSelectedForImageAnalysis_withImageCaptureInLimitedDevice_LegacyApi() {
-        previewSizeIsSelectedForImageAnalysis_withImageCaptureInLimitedDevice(
-            legacyUseCaseCreator, PREVIEW_SIZE
-        )
-    }
-
-    // For the ResolutionSelector API, RECORD_SIZE can't be used because it exceeds
-    // PREVIEW_SIZE. Therefore, the logic will fallback to select a 4:3 PREVIEW_SIZE. Then,
-    // 640x480 will be selected.
-    @Test
-    fun previewSizeIsSelectedForImageAnalysis_withImageCaptureInLimitedDevice_RS_SensorSize() {
-        previewSizeIsSelectedForImageAnalysis_withImageCaptureInLimitedDevice(
-            resolutionSelectorUseCaseCreator, RESOLUTION_VGA
-        )
-    }
-
-    @Test
-    fun previewSizeIsSelectedForImageAnalysis_withImageCaptureInLimitedDevice_RS_ViewSize() {
-        previewSizeIsSelectedForImageAnalysis_withImageCaptureInLimitedDevice(
-            viewSizeResolutionSelectorUseCaseCreator, RESOLUTION_VGA
-        )
-    }
-
-    private fun previewSizeIsSelectedForImageAnalysis_withImageCaptureInLimitedDevice(
-        useCaseCreator: UseCaseCreator,
-        expectedResult: Size
-    ) {
-        setupCameraAndInitCameraX(
-            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
-        )
-        val supportedSurfaceCombination = SupportedSurfaceCombination(
-            context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
-        )
-        val preview = useCaseCreator.createUseCase(PREVIEW_USE_CASE) as Preview
-        preview.setSurfaceProvider(
-            CameraXExecutors.directExecutor(),
-            SurfaceTextureProvider.createSurfaceTextureProvider(
-                Mockito.mock(
-                    SurfaceTextureCallback::class.java
-                )
-            )
-        )
-        // ImageCapture has no explicit target resolution setting
-        val imageCapture = useCaseCreator.createUseCase(IMAGE_CAPTURE_USE_CASE)
-        // A LEGACY-level above device supports the following configuration.
-        //     PRIV/PREVIEW + YUV/PREVIEW + JPEG/MAXIMUM
-        //
-        // A LIMITED-level above device supports the following configuration.
-        //     PRIV/PREVIEW + YUV/RECORD + JPEG/RECORD
-        //
-        // Even there is a RECORD size target resolution setting for ImageAnalysis, ImageCapture
-        // will still have higher priority to have a MAXIMUM size resolution if the app doesn't
-        // explicitly specify a RECORD size target resolution to ImageCapture.
-        val imageAnalysis = useCaseCreator.createUseCase(
-            IMAGE_ANALYSIS_USE_CASE,
-            targetRotation = Surface.ROTATION_90,
-            preferredResolution = RECORD_SIZE
-        )
-        val suggestedStreamSpecMap = getSuggestedStreamSpecMap(
-            false,
-            supportedSurfaceCombination,
-            preview,
-            imageCapture,
-            imageAnalysis
-        )
-        assertThat(suggestedStreamSpecMap[imageAnalysis]?.resolution).isEqualTo(expectedResult)
-    }
-
-    @Test
-    fun imageAnalysisSelectRecordSize_imageCaptureHasExplicitSizeInLimitedDevice_LegacyApi() {
-        imageAnalysisSelectRecordSize_imageCaptureHasExplicitSizeInLimitedDevice(
-            legacyUseCaseCreator
-        )
-    }
-
-    @Test
-    fun imageAnalysisSelectRecordSize_imageCaptureHasExplicitSizeInLimitedDevice_RS_SensorSize() {
-        imageAnalysisSelectRecordSize_imageCaptureHasExplicitSizeInLimitedDevice(
-            resolutionSelectorUseCaseCreator
-        )
-    }
-
-    @Test
-    fun imageAnalysisSelectRecordSize_imageCaptureHasExplicitSizeInLimitedDevice_RS_ViewSize() {
-        imageAnalysisSelectRecordSize_imageCaptureHasExplicitSizeInLimitedDevice(
-            viewSizeResolutionSelectorUseCaseCreator
-        )
-    }
-
-    private fun imageAnalysisSelectRecordSize_imageCaptureHasExplicitSizeInLimitedDevice(
-        useCaseCreator: UseCaseCreator
-    ) {
-        setupCameraAndInitCameraX(
-            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
-        )
-        val supportedSurfaceCombination = SupportedSurfaceCombination(
-            context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
-        )
-        val preview = useCaseCreator.createUseCase(PREVIEW_USE_CASE) as Preview
-        preview.setSurfaceProvider(
-            CameraXExecutors.directExecutor(),
-            SurfaceTextureProvider.createSurfaceTextureProvider(
-                Mockito.mock(
-                    SurfaceTextureCallback::class.java
-                )
-            )
-        )
-        // ImageCapture has no explicit RECORD size target resolution setting
-        val imageCapture = useCaseCreator.createUseCase(
-            IMAGE_CAPTURE_USE_CASE,
-            targetRotation = Surface.ROTATION_90,
-            preferredResolution = RECORD_SIZE
-        )
-        // A LEGACY-level above device supports the following configuration.
-        //     PRIV/PREVIEW + YUV/PREVIEW + JPEG/MAXIMUM
-        //
-        // A LIMITED-level above device supports the following configuration.
-        //     PRIV/PREVIEW + YUV/RECORD + JPEG/RECORD
-        //
-        // A RECORD can be selected for ImageAnalysis if the ImageCapture has a explicit RECORD
-        // size target resolution setting. It means that the application know the trade-off and
-        // the ImageAnalysis has higher priority to get a larger resolution than ImageCapture.
-        val imageAnalysis = useCaseCreator.createUseCase(
-            IMAGE_ANALYSIS_USE_CASE,
-            targetRotation = Surface.ROTATION_90,
-            preferredResolution = RECORD_SIZE
-        )
-        val suggestedStreamSpecMap = getSuggestedStreamSpecMap(
-            false,
-            supportedSurfaceCombination,
-            preview,
-            imageCapture,
-            imageAnalysis
-        )
-        assertThat(suggestedStreamSpecMap[imageAnalysis]?.resolution).isEqualTo(RECORD_SIZE)
-    }
-
-    @Config(minSdk = Build.VERSION_CODES.M)
-    @Test
-    fun highResolutionIsSelected_whenHighResolutionIsEnabled() {
-        setupCameraAndInitCameraX(
-            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
-            capabilities = intArrayOf(
-                CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE
-            ),
-            supportedHighResolutionSizes = arrayOf(Size(8000, 6000), Size(8000, 4500))
-        )
-        val supportedSurfaceCombination = SupportedSurfaceCombination(
-            context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
-        )
-
-        val useCase = createUseCaseByResolutionSelector(FAKE_USE_CASE, highResolutionEnabled = true)
-        val suggestedStreamSpecMap = getSuggestedStreamSpecMap(
-            false, supportedSurfaceCombination, useCase)
-
-        // Checks 8000x6000 is final selected for the use case.
-        assertThat(suggestedStreamSpecMap[useCase]?.resolution).isEqualTo(Size(8000, 6000))
-    }
-
-    @Config(minSdk = Build.VERSION_CODES.M)
-    @Test
-    fun highResolutionIsNotSelected_whenHighResolutionIsEnabled_withoutBurstCaptureCapability() {
-        setupCameraAndInitCameraX(
-            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
-            supportedHighResolutionSizes = arrayOf(Size(8000, 6000), Size(8000, 4500))
-        )
-        val supportedSurfaceCombination = SupportedSurfaceCombination(
-            context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
-        )
-
-        val useCase = createUseCaseByResolutionSelector(FAKE_USE_CASE, highResolutionEnabled = true)
-        val suggestedStreamSpecMap = getSuggestedStreamSpecMap(
-            false, supportedSurfaceCombination, useCase)
-
-        // Checks 8000x6000 is final selected for the use case.
-        assertThat(suggestedStreamSpecMap[useCase]?.resolution).isEqualTo(Size(4032, 3024))
-    }
-
-    @Config(minSdk = Build.VERSION_CODES.M)
-    @Test
-    fun highResolutionIsNotSelected_whenHighResolutionIsNotEnabled_targetResolution8000x6000() {
-        setupCameraAndInitCameraX(
-            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
-            capabilities = intArrayOf(
-                CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE
-            ),
-            supportedHighResolutionSizes = arrayOf(Size(8000, 6000), Size(8000, 4500))
-        )
-        val supportedSurfaceCombination = SupportedSurfaceCombination(
-            context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
-        )
-
-        val useCase =
-            createUseCaseByResolutionSelector(FAKE_USE_CASE, preferredResolution = Size(8000, 6000))
-        val suggestedStreamSpecMap = getSuggestedStreamSpecMap(
-            false, supportedSurfaceCombination, useCase)
-
-        // Checks 8000x6000 is final selected for the use case.
-        assertThat(suggestedStreamSpecMap[useCase]?.resolution).isEqualTo(Size(4032, 3024))
-    }
-
-    @Config(minSdk = Build.VERSION_CODES.M)
-    @Test
-    fun highResolutionIsSelected_whenHighResolutionIsEnabled_aspectRatio16x9() {
-        setupCameraAndInitCameraX(
-            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
-            capabilities = intArrayOf(
-                CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE
-            ),
-            supportedHighResolutionSizes = arrayOf(Size(8000, 6000), Size(8000, 4500))
-        )
-        val supportedSurfaceCombination = SupportedSurfaceCombination(
-            context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
-        )
-
-        val useCase = createUseCaseByResolutionSelector(
-            FAKE_USE_CASE,
-            preferredAspectRatio = AspectRatio.RATIO_16_9,
-            highResolutionEnabled = true
-        )
-        val suggestedStreamSpecMap = getSuggestedStreamSpecMap(
-            false, supportedSurfaceCombination, useCase)
-
-        // Checks 8000x6000 is final selected for the use case.
-        assertThat(suggestedStreamSpecMap[useCase]?.resolution).isEqualTo(Size(8000, 4500))
-    }
-
-    @Test
-    @Throws(CameraUnavailableException::class, CameraAccessExceptionCompat::class)
-    fun getSupportedOutputSizes_single_valid_targetFPS() {
-        // a valid target means the device is capable of that fps
-        setupCameraAndInitCameraX(
-            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
-        )
-        val supportedSurfaceCombination = SupportedSurfaceCombination(
-            context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
-        )
-
-        // use case with target fps
-        val useCase1 = createUseCaseByLegacyApi(
-            FAKE_USE_CASE,
-            targetFrameRate = Range<Int>(25, 30)
-        )
-
-        val suggestedStreamSpecMap = getSuggestedStreamSpecMap(
-            false,
-            supportedSurfaceCombination,
-            useCase1
-        )
-        // single selected size should be equal to 3840 x 2160
-        assertThat(suggestedStreamSpecMap[useCase1]!!.resolution).isEqualTo(Size(3840, 2160))
-    }
-
-    @Test
-    @Throws(CameraUnavailableException::class, CameraAccessExceptionCompat::class)
-    fun getSupportedOutputSizes_single_invalid_targetFPS() {
-        // an invalid target means the device would neve be able to reach that fps
-        setupCameraAndInitCameraX(
-            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
-        )
-        val supportedSurfaceCombination = SupportedSurfaceCombination(
-            context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
-        )
-
-        // use case with target fps
-        val useCase1 = createUseCaseByLegacyApi(
-            FAKE_USE_CASE,
-            targetFrameRate = Range<Int>(65, 70)
-        )
-
-        val suggestedStreamSpecMap = getSuggestedStreamSpecMap(
-            false,
-            supportedSurfaceCombination,
-            useCase1
-        )
-        // single selected size should be equal to 3840 x 2160
-        assertThat(suggestedStreamSpecMap[useCase1]!!.resolution).isEqualTo(Size(800, 450))
-    }
-
-    @Test
-    @Throws(CameraUnavailableException::class, CameraAccessExceptionCompat::class)
-    fun getSupportedOutputSizes_multiple_targetFPS_first_is_larger() {
-        // a valid target means the device is capable of that fps
-        setupCameraAndInitCameraX(
-            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL
-        )
-        val supportedSurfaceCombination = SupportedSurfaceCombination(
-            context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
-        )
-
-        val useCase1 = createUseCaseByLegacyApi(
-            FAKE_USE_CASE,
-            targetFrameRate = Range<Int>(30, 35),
-            surfaceOccupancyPriority = 1
-        )
-
-        val useCase2 = createUseCaseByLegacyApi(
-            FAKE_USE_CASE,
-            targetFrameRate = Range<Int>(15, 25)
-        )
-
-        val suggestedStreamSpecMap = getSuggestedStreamSpecMap(
-            false,
-            supportedSurfaceCombination,
-            useCase1,
-            useCase2
-        )
-        // both selected size should be no larger than 1920 x 1080
-        assertThat(sizeIsAtMost(suggestedStreamSpecMap[useCase1]!!.resolution, Size(1920, 1445)))
-            .isTrue()
-        assertThat(sizeIsAtMost(suggestedStreamSpecMap[useCase2]!!.resolution, Size(1920, 1445)))
-            .isTrue()
-    }
-
-    @Test
-    @Throws(CameraUnavailableException::class, CameraAccessExceptionCompat::class)
-    fun getSupportedOutputSizes_multiple_targetFPS_first_is_smaller() {
-        // a valid target means the device is capable of that fps
-        setupCameraAndInitCameraX(
-            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
-        )
-        val supportedSurfaceCombination = SupportedSurfaceCombination(
-            context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
-        )
-
-        val useCase1 = createUseCaseByLegacyApi(
-            FAKE_USE_CASE,
-            targetFrameRate = Range<Int>(30, 35),
-            surfaceOccupancyPriority = 1
-        )
-
-        val useCase2 = createUseCaseByLegacyApi(
-            FAKE_USE_CASE,
-            targetFrameRate = Range<Int>(45, 50)
-        )
-
-        val suggestedStreamSpecMap = getSuggestedStreamSpecMap(
-            false,
-            supportedSurfaceCombination,
-            useCase1,
-            useCase2
-        )
-        // both selected size should be no larger than 1920 x 1440
-        assertThat(sizeIsAtMost(suggestedStreamSpecMap[useCase1]!!.resolution, Size(1920, 1440)))
-            .isTrue()
-        assertThat(sizeIsAtMost(suggestedStreamSpecMap[useCase2]!!.resolution, Size(1920, 1440)))
-            .isTrue()
-    }
-
-    @Test
-    @Throws(CameraUnavailableException::class, CameraAccessExceptionCompat::class)
-    fun getSupportedOutputSizes_multiple_targetFPS_intersect() {
-        // first and second new use cases have target fps that intersect each other
-        setupCameraAndInitCameraX(
-            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
-        )
-        val supportedSurfaceCombination = SupportedSurfaceCombination(
-            context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
-        )
-
-        val useCase1 = createUseCaseByLegacyApi(
-            FAKE_USE_CASE,
-            targetFrameRate = Range<Int>(30, 40),
-            surfaceOccupancyPriority = 1
-        )
-
-        val useCase2 = createUseCaseByLegacyApi(
-            FAKE_USE_CASE,
-            targetFrameRate = Range<Int>(35, 45)
-        )
-
-        val suggestedStreamSpecMap = getSuggestedStreamSpecMap(
-            false,
-            supportedSurfaceCombination,
-            useCase1,
-            useCase2
-        )
-        // effective target fps becomes 35-40
-        // both selected size should be no larger than 1920 x 1080
-        assertThat(sizeIsAtMost(suggestedStreamSpecMap[useCase1]!!.resolution, Size(1920, 1080)))
-            .isTrue()
-        assertThat(sizeIsAtMost(suggestedStreamSpecMap[useCase2]!!.resolution, Size(1920, 1080)))
-            .isTrue()
-    }
-    @Test
-    @Throws(CameraUnavailableException::class, CameraAccessExceptionCompat::class)
-    fun getSupportedOutputSizes_multiple_cases_first_has_targetFPS() {
-        // first new use case has a target fps, second new use case does not
-        setupCameraAndInitCameraX(
-            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
-        )
-        val supportedSurfaceCombination = SupportedSurfaceCombination(
-            context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
-        )
-
-        val useCase1 = createUseCaseByLegacyApi(
-            FAKE_USE_CASE,
-            targetFrameRate = Range<Int>(30, 35),
-            surfaceOccupancyPriority = 1
-        )
-
-        val useCase2 = createUseCaseByLegacyApi(
-            FAKE_USE_CASE
-        )
-
-        val suggestedStreamSpecMap = getSuggestedStreamSpecMap(
-            false,
-            supportedSurfaceCombination,
-            useCase1,
-            useCase2
-        )
-        // both selected size should be no larger than 1920 x 1440
-        assertThat(sizeIsAtMost(suggestedStreamSpecMap[useCase1]!!.resolution, Size(1920, 1440)))
-            .isTrue()
-        assertThat(sizeIsAtMost(suggestedStreamSpecMap[useCase2]!!.resolution, Size(1920, 1440)))
-            .isTrue()
-    }
-
-    @Test
-    @Throws(CameraUnavailableException::class, CameraAccessExceptionCompat::class)
-    fun getSupportedOutputSizes_multiple_cases_second_has_targetFPS() {
-        // second new use case does not have a target fps, first new use case does not
-        setupCameraAndInitCameraX(
-            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
-        )
-        val supportedSurfaceCombination = SupportedSurfaceCombination(
-            context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
-        )
-
-        val useCase1 = createUseCaseByLegacyApi(
-            FAKE_USE_CASE
-        )
-        val useCase2 = createUseCaseByLegacyApi(
-            FAKE_USE_CASE,
-            targetFrameRate = Range<Int>(30, 35)
-        )
-
-        val suggestedStreamSpecMap = getSuggestedStreamSpecMap(
-            false,
-            supportedSurfaceCombination,
-            useCase1,
-            useCase2
-        )
-        // both selected size should be no larger than 1920 x 1440
-        assertThat(sizeIsAtMost(suggestedStreamSpecMap[useCase1]!!.resolution, Size(1920, 1440)))
-            .isTrue()
-        assertThat(sizeIsAtMost(suggestedStreamSpecMap[useCase2]!!.resolution, Size(1920, 1440)))
-            .isTrue()
-    }
-
-    @Test
-    @Throws(CameraUnavailableException::class, CameraAccessExceptionCompat::class)
-    fun getSupportedOutputSizes_attached_with_targetFPS_no_new_targetFPS() {
-        // existing surface with target fps + new use case without a target fps
-        setupCameraAndInitCameraX(
-            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
-        )
-        val supportedSurfaceCombination = SupportedSurfaceCombination(
-            context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
-        )
-
-        // existing surface w/ target fps
-        val attachedSurfaceInfo = AttachedSurfaceInfo.create(
-            SurfaceConfig.create(
-                ConfigType.JPEG,
-                ConfigSize.PREVIEW
-            ), ImageFormat.JPEG,
-            Size(1280, 720), Range(40, 50)
-        )
-
-        // new use case with no target fps
-        val useCase1 = createUseCaseByLegacyApi(FAKE_USE_CASE)
-
-        val suggestedStreamSpecMap = getSuggestedStreamSpecMap(
-            false,
-            supportedSurfaceCombination,
-            useCase1,
-            attachedSurfaces = listOf(attachedSurfaceInfo)
-        )
-        // size should be no larger than 1280 x 960
-        assertThat(sizeIsAtMost(suggestedStreamSpecMap[useCase1]!!.resolution, Size(1280, 960)))
-            .isTrue()
-    }
-    @Test
-    @Throws(CameraUnavailableException::class, CameraAccessExceptionCompat::class)
-    fun getSupportedOutputSizes_attached_with_targetFPS_and_new_targetFPS_no_intersect() {
-        // existing surface with target fps + new use case with target fps that does not intersect
-        setupCameraAndInitCameraX(
-            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
-        )
-        val supportedSurfaceCombination = SupportedSurfaceCombination(
-            context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
-        )
-
-        // existing surface w/ target fps
-        val attachedSurfaceInfo = AttachedSurfaceInfo.create(
-            SurfaceConfig.create(
-                ConfigType.JPEG,
-                ConfigSize.PREVIEW
-            ), ImageFormat.JPEG,
-            Size(1280, 720), Range(40, 50)
-        )
-
-        // new use case with target fps
-        val useCase1 = createUseCaseByLegacyApi(
-            FAKE_USE_CASE,
-            targetFrameRate = Range<Int>(30, 35)
-
-        )
-
-        val suggestedStreamSpecMap = getSuggestedStreamSpecMap(
-            false,
-            supportedSurfaceCombination,
-            useCase1,
-            attachedSurfaces = listOf(attachedSurfaceInfo)
-        )
-        // size of new surface should be no larger than 1280 x 960
-        assertThat(sizeIsAtMost(suggestedStreamSpecMap[useCase1]!!.resolution, Size(1280, 960)))
-            .isTrue()
-    }
-
-    @Test
-    @Throws(CameraUnavailableException::class, CameraAccessExceptionCompat::class)
-    fun getSupportedOutputSizes_attached_with_targetFPS_and_new_targetFPS_with_intersect() {
-        // existing surface with target fps + new use case with target fps that intersect each other
-        setupCameraAndInitCameraX(
-            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
-        )
-        val supportedSurfaceCombination = SupportedSurfaceCombination(
-            context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
-        )
-
-        // existing surface w/ target fps
-        val attachedSurfaceInfo = AttachedSurfaceInfo.create(
-            SurfaceConfig.create(
-                ConfigType.JPEG,
-                ConfigSize.PREVIEW
-            ), ImageFormat.JPEG,
-            Size(1280, 720), Range(40, 50)
-        )
-
-        // new use case with target fps
-        val useCase1 = createUseCaseByLegacyApi(
-            FAKE_USE_CASE,
-            targetFrameRate = Range<Int>(45, 50)
-
-        )
-
-        val suggestedStreamSpecMap = getSuggestedStreamSpecMap(
-            false,
-            supportedSurfaceCombination,
-            useCase1,
-            attachedSurfaces = listOf(attachedSurfaceInfo)
-        )
-        // size of new surface should be no larger than 1280 x 720
-        assertThat(sizeIsAtMost(suggestedStreamSpecMap[useCase1]!!.resolution, Size(1280, 720)))
-            .isTrue()
-    }
-
-    /**
-     * Helper function that returns whether size is <= maxSize
-     *
-     */
-    private fun sizeIsAtMost(size: Size, maxSize: Size): Boolean {
-        return (size.height * size.width) <= (maxSize.height * maxSize.width)
-    }
-
     /**
      * Sets up camera according to the specified settings and initialize [CameraX].
      *
@@ -3255,17 +1673,134 @@
     }
 
     /**
+     * Sets up camera according to the specified settings.
+     *
+     * @param cameraId the camera id to be set up. Default value is [DEFAULT_CAMERA_ID].
+     * @param hardwareLevel the hardware level of the camera. Default value is
+     * [CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY].
+     * @param sensorOrientation the sensor orientation of the camera. Default value is
+     * [SENSOR_ORIENTATION_90].
+     * @param pixelArraySize the active pixel array size of the camera. Default value is
+     * [LANDSCAPE_PIXEL_ARRAY_SIZE].
+     * @param supportedSizes the supported sizes of the camera. Default value is
+     * [DEFAULT_SUPPORTED_SIZES].
+     * @param capabilities the capabilities of the camera. Default value is null.
+     */
+    fun setupCamera(
+        cameraId: String = DEFAULT_CAMERA_ID,
+        hardwareLevel: Int = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY,
+        sensorOrientation: Int = SENSOR_ORIENTATION_90,
+        pixelArraySize: Size = LANDSCAPE_PIXEL_ARRAY_SIZE,
+        supportedSizes: Array<Size> = DEFAULT_SUPPORTED_SIZES,
+        supportedHighResolutionSizes: Array<Size>? = null,
+        capabilities: IntArray? = null
+    ) {
+        val mockMap = Mockito.mock(StreamConfigurationMap::class.java).also {
+            // Sets up the supported sizes
+            Mockito.`when`(it.getOutputSizes(ArgumentMatchers.anyInt()))
+                .thenReturn(supportedSizes)
+            // ImageFormat.PRIVATE was supported since API level 23. Before that, the supported
+            // output sizes need to be retrieved via SurfaceTexture.class.
+            Mockito.`when`(it.getOutputSizes(SurfaceTexture::class.java))
+                .thenReturn(supportedSizes)
+            // This is setup for the test to determine RECORD size from StreamConfigurationMap
+            Mockito.`when`(it.getOutputSizes(MediaRecorder::class.java))
+                .thenReturn(supportedSizes)
+
+            // setup to return different minimum frame durations depending on resolution
+            // minimum frame durations were designated only for the purpose of testing
+            Mockito.`when`(it.getOutputMinFrameDuration(
+                ArgumentMatchers.anyInt(),
+                ArgumentMatchers.eq(Size(4032, 3024))
+            ))
+                .thenReturn(50000000L) // 20 fps, size maximum
+
+            Mockito.`when`(it.getOutputMinFrameDuration(
+                ArgumentMatchers.anyInt(),
+                ArgumentMatchers.eq(Size(3840, 2160))
+            ))
+                .thenReturn(40000000L) // 25, size record
+
+            Mockito.`when`(it.getOutputMinFrameDuration(
+                ArgumentMatchers.anyInt(),
+                ArgumentMatchers.eq(Size(1920, 1440))
+            ))
+                .thenReturn(30000000L) // 30
+
+            Mockito.`when`(it.getOutputMinFrameDuration(
+                ArgumentMatchers.anyInt(),
+                ArgumentMatchers.eq(Size(1920, 1080))
+            ))
+                .thenReturn(28000000L) // 35
+
+            Mockito.`when`(it.getOutputMinFrameDuration(
+                ArgumentMatchers.anyInt(),
+                ArgumentMatchers.eq(Size(1280, 960))
+            ))
+                .thenReturn(25000000L) // 40
+
+            Mockito.`when`(it.getOutputMinFrameDuration(
+                ArgumentMatchers.anyInt(),
+                ArgumentMatchers.eq(Size(1280, 720))
+            ))
+                .thenReturn(22000000L) // 45, size preview/display
+
+            Mockito.`when`(it.getOutputMinFrameDuration(
+                ArgumentMatchers.anyInt(),
+                ArgumentMatchers.eq(Size(960, 544))
+            ))
+                .thenReturn(20000000L) // 50
+
+            Mockito.`when`(it.getOutputMinFrameDuration(
+                ArgumentMatchers.anyInt(),
+                ArgumentMatchers.eq(Size(800, 450))
+            ))
+                .thenReturn(16666000L) // 60fps
+
+            Mockito.`when`(it.getOutputMinFrameDuration(
+                ArgumentMatchers.anyInt(),
+                ArgumentMatchers.eq(Size(640, 480))
+            ))
+                .thenReturn(16666000L) // 60fps
+
+            // Sets up the supported high resolution sizes
+            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+                Mockito.`when`(it.getHighResolutionOutputSizes(ArgumentMatchers.anyInt()))
+                    .thenReturn(supportedHighResolutionSizes)
+            }
+        }
+
+        val characteristics = ShadowCameraCharacteristics.newCameraCharacteristics()
+        Shadow.extract<ShadowCameraCharacteristics>(characteristics).apply {
+            set(CameraCharacteristics.LENS_FACING, CameraCharacteristics.LENS_FACING_BACK)
+            set(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL, hardwareLevel)
+            set(CameraCharacteristics.SENSOR_ORIENTATION, sensorOrientation)
+            set(CameraCharacteristics.SENSOR_INFO_PIXEL_ARRAY_SIZE, pixelArraySize)
+            set(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP, mockMap)
+            capabilities?.let {
+                set(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES, it)
+            }
+        }
+
+        val cameraManager = ApplicationProvider.getApplicationContext<Context>()
+            .getSystemService(Context.CAMERA_SERVICE) as CameraManager
+        (Shadow.extract<Any>(cameraManager) as ShadowCameraManager)
+            .addCamera(cameraId, characteristics)
+    }
+
+    /**
      * Initializes the [CameraX].
      */
     private fun initCameraX() {
         val surfaceManagerProvider =
             CameraDeviceSurfaceManager.Provider { context, _, availableCameraIds ->
-                Camera2DeviceSurfaceManager(
+                cameraDeviceSurfaceManager = Camera2DeviceSurfaceManager(
                     context,
                     mockCamcorderProfileHelper,
                     CameraManagerCompat.from(this@SupportedSurfaceCombinationTest.context),
                     availableCameraIds
                 )
+                cameraDeviceSurfaceManager
             }
         val cameraXConfig = CameraXConfig.Builder.fromConfig(Camera2Config.defaultConfig())
             .setDeviceSurfaceManagerProvider(surfaceManagerProvider)
@@ -3298,7 +1833,9 @@
                     removeAt(index)
                 }
                 if (!supportedSurfaceCombination.checkSupported(
-                        isConcurrentCameraModeOn, subConfigurationList)) {
+                        isConcurrentCameraModeOn, subConfigurationList
+                    )
+                ) {
                     return false
                 }
             }
@@ -3306,164 +1843,27 @@
         return true
     }
 
-    /**
-     * Gets the suggested resolution map by the converted ResolutionSelector use case config which
-     * will also be converted when a use case is bound to the lifecycle.
-     */
-    private fun getSuggestedStreamSpecMap(
-        isConcurrentCameraModeOn: Boolean,
-        supportedSurfaceCombination: SupportedSurfaceCombination,
-        vararg useCases: UseCase,
-        attachedSurfaces: List<AttachedSurfaceInfo>? = null,
-        cameraFactory: CameraFactory = this.cameraFactory!!,
-        cameraId: String = DEFAULT_CAMERA_ID,
-        useCaseConfigFactory: UseCaseConfigFactory = this.useCaseConfigFactory!!
-    ): Map<UseCase, StreamSpec?> {
-        // Generates the use case to new ResolutionSelector use case config map
-        val useCaseToConfigMap = Configs.useCaseConfigMapWithDefaultSettingsFromUseCaseList(
-            cameraFactory.getCamera(cameraId).cameraInfoInternal,
-            listOf(*useCases),
-            useCaseConfigFactory
-        )
-        // Uses the use case config list to get suggested stream specs
-        val useCaseConfigStreamSpecMap = supportedSurfaceCombination
-            .getSuggestedStreamSpecifications(
-                isConcurrentCameraModeOn,
-                attachedSurfaces ?: emptyList(),
-                mutableListOf<UseCaseConfig<*>?>().apply { addAll(useCaseToConfigMap.values) }
-        )
-        val useCaseStreamSpecMap = mutableMapOf<UseCase, StreamSpec?>()
-        // Maps the use cases to the suggestion resolutions
-        for (useCase in useCases) {
-            useCaseStreamSpecMap[useCase] = useCaseConfigStreamSpecMap[useCaseToConfigMap[useCase]]
-        }
-        return useCaseStreamSpecMap
-    }
-
-    /**
-     * Gets the supported output sizes by the converted ResolutionSelector use case config which
-     * will also be converted when a use case is bound to the lifecycle.
-     */
-    private fun getSupportedOutputSizes(
-        supportedSurfaceCombination: SupportedSurfaceCombination,
-        useCase: UseCase,
-        cameraId: String = DEFAULT_CAMERA_ID,
-        useCaseConfigFactory: UseCaseConfigFactory = this.useCaseConfigFactory!!
-    ): List<Size?> {
-        // Converts the use case config to new ResolutionSelector config
-        val useCaseToConfigMap = Configs.useCaseConfigMapWithDefaultSettingsFromUseCaseList(
-            cameraFactory!!.getCamera(cameraId).cameraInfoInternal,
-            listOf(useCase),
-            useCaseConfigFactory
-        )
-        return supportedSurfaceCombination.getSupportedOutputSizes(useCaseToConfigMap[useCase]!!)
-    }
-
-    /**
-     * Creates [Preview], [ImageCapture], [ImageAnalysis] or FakeUseCase according to the specified
-     * settings.
-     *
-     * @param useCaseType Which of [Preview], [ImageCapture], [ImageAnalysis] and FakeUseCase should
-     * be created.
-     * @param targetRotation the target rotation setting. Default is UNKNOWN_ROTATION and no target
-     * rotation will be set to the created use case.
-     * @param targetAspectRatio the target aspect ratio setting. Default is UNKNOWN_ASPECT_RATIO
-     * and no target aspect ratio will be set to the created use case.
-     * @param targetResolution the target resolution setting which should still be specified in the
-     * legacy API approach. The size should be expressed in the coordinate frame after rotating the
-     * supported sizes by the target rotation. Default is null.
-     * @param maxResolution the max resolution setting. Default is null.
-     * @param defaultResolution the default resolution setting. Default is null.
-     * @param supportedResolutions the customized supported resolutions. Default is null.
-     * @param customOrderedResolutions the custom ordered resolutions. Default is null.
-     */
-    private fun createUseCaseByLegacyApi(
-        useCaseType: Int,
-        targetRotation: Int = UNKNOWN_ROTATION,
-        targetAspectRatio: Int = UNKNOWN_ASPECT_RATIO,
-        targetResolution: Size? = null,
-        targetFrameRate: Range<Int>? = null,
-        surfaceOccupancyPriority: Int = -1,
-        maxResolution: Size? = null,
-        defaultResolution: Size? = null,
-        supportedResolutions: List<Pair<Int, Array<Size>>>? = null,
-        customOrderedResolutions: List<Size>? = null,
+    private fun createUseCase(
+        captureType: CaptureType,
+        targetFrameRate: Range<Int>? = null
     ): UseCase {
-        val builder = when (useCaseType) {
-            PREVIEW_USE_CASE -> Preview.Builder()
-            IMAGE_CAPTURE_USE_CASE -> ImageCapture.Builder()
-            IMAGE_ANALYSIS_USE_CASE -> ImageAnalysis.Builder()
-            else -> FakeUseCaseConfig.Builder(UseCaseConfigFactory.CaptureType.IMAGE_CAPTURE)
+        val builder = FakeUseCaseConfig.Builder(captureType, when (captureType) {
+            CaptureType.IMAGE_CAPTURE -> ImageFormat.JPEG
+            CaptureType.IMAGE_ANALYSIS -> ImageFormat.YUV_420_888
+            else -> INTERNAL_DEFINED_IMAGE_FORMAT_PRIVATE
+        })
+        targetFrameRate?.let {
+            builder.mutableConfig.insertOption(UseCaseConfig.OPTION_TARGET_FRAME_RATE, it)
         }
-        if (targetRotation != UNKNOWN_ROTATION) {
-            builder.setTargetRotation(targetRotation)
-        }
-        if (targetAspectRatio != UNKNOWN_ASPECT_RATIO) {
-            builder.setTargetAspectRatio(targetAspectRatio)
-        }
-        if (surfaceOccupancyPriority >= 0) {
-            builder.setSurfaceOccupancyPriority(surfaceOccupancyPriority)
-        }
-        builder.mutableConfig.insertOption(OPTION_TARGET_FRAME_RATE, targetFrameRate)
-        targetResolution?.let { builder.setTargetResolution(it) }
-        maxResolution?.let { builder.setMaxResolution(it) }
-        defaultResolution?.let { builder.setDefaultResolution(it) }
-        supportedResolutions?.let { builder.setSupportedResolutions(it) }
-        customOrderedResolutions?.let { builder.setCustomOrderedResolutions(it) }
         return builder.build()
     }
 
-    /** Creates a VideoCapture with a default QualitySelector  */
-    private fun createVideoCapture(): VideoCapture<TestVideoOutput> {
-        return createVideoCapture(VideoSpec.QUALITY_SELECTOR_AUTO)
-    }
-
-    /** Creates a VideoCapture with one ore more specific Quality  */
-    private fun createVideoCapture(vararg quality: Quality): VideoCapture<TestVideoOutput> {
-        return createVideoCapture(QualitySelector.fromOrderedList(listOf(*quality)))
-    }
-
-    /** Creates a VideoCapture with a customized QualitySelector  */
-    private fun createVideoCapture(qualitySelector: QualitySelector):
-        VideoCapture<TestVideoOutput> {
-        val mediaSpec = MediaSpec.builder().configureVideo {
-            it.setQualitySelector(
-                qualitySelector
-            )
-        }.build()
-        val videoOutput = TestVideoOutput()
-        videoOutput.mediaSpecObservable.setState(mediaSpec)
-        return VideoCapture.withOutput(videoOutput)
-    }
-
-    /** A fake implementation of VideoOutput  */
-    private class TestVideoOutput : VideoOutput {
-        var mediaSpecObservable: MutableStateObservable<MediaSpec> =
-            MutableStateObservable.withInitialState(MediaSpec.builder().build())
-        var surfaceRequest: SurfaceRequest? = null
-        var sourceState: SourceState? = null
-        override fun onSurfaceRequested(@NonNull request: SurfaceRequest) {
-            surfaceRequest = request
-        }
-        override fun getMediaSpec() = mediaSpecObservable
-        override fun onSourceStateChanged(@NonNull sourceState: SourceState) {
-            this.sourceState = sourceState
-        }
-    }
-
-    private interface UseCaseCreator {
-        fun createUseCase(
-            useCaseType: Int,
-            targetRotation: Int = UNKNOWN_ROTATION,
-            preferredAspectRatio: Int = UNKNOWN_ASPECT_RATIO,
-            preferredResolution: Size? = null,
-            targetFrameRate: Range<Int>? = null,
-            surfaceOccupancyPriority: Int = -1,
-            maxResolution: Size? = null,
-            highResolutionEnabled: Boolean = false,
-            defaultResolution: Size? = null,
-            supportedResolutions: List<Pair<Int, Array<Size>>>? = null,
-            customOrderedResolutions: List<Size>? = null,
-        ): UseCase
+    private fun createRawUseCase(): UseCase {
+        val builder = FakeUseCaseConfig.Builder()
+        builder.mutableConfig.insertOption(
+            UseCaseConfig.OPTION_INPUT_FORMAT,
+            ImageFormat.RAW_SENSOR
+        )
+        return builder.build()
     }
 }
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/impl/CameraDeviceSurfaceManager.java b/camera/camera-core/src/main/java/androidx/camera/core/impl/CameraDeviceSurfaceManager.java
index 66e9cf6..48bd32c 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/impl/CameraDeviceSurfaceManager.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/impl/CameraDeviceSurfaceManager.java
@@ -87,12 +87,16 @@
     /**
      * Retrieves a map of suggested stream specifications for the given list of use cases.
      *
-     * @param isConcurrentCameraModeOn true if concurrent camera mode is on, otherwise false.
-     * @param cameraId          the camera id of the camera device used by the use cases
-     * @param existingSurfaces  list of surfaces already configured and used by the camera. The
-     *                          stream specifications for these surface can not change.
-     * @param newUseCaseConfigs list of configurations of the use cases that will be given a
-     *                          suggested stream specification
+     * @param isConcurrentCameraModeOn          true if concurrent camera mode is on, otherwise
+     *                                          false.
+     * @param cameraId                          the camera id of the camera device used by the
+     *                                          use cases
+     * @param existingSurfaces                  list of surfaces already configured and used by
+     *                                          the camera. The stream specifications for these
+     *                                          surface can not change.
+     * @param newUseCaseConfigsSupportedSizeMap map of configurations of the use cases to the
+     *                                          supported output sizes list that will be given a
+     *                                          suggested stream specification
      * @return map of suggested stream specifications for given use cases
      * @throws IllegalStateException    if not initialized
      * @throws IllegalArgumentException if {@code newUseCaseConfigs} is an empty list, if
@@ -105,5 +109,5 @@
             boolean isConcurrentCameraModeOn,
             @NonNull String cameraId,
             @NonNull List<AttachedSurfaceInfo> existingSurfaces,
-            @NonNull List<UseCaseConfig<?>> newUseCaseConfigs);
+            @NonNull Map<UseCaseConfig<?>, List<Size>> newUseCaseConfigsSupportedSizeMap);
 }
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/internal/CameraUseCaseAdapter.java b/camera/camera-core/src/main/java/androidx/camera/core/internal/CameraUseCaseAdapter.java
index 789f991..37db291 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/internal/CameraUseCaseAdapter.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/internal/CameraUseCaseAdapter.java
@@ -17,6 +17,7 @@
 package androidx.camera.core.internal;
 
 import static androidx.camera.core.impl.ImageFormatConstants.INTERNAL_DEFINED_IMAGE_FORMAT_PRIVATE;
+import static androidx.camera.core.impl.utils.TransformUtils.rectToSize;
 import static androidx.core.util.Preconditions.checkArgument;
 import static androidx.core.util.Preconditions.checkState;
 
@@ -525,9 +526,14 @@
             suggestedStreamSpecs.put(useCase, useCase.getAttachedStreamSpec());
         }
 
+        Rect sensorRect = ((CameraControlInternal) getCameraControl()).getSensorRect();
+        SupportedOutputSizesSorter supportedOutputSizesSorter = new SupportedOutputSizesSorter(
+                (CameraInfoInternal) getCameraInfo(), rectToSize(sensorRect));
+
         // Calculate resolution for new use cases.
         if (!newUseCases.isEmpty()) {
             Map<UseCaseConfig<?>, UseCase> configToUseCaseMap = new HashMap<>();
+            Map<UseCaseConfig<?>, List<Size>> configToSupportedSizesMap = new HashMap<>();
             for (UseCase useCase : newUseCases) {
                 ConfigPair configPair = configPairMap.get(useCase);
                 // Combine with default configuration.
@@ -535,6 +541,9 @@
                         useCase.mergeConfigs(cameraInfoInternal, configPair.mExtendedConfig,
                                 configPair.mCameraConfig);
                 configToUseCaseMap.put(combinedUseCaseConfig, useCase);
+                configToSupportedSizesMap.put(combinedUseCaseConfig,
+                        supportedOutputSizesSorter.getSortedSupportedOutputSizes(
+                                combinedUseCaseConfig));
             }
 
             // Get suggested stream specifications and update the use case session configuration
@@ -542,7 +551,7 @@
                     mCameraDeviceSurfaceManager.getSuggestedStreamSpecs(
                             isConcurrentCameraModeOn,
                             cameraId, existingSurfaces,
-                            new ArrayList<>(configToUseCaseMap.keySet()));
+                            configToSupportedSizesMap);
 
             for (Map.Entry<UseCaseConfig<?>, UseCase> entry : configToUseCaseMap.entrySet()) {
                 suggestedStreamSpecs.put(entry.getValue(),
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/internal/SupportedOutputSizesSorter.java b/camera/camera-core/src/main/java/androidx/camera/core/internal/SupportedOutputSizesSorter.java
new file mode 100644
index 0000000..b8e80ef
--- /dev/null
+++ b/camera/camera-core/src/main/java/androidx/camera/core/internal/SupportedOutputSizesSorter.java
@@ -0,0 +1,424 @@
+/*
+ * Copyright 2023 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.internal;
+
+import static androidx.camera.core.impl.utils.AspectRatioUtil.ASPECT_RATIO_16_9;
+import static androidx.camera.core.impl.utils.AspectRatioUtil.ASPECT_RATIO_3_4;
+import static androidx.camera.core.impl.utils.AspectRatioUtil.ASPECT_RATIO_4_3;
+import static androidx.camera.core.impl.utils.AspectRatioUtil.ASPECT_RATIO_9_16;
+import static androidx.camera.core.impl.utils.AspectRatioUtil.hasMatchingAspectRatio;
+
+import android.util.Pair;
+import android.util.Rational;
+import android.util.Size;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.RequiresApi;
+import androidx.camera.core.AspectRatio;
+import androidx.camera.core.Logger;
+import androidx.camera.core.ResolutionSelector;
+import androidx.camera.core.impl.CameraInfoInternal;
+import androidx.camera.core.impl.ImageOutputConfig;
+import androidx.camera.core.impl.UseCaseConfig;
+import androidx.camera.core.impl.utils.AspectRatioUtil;
+import androidx.camera.core.impl.utils.CompareSizesByArea;
+import androidx.camera.core.internal.utils.SizeUtil;
+import androidx.core.util.Preconditions;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * A class used to sort the supported output sizes according to the use case configs
+ */
+@RequiresApi(21)
+class SupportedOutputSizesSorter {
+    private static final String TAG = "SupportedOutputSizesCollector";
+    private final CameraInfoInternal mCameraInfoInternal;
+    private final Size mActiveArraySize;
+    private final boolean mIsSensorLandscapeResolution;
+    private final SupportedOutputSizesSorterLegacy mSupportedOutputSizesSorterLegacy;
+
+    SupportedOutputSizesSorter(@NonNull CameraInfoInternal cameraInfoInternal,
+            @NonNull Size activeArraySize) {
+        mCameraInfoInternal = cameraInfoInternal;
+        mActiveArraySize = activeArraySize;
+        mIsSensorLandscapeResolution = mActiveArraySize.getWidth() >= mActiveArraySize.getHeight();
+        mSupportedOutputSizesSorterLegacy =
+                new SupportedOutputSizesSorterLegacy(cameraInfoInternal, activeArraySize);
+    }
+
+    @NonNull
+    List<Size> getSortedSupportedOutputSizes(@NonNull UseCaseConfig<?> useCaseConfig) {
+        ImageOutputConfig imageOutputConfig = (ImageOutputConfig) useCaseConfig;
+        List<Size> customOrderedResolutions = imageOutputConfig.getCustomOrderedResolutions(null);
+
+        // Directly returns the custom ordered resolutions list if it is set.
+        if (customOrderedResolutions != null) {
+            return customOrderedResolutions;
+        }
+
+        // Retrieves the resolution candidate list according to the use case config if
+        List<Size> resolutionCandidateList = getResolutionCandidateList(useCaseConfig);
+
+        ResolutionSelector resolutionSelector = imageOutputConfig.getResolutionSelector(null);
+
+        if (resolutionSelector == null) {
+            return mSupportedOutputSizesSorterLegacy.sortSupportedOutputSizes(
+                    resolutionCandidateList, useCaseConfig);
+        } else {
+            Size miniBoundingSize = resolutionSelector.getPreferredResolution();
+            if (miniBoundingSize == null) {
+                miniBoundingSize = imageOutputConfig.getDefaultResolution(null);
+            }
+            return sortSupportedOutputSizesByResolutionSelector(resolutionCandidateList,
+                    resolutionSelector, miniBoundingSize);
+        }
+    }
+
+    @NonNull
+    private List<Size> getResolutionCandidateList(@NonNull UseCaseConfig<?> useCaseConfig) {
+        int imageFormat = useCaseConfig.getInputFormat();
+        // Tries to get the custom supported resolutions list if it is set
+        List<Size> resolutionCandidateList = getCustomizedSupportedResolutionsFromConfig(
+                imageFormat, (ImageOutputConfig) useCaseConfig);
+
+        // Tries to get the supported output sizes from the CameraInfoInternal if both custom
+        // ordered and supported resolutions lists are not set.
+        if (resolutionCandidateList == null) {
+            resolutionCandidateList = mCameraInfoInternal.getSupportedResolutions(imageFormat);
+        }
+
+        return resolutionCandidateList;
+    }
+
+    /**
+     * Retrieves the customized supported resolutions from the use case config.
+     *
+     * <p>In some cases, the use case might not be able to use all the supported output sizes
+     * retrieved from the stream configuration map. For example, extensions is enabled. These
+     * sizes can be set in the use case config by
+     * {@link ImageOutputConfig.Builder#setSupportedResolutions(List)}. SupportedOutputSizesSorter
+     * should use the customized supported resolutions to run the sort/filter logic if it is set.
+     */
+    @Nullable
+    private List<Size> getCustomizedSupportedResolutionsFromConfig(int imageFormat,
+            @NonNull ImageOutputConfig config) {
+        Size[] outputSizes = null;
+
+        // Try to retrieve customized supported resolutions from config.
+        List<Pair<Integer, Size[]>> formatResolutionsPairList =
+                config.getSupportedResolutions(null);
+
+        if (formatResolutionsPairList != null) {
+            for (Pair<Integer, Size[]> formatResolutionPair : formatResolutionsPairList) {
+                if (formatResolutionPair.first == imageFormat) {
+                    outputSizes = formatResolutionPair.second;
+                    break;
+                }
+            }
+        }
+
+        return outputSizes == null ? null : Arrays.asList(outputSizes);
+    }
+
+    /**
+     * Sorts the resolution candidate list by the following steps:
+     *
+     * 1. Filters out the candidate list according to the max resolution.
+     * 2. Sorts the candidate list according to ResolutionSelector strategies.
+     */
+    @NonNull
+    private List<Size> sortSupportedOutputSizesByResolutionSelector(
+            @NonNull List<Size> resolutionCandidateList,
+            @NonNull ResolutionSelector resolutionSelector,
+            @Nullable Size miniBoundingSize) {
+        if (resolutionCandidateList.isEmpty()) {
+            return resolutionCandidateList;
+        }
+
+        List<Size> descendingSizeList = new ArrayList<>(resolutionCandidateList);
+
+        // Sort the result sizes. The Comparator result must be reversed to have a descending
+        // order result.
+        Collections.sort(descendingSizeList, new CompareSizesByArea(true));
+
+        // 1. Filters out the candidate list according to the min size bound and max resolution.
+        List<Size> filteredSizeList = filterOutResolutionCandidateListByMaxResolutionSetting(
+                descendingSizeList, resolutionSelector);
+
+        // 2. Sorts the candidate list according to the rules of new Resolution API.
+        return sortResolutionCandidateListByTargetAspectRatioAndResolutionSettings(
+                filteredSizeList, resolutionSelector, miniBoundingSize);
+
+    }
+
+    /**
+     * Filters out the resolution candidate list by the max resolution setting.
+     *
+     * The input size list should have been sorted in descending order.
+     */
+    private static List<Size> filterOutResolutionCandidateListByMaxResolutionSetting(
+            @NonNull List<Size> resolutionCandidateList,
+            @NonNull ResolutionSelector resolutionSelector) {
+        // Retrieves the max resolution setting. When ResolutionSelector is used, all resolution
+        // selection logic should depend on ResolutionSelector's settings.
+        Size maxResolution = resolutionSelector.getMaxResolution();
+
+        if (maxResolution == null) {
+            return resolutionCandidateList;
+        }
+
+        // Filter out the resolution candidate list by the max resolution. Sizes that any edge
+        // exceeds the max resolution will be filtered out.
+        List<Size> resultList = new ArrayList<>();
+        for (Size outputSize : resolutionCandidateList) {
+            if (!SizeUtil.isLongerInAnyEdge(outputSize, maxResolution)) {
+                resultList.add(outputSize);
+            }
+        }
+
+        if (resultList.isEmpty()) {
+            throw new IllegalArgumentException(
+                    "Resolution candidate list is empty after filtering out by the settings!");
+        }
+
+        return resultList;
+    }
+
+    /**
+     * Sorts the resolution candidate list according to the new ResolutionSelector API logic.
+     *
+     * The list will be sorted by the following order:
+     * 1. size of preferred resolution
+     * 2. a resolution with preferred aspect ratio, is not smaller than, and is closest to the
+     * preferred resolution.
+     * 3. resolutions with preferred aspect ratio and is smaller than the preferred resolution
+     * size in descending order of resolution area size.
+     * 4. Other sizes sorted by CompareAspectRatiosByMappingAreaInFullFovAspectRatioSpace and
+     * area size.
+     */
+    @NonNull
+    private List<Size> sortResolutionCandidateListByTargetAspectRatioAndResolutionSettings(
+            @NonNull List<Size> resolutionCandidateList,
+            @NonNull ResolutionSelector resolutionSelector,
+            @Nullable Size miniBoundingSize) {
+        Rational aspectRatio = getTargetAspectRatioRationalValue(
+                resolutionSelector.getPreferredAspectRatio(), mIsSensorLandscapeResolution);
+        Preconditions.checkNotNull(aspectRatio, "ResolutionSelector should also have aspect ratio"
+                + " value.");
+
+        Size targetSize = resolutionSelector.getPreferredResolution();
+        List<Size> resultList = sortResolutionCandidateListByTargetAspectRatioAndSize(
+                resolutionCandidateList, aspectRatio, miniBoundingSize);
+
+        // Moves the target size to the first position if it exists in the resolution candidate
+        // list.
+        if (resultList.contains(targetSize)) {
+            resultList.remove(targetSize);
+            resultList.add(0, targetSize);
+        }
+
+        return resultList;
+    }
+
+    /**
+     * Sorts the resolution candidate list according to the target aspect ratio and size settings.
+     *
+     * 1. The resolution candidate list will be grouped by aspect ratio.
+     * 2. Moves the smallest size larger than the mini bounding size to the first position for each
+     * aspect ratio sizes group.
+     * 3. The aspect ratios of groups will be sorted against to the target aspect ratio setting by
+     * CompareAspectRatiosByMappingAreaInFullFovAspectRatioSpace.
+     * 4. Concatenate all sizes as the result list
+     */
+    @NonNull
+    private List<Size> sortResolutionCandidateListByTargetAspectRatioAndSize(
+            @NonNull List<Size> resolutionCandidateList, @NonNull Rational aspectRatio,
+            @Nullable Size miniBoundingSize) {
+        // Rearrange the supported size to put the ones with the same aspect ratio in the front
+        // of the list and put others in the end from large to small. Some low end devices may
+        // not able to get an supported resolution that match the preferred aspect ratio.
+
+        // Group output sizes by aspect ratio.
+        Map<Rational, List<Size>> aspectRatioSizeListMap =
+                groupSizesByAspectRatio(resolutionCandidateList);
+
+        // If the target resolution is set, use it to remove unnecessary larger sizes.
+        if (miniBoundingSize != null) {
+            // Sorts sizes from each aspect ratio size list
+            for (Rational key : aspectRatioSizeListMap.keySet()) {
+                List<Size> sortedResult = sortSupportedSizesByMiniBoundingSize(
+                        aspectRatioSizeListMap.get(key), miniBoundingSize);
+                aspectRatioSizeListMap.put(key, sortedResult);
+            }
+        }
+
+        // Sort the aspect ratio key set by the target aspect ratio.
+        List<Rational> aspectRatios = new ArrayList<>(aspectRatioSizeListMap.keySet());
+        Rational fullFovRatio = mActiveArraySize != null ? new Rational(
+                mActiveArraySize.getWidth(), mActiveArraySize.getHeight()) : null;
+        Collections.sort(aspectRatios,
+                new AspectRatioUtil.CompareAspectRatiosByMappingAreaInFullFovAspectRatioSpace(
+                        aspectRatio, fullFovRatio));
+
+        List<Size> resultList = new ArrayList<>();
+
+        // Put available sizes into final result list by aspect ratio distance to target ratio.
+        for (Rational rational : aspectRatios) {
+            for (Size size : aspectRatioSizeListMap.get(rational)) {
+                // A size may exist in multiple groups in mod16 condition. Keep only one in
+                // the final list.
+                if (!resultList.contains(size)) {
+                    resultList.add(size);
+                }
+            }
+        }
+
+        return resultList;
+    }
+
+    /**
+     * Returns the target aspect ratio rational value according to the ResolutionSelector settings.
+     */
+    @Nullable
+    static Rational getTargetAspectRatioRationalValue(@AspectRatio.Ratio int aspectRatio,
+            boolean isSensorLandscapeResolution) {
+        Rational outputRatio = null;
+
+        switch (aspectRatio) {
+            case AspectRatio.RATIO_4_3:
+                outputRatio = isSensorLandscapeResolution ? ASPECT_RATIO_4_3
+                        : ASPECT_RATIO_3_4;
+                break;
+            case AspectRatio.RATIO_16_9:
+                outputRatio = isSensorLandscapeResolution ? ASPECT_RATIO_16_9
+                        : ASPECT_RATIO_9_16;
+                break;
+            case AspectRatio.RATIO_DEFAULT:
+                break;
+            default:
+                Logger.e(TAG, "Undefined target aspect ratio: " + aspectRatio);
+        }
+
+        return outputRatio;
+    }
+
+    /**
+     * Returns the grouping aspect ratio keys of the input resolution list.
+     *
+     * <p>Some sizes might be mod16 case. When grouping, those sizes will be grouped into an
+     * existing aspect ratio group if the aspect ratio can match by the mod16 rule.
+     */
+    @NonNull
+    static List<Rational> getResolutionListGroupingAspectRatioKeys(
+            @NonNull List<Size> resolutionCandidateList) {
+        List<Rational> aspectRatios = new ArrayList<>();
+
+        // Adds the default 4:3 and 16:9 items first to avoid their mod16 sizes to create
+        // additional items.
+        aspectRatios.add(ASPECT_RATIO_4_3);
+        aspectRatios.add(ASPECT_RATIO_16_9);
+
+        // Tries to find the aspect ratio which the target size belongs to.
+        for (Size size : resolutionCandidateList) {
+            Rational newRatio = new Rational(size.getWidth(), size.getHeight());
+            boolean aspectRatioFound = aspectRatios.contains(newRatio);
+
+            // The checking size might be a mod16 size which can be mapped to an existing aspect
+            // ratio group.
+            if (!aspectRatioFound) {
+                boolean hasMatchingAspectRatio = false;
+                for (Rational aspectRatio : aspectRatios) {
+                    if (hasMatchingAspectRatio(size, aspectRatio)) {
+                        hasMatchingAspectRatio = true;
+                        break;
+                    }
+                }
+                if (!hasMatchingAspectRatio) {
+                    aspectRatios.add(newRatio);
+                }
+            }
+        }
+
+        return aspectRatios;
+    }
+
+    /**
+     * Removes unnecessary sizes by target size.
+     *
+     * <p>If the target resolution is set, a size that is equal to or closest to the target
+     * resolution will be selected. If the list includes more than one size equal to or larger
+     * than the target resolution, only one closest size needs to be kept. The other larger sizes
+     * can be removed so that they won't be selected to use.
+     *
+     * @param supportedSizesList The list should have been sorted in descending order.
+     * @param miniBoundingSize   The target size used to remove unnecessary sizes.
+     */
+    static List<Size> sortSupportedSizesByMiniBoundingSize(@NonNull List<Size> supportedSizesList,
+            @NonNull Size miniBoundingSize) {
+        if (supportedSizesList.isEmpty()) {
+            return supportedSizesList;
+        }
+
+        List<Size> resultList = new ArrayList<>();
+
+        // Get the index of the item that is equal to or closest to the target size.
+        for (int i = 0; i < supportedSizesList.size(); i++) {
+            Size outputSize = supportedSizesList.get(i);
+            if (outputSize.getWidth() >= miniBoundingSize.getWidth()
+                    && outputSize.getHeight() >= miniBoundingSize.getHeight()) {
+                // The supportedSizesList is in descending order. Checking and put the
+                // mini-bounding-above size at position 0 so that the smallest larger resolution
+                // will be put in the first position finally.
+                resultList.add(0, outputSize);
+            } else {
+                // Appends the remaining smaller sizes in descending order.
+                resultList.add(outputSize);
+            }
+        }
+
+        return resultList;
+    }
+
+    static Map<Rational, List<Size>> groupSizesByAspectRatio(@NonNull List<Size> sizes) {
+        Map<Rational, List<Size>> aspectRatioSizeListMap = new HashMap<>();
+
+        List<Rational> aspectRatioKeys = getResolutionListGroupingAspectRatioKeys(sizes);
+
+        for (Rational aspectRatio : aspectRatioKeys) {
+            aspectRatioSizeListMap.put(aspectRatio, new ArrayList<>());
+        }
+
+        for (Size outputSize : sizes) {
+            for (Rational key : aspectRatioSizeListMap.keySet()) {
+                // Put the size into all groups that is matched in mod16 condition since a size
+                // may match multiple aspect ratio in mod16 algorithm.
+                if (hasMatchingAspectRatio(outputSize, key)) {
+                    aspectRatioSizeListMap.get(key).add(outputSize);
+                }
+            }
+        }
+
+        return aspectRatioSizeListMap;
+    }
+}
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/internal/SupportedOutputSizesSorterLegacy.java b/camera/camera-core/src/main/java/androidx/camera/core/internal/SupportedOutputSizesSorterLegacy.java
new file mode 100644
index 0000000..674a6a4
--- /dev/null
+++ b/camera/camera-core/src/main/java/androidx/camera/core/internal/SupportedOutputSizesSorterLegacy.java
@@ -0,0 +1,275 @@
+/*
+ * Copyright 2023 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.internal;
+
+import static androidx.camera.core.impl.utils.AspectRatioUtil.hasMatchingAspectRatio;
+import static androidx.camera.core.internal.SupportedOutputSizesSorter.getResolutionListGroupingAspectRatioKeys;
+import static androidx.camera.core.internal.SupportedOutputSizesSorter.groupSizesByAspectRatio;
+import static androidx.camera.core.internal.SupportedOutputSizesSorter.sortSupportedSizesByMiniBoundingSize;
+import static androidx.camera.core.internal.utils.SizeUtil.RESOLUTION_VGA;
+import static androidx.camera.core.internal.utils.SizeUtil.RESOLUTION_ZERO;
+import static androidx.camera.core.internal.utils.SizeUtil.getArea;
+
+import android.hardware.camera2.CameraCharacteristics;
+import android.util.Rational;
+import android.util.Size;
+import android.view.Surface;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.RequiresApi;
+import androidx.camera.core.AspectRatio;
+import androidx.camera.core.impl.CameraInfoInternal;
+import androidx.camera.core.impl.ImageOutputConfig;
+import androidx.camera.core.impl.UseCaseConfig;
+import androidx.camera.core.impl.utils.AspectRatioUtil;
+import androidx.camera.core.impl.utils.CameraOrientationUtil;
+import androidx.camera.core.impl.utils.CompareSizesByArea;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * A class used to sort the supported output sizes according to the legacy use case configs
+ */
+@RequiresApi(21)
+class SupportedOutputSizesSorterLegacy {
+    private static final String TAG = "SupportedOutputSizesCollector";
+    private final int mSensorOrientation;
+    private final int mLensFacing;
+    private final Size mActiveArraySize;
+    private final boolean mIsSensorLandscapeResolution;
+
+    SupportedOutputSizesSorterLegacy(@NonNull CameraInfoInternal cameraInfoInternal,
+            @NonNull Size activeArraySize) {
+        mSensorOrientation = cameraInfoInternal.getSensorRotationDegrees();
+        mLensFacing = cameraInfoInternal.getLensFacing();
+        mActiveArraySize = activeArraySize;
+        mIsSensorLandscapeResolution = mActiveArraySize.getWidth() >= mActiveArraySize.getHeight();
+    }
+
+    /**
+     * Sorts the resolution candidate list by the following steps:
+     *
+     * 1. Filters out the candidate list according to the mini and max resolution.
+     * 2. Sorts the candidate list according to legacy target aspect ratio or resolution settings.
+     */
+    @NonNull
+    List<Size> sortSupportedOutputSizes(
+            @NonNull List<Size> resolutionCandidateList,
+            @NonNull UseCaseConfig<?> useCaseConfig) {
+        if (resolutionCandidateList.isEmpty()) {
+            return resolutionCandidateList;
+        }
+
+        List<Size> descendingSizeList = new ArrayList<>(resolutionCandidateList);
+
+        // Sort the result sizes. The Comparator result must be reversed to have a descending
+        // order result.
+        Collections.sort(descendingSizeList, new CompareSizesByArea(true));
+
+        List<Size> filteredSizeList = new ArrayList<>();
+        ImageOutputConfig imageOutputConfig = (ImageOutputConfig) useCaseConfig;
+        Size maxSize = imageOutputConfig.getMaxResolution(null);
+        Size maxSupportedOutputSize = descendingSizeList.get(0);
+
+        // Set maxSize as the max resolution setting or the max supported output size for the
+        // image format, whichever is smaller.
+        if (maxSize == null || getArea(maxSupportedOutputSize) < getArea(maxSize)) {
+            maxSize = maxSupportedOutputSize;
+        }
+
+        Size targetSize = getTargetSize(imageOutputConfig);
+        Size minSize = RESOLUTION_VGA;
+        int defaultSizeArea = getArea(RESOLUTION_VGA);
+        int maxSizeArea = getArea(maxSize);
+        // When maxSize is smaller than 640x480, set minSize as 0x0. It means the min size bound
+        // will be ignored. Otherwise, set the minimal size according to min(DEFAULT_SIZE,
+        // TARGET_RESOLUTION).
+        if (maxSizeArea < defaultSizeArea) {
+            minSize = RESOLUTION_ZERO;
+        } else if (targetSize != null && getArea(targetSize) < defaultSizeArea) {
+            minSize = targetSize;
+        }
+
+        // Filter out the ones that exceed the maximum size and the minimum size. The output
+        // sizes candidates list won't have duplicated items.
+        for (Size outputSize : descendingSizeList) {
+            if (getArea(outputSize) <= getArea(maxSize) && getArea(outputSize) >= getArea(minSize)
+                    && !filteredSizeList.contains(outputSize)) {
+                filteredSizeList.add(outputSize);
+            }
+        }
+
+        if (filteredSizeList.isEmpty()) {
+            throw new IllegalArgumentException(
+                    "All supported output sizes are filtered out according to current resolution "
+                            + "selection settings.");
+        }
+
+        Rational aspectRatio = getTargetAspectRatioByLegacyApi(imageOutputConfig, filteredSizeList);
+
+        // Check the default resolution if the target resolution is not set
+        targetSize = targetSize == null ? imageOutputConfig.getDefaultResolution(null) : targetSize;
+
+        List<Size> resultSizeList = new ArrayList<>();
+        Map<Rational, List<Size>> aspectRatioSizeListMap = new HashMap<>();
+
+        if (aspectRatio == null) {
+            // If no target aspect ratio is set, all sizes can be added to the result list
+            // directly. No need to sort again since the source list has been sorted previously.
+            resultSizeList.addAll(filteredSizeList);
+
+            // If the target resolution is set, use it to sort the sizes list.
+            if (targetSize != null) {
+                resultSizeList = sortSupportedSizesByMiniBoundingSize(resultSizeList, targetSize);
+            }
+        } else {
+            // Rearrange the supported size to put the ones with the same aspect ratio in the front
+            // of the list and put others in the end from large to small. Some low end devices may
+            // not able to get an supported resolution that match the preferred aspect ratio.
+
+            // Group output sizes by aspect ratio.
+            aspectRatioSizeListMap = groupSizesByAspectRatio(filteredSizeList);
+
+            // If the target resolution is set, use it to remove unnecessary larger sizes.
+            if (targetSize != null) {
+                // Remove unnecessary larger sizes from each aspect ratio size list
+                for (Rational key : aspectRatioSizeListMap.keySet()) {
+                    List<Size> sortedResult = sortSupportedSizesByMiniBoundingSize(
+                            aspectRatioSizeListMap.get(key), targetSize);
+                    aspectRatioSizeListMap.put(key, sortedResult);
+                }
+            }
+
+            // Sort the aspect ratio key set by the target aspect ratio.
+            List<Rational> aspectRatios = new ArrayList<>(aspectRatioSizeListMap.keySet());
+            Rational fullFovRatio = mActiveArraySize != null ? new Rational(
+                    mActiveArraySize.getWidth(), mActiveArraySize.getHeight()) : null;
+            Collections.sort(aspectRatios,
+                    new AspectRatioUtil.CompareAspectRatiosByMappingAreaInFullFovAspectRatioSpace(
+                            aspectRatio, fullFovRatio));
+
+            // Put available sizes into final result list by aspect ratio distance to target ratio.
+            for (Rational rational : aspectRatios) {
+                for (Size size : aspectRatioSizeListMap.get(rational)) {
+                    // A size may exist in multiple groups in mod16 condition. Keep only one in
+                    // the final list.
+                    if (!resultSizeList.contains(size)) {
+                        resultSizeList.add(size);
+                    }
+                }
+            }
+        }
+
+        return resultSizeList;
+    }
+
+    /**
+     * Returns the target aspect ratio rational value according to the legacy API settings.
+     */
+    private Rational getTargetAspectRatioByLegacyApi(@NonNull ImageOutputConfig imageOutputConfig,
+            @NonNull List<Size> resolutionCandidateList) {
+        Rational outputRatio = null;
+
+        if (imageOutputConfig.hasTargetAspectRatio()) {
+            @AspectRatio.Ratio int aspectRatio = imageOutputConfig.getTargetAspectRatio();
+            outputRatio = SupportedOutputSizesSorter.getTargetAspectRatioRationalValue(aspectRatio,
+                    mIsSensorLandscapeResolution);
+        } else {
+            // The legacy resolution API will use the aspect ratio of the target size to
+            // be the fallback target aspect ratio value when the use case has no target
+            // aspect ratio setting.
+            Size targetSize = getTargetSize(imageOutputConfig);
+            if (targetSize != null) {
+                outputRatio = getAspectRatioGroupKeyOfTargetSize(targetSize,
+                        resolutionCandidateList);
+            }
+        }
+
+        return outputRatio;
+    }
+
+    @Nullable
+    private Size getTargetSize(@NonNull ImageOutputConfig imageOutputConfig) {
+        int targetRotation = imageOutputConfig.getTargetRotation(Surface.ROTATION_0);
+        // Calibrate targetSize by the target rotation value.
+        Size targetSize = imageOutputConfig.getTargetResolution(null);
+        targetSize = flipSizeByRotation(targetSize, targetRotation, mLensFacing,
+                mSensorOrientation);
+        return targetSize;
+    }
+
+    /**
+     * Returns the aspect ratio group key of the target size when grouping the input resolution
+     * candidate list.
+     *
+     * The resolution candidate list will be grouped with mod 16 consideration. Therefore, we
+     * also need to consider the mod 16 factor to find which aspect ratio of group the target size
+     * might be put in. So that sizes of the group will be selected to use in the highest priority.
+     */
+    @Nullable
+    private static Rational getAspectRatioGroupKeyOfTargetSize(@Nullable Size targetSize,
+            @NonNull List<Size> resolutionCandidateList) {
+        if (targetSize == null) {
+            return null;
+        }
+
+        List<Rational> aspectRatios = getResolutionListGroupingAspectRatioKeys(
+                resolutionCandidateList);
+
+        for (Rational aspectRatio : aspectRatios) {
+            if (hasMatchingAspectRatio(targetSize, aspectRatio)) {
+                return aspectRatio;
+            }
+        }
+
+        return new Rational(targetSize.getWidth(), targetSize.getHeight());
+    }
+
+    // Use target rotation to calibrate the size.
+    @Nullable
+    private static Size flipSizeByRotation(@Nullable Size size, int targetRotation, int lensFacing,
+            int sensorOrientation) {
+        Size outputSize = size;
+        // Calibrates the size with the display and sensor rotation degrees values.
+        if (size != null && isRotationNeeded(targetRotation, lensFacing, sensorOrientation)) {
+            outputSize = new Size(/* width= */size.getHeight(), /* height= */size.getWidth());
+        }
+        return outputSize;
+    }
+
+    private static boolean isRotationNeeded(int targetRotation, int lensFacing,
+            int sensorOrientation) {
+        int relativeRotationDegrees =
+                CameraOrientationUtil.surfaceRotationToDegrees(targetRotation);
+
+        // Currently this assumes that a back-facing camera is always opposite to the screen.
+        // This may not be the case for all devices, so in the future we may need to handle that
+        // scenario.
+        boolean isOppositeFacingScreen = CameraCharacteristics.LENS_FACING_BACK == lensFacing;
+
+        int sensorRotationDegrees = CameraOrientationUtil.getRelativeImageRotation(
+                relativeRotationDegrees,
+                sensorOrientation,
+                isOppositeFacingScreen);
+        return sensorRotationDegrees == 90 || sensorRotationDegrees == 270;
+    }
+}
diff --git a/camera/camera-core/src/test/java/androidx/camera/core/internal/SupportedOutputSizesSorterLegacyTest.kt b/camera/camera-core/src/test/java/androidx/camera/core/internal/SupportedOutputSizesSorterLegacyTest.kt
new file mode 100644
index 0000000..1304995
--- /dev/null
+++ b/camera/camera-core/src/test/java/androidx/camera/core/internal/SupportedOutputSizesSorterLegacyTest.kt
@@ -0,0 +1,390 @@
+/*
+ * Copyright 2023 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.internal
+
+import android.graphics.ImageFormat
+import android.os.Build
+import android.util.Size
+import android.view.Surface
+import androidx.camera.core.AspectRatio
+import androidx.camera.core.impl.UseCaseConfig
+import androidx.camera.core.impl.UseCaseConfigFactory.CaptureType
+import androidx.camera.core.impl.utils.CompareSizesByArea
+import androidx.camera.testing.fakes.FakeCameraInfoInternal
+import androidx.camera.testing.fakes.FakeUseCaseConfig
+import com.google.common.truth.Truth.assertThat
+import java.util.Arrays
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.robolectric.RobolectricTestRunner
+import org.robolectric.annotation.internal.DoNotInstrument
+
+private const val TARGET_ASPECT_RATIO_NONE = -99
+private val LANDSCAPE_ACTIVE_ARRAY_SIZE = Size(4032, 3024)
+private val DEFAULT_SUPPORTED_SIZES = listOf(
+    Size(4032, 3024), // 4:3
+    Size(3840, 2160), // 16:9
+    Size(1920, 1440), // 4:3
+    Size(1920, 1080), // 16:9
+    Size(1280, 960), // 4:3
+    Size(1280, 720), // 16:9
+    Size(960, 544), // a mod16 version of resolution with 16:9 aspect ratio.
+    Size(800, 450), // 16:9
+    Size(640, 480), // 4:3
+    Size(320, 240), // 4:3
+    Size(320, 180), // 16:9
+    Size(256, 144) // 16:9
+)
+
+/**
+ * Unit tests for [SupportedOutputSizesSorterLegacy].
+ */
+@RunWith(RobolectricTestRunner::class)
+@DoNotInstrument
+@org.robolectric.annotation.Config(minSdk = Build.VERSION_CODES.LOLLIPOP)
+class SupportedOutputSizesSorterLegacyTest {
+    private val cameraInfoInternal = FakeCameraInfoInternal()
+    private val supportedOutputSizesSorterLegacy =
+        SupportedOutputSizesSorterLegacy(cameraInfoInternal, LANDSCAPE_ACTIVE_ARRAY_SIZE)
+
+    @Test
+    fun checkFilterOutSmallSizesByDefaultSize640x480() {
+        val resultList = supportedOutputSizesSorterLegacy.sortSupportedOutputSizes(
+            DEFAULT_SUPPORTED_SIZES,
+            createUseCaseConfig()
+        )
+        // Sizes smaller than 640x480 are filtered out by default
+        assertThat(resultList).containsNoneIn(
+            arrayOf(
+                Size(320, 240),
+                Size(320, 180),
+                Size(256, 144)
+            )
+        )
+    }
+
+    @Test
+    fun canPrioritize4x3SizesByTargetAspectRatio() {
+        val useCaseConfig = createUseCaseConfig(targetAspectRatio = AspectRatio.RATIO_4_3)
+        val resultList = supportedOutputSizesSorterLegacy.sortSupportedOutputSizes(
+            DEFAULT_SUPPORTED_SIZES,
+            useCaseConfig
+        )
+        // 4:3 items are prioritized in the result list
+        assertThat(resultList).containsExactlyElementsIn(
+            arrayOf(
+                // 4:3 items
+                Size(4032, 3024),
+                Size(1920, 1440),
+                Size(1280, 960),
+                Size(640, 480),
+                // 16:9 items
+                Size(3840, 2160),
+                Size(1920, 1080),
+                Size(1280, 720),
+                Size(960, 544),
+                Size(800, 450),
+            )
+        ).inOrder()
+    }
+
+    @Test
+    fun canPrioritize16x9SizesByTargetAspectRatio() {
+        val useCaseConfig = createUseCaseConfig(targetAspectRatio = AspectRatio.RATIO_16_9)
+        val resultList = supportedOutputSizesSorterLegacy.sortSupportedOutputSizes(
+            DEFAULT_SUPPORTED_SIZES,
+            useCaseConfig
+        )
+        // 16:9 items are prioritized in the result list
+        assertThat(resultList).containsExactlyElementsIn(
+            arrayOf(
+                // 16:9 items
+                Size(3840, 2160),
+                Size(1920, 1080),
+                Size(1280, 720),
+                Size(960, 544),
+                Size(800, 450),
+                // 4:3 items
+                Size(4032, 3024),
+                Size(1920, 1440),
+                Size(1280, 960),
+                Size(640, 480),
+            )
+        ).inOrder()
+    }
+
+    @Test
+    fun canPrioritize4x3SizesByTargetResolution() {
+        val useCaseConfig = createUseCaseConfig(targetResolution = Size(1280, 960))
+        val resultList = supportedOutputSizesSorterLegacy.sortSupportedOutputSizes(
+            DEFAULT_SUPPORTED_SIZES,
+            useCaseConfig
+        )
+        // 4:3 items are prioritized in the result list
+        assertThat(resultList).containsExactlyElementsIn(
+            arrayOf(
+                // 4:3 items
+                Size(1280, 960),
+                Size(1920, 1440),
+                Size(4032, 3024),
+                Size(640, 480),
+                // 16:9 items
+                Size(1920, 1080),
+                Size(3840, 2160),
+                Size(1280, 720),
+                Size(960, 544),
+                Size(800, 450),
+            )
+        ).inOrder()
+    }
+
+    @Test
+    fun canPrioritize16x9SizesByTargetResolution() {
+        val useCaseConfig = createUseCaseConfig(targetResolution = Size(1920, 1080))
+        val resultList = supportedOutputSizesSorterLegacy.sortSupportedOutputSizes(
+            DEFAULT_SUPPORTED_SIZES,
+            useCaseConfig
+        )
+        // 16:9 items are prioritized in the result list
+        assertThat(resultList).containsExactlyElementsIn(
+            arrayOf(
+                // 16:9 items
+                Size(1920, 1080),
+                Size(3840, 2160),
+                Size(1280, 720),
+                Size(960, 544),
+                Size(800, 450),
+                // 4:3 items
+                Size(1920, 1440),
+                Size(4032, 3024),
+                Size(1280, 960),
+                Size(640, 480),
+            )
+        ).inOrder()
+    }
+
+    @Test
+    fun canSelectSmallSizesByTargetResolutionSmallerThan640x480() {
+        val useCaseConfig = createUseCaseConfig(targetResolution = Size(320, 240))
+        val resultList = supportedOutputSizesSorterLegacy.sortSupportedOutputSizes(
+            DEFAULT_SUPPORTED_SIZES,
+            useCaseConfig
+        )
+        // 16:9 items are prioritized in the result list
+        assertThat(resultList).containsExactlyElementsIn(
+            arrayOf(
+                // 4:3 items
+                Size(320, 240),
+                Size(640, 480),
+                Size(1280, 960),
+                Size(1920, 1440),
+                Size(4032, 3024),
+                // 16:9 items
+                Size(800, 450),
+                Size(960, 544),
+                Size(1280, 720),
+                Size(1920, 1080),
+                Size(3840, 2160),
+            )
+        ).inOrder()
+    }
+
+    @Test
+    fun canPrioritizeSizesByResolutionOfUncommonAspectRatio() {
+        val useCaseConfig = createUseCaseConfig(targetResolution = Size(1280, 640))
+        val resultList = supportedOutputSizesSorterLegacy.sortSupportedOutputSizes(
+            DEFAULT_SUPPORTED_SIZES,
+            useCaseConfig
+        )
+        // Sets target resolution as 1280x640, all supported resolutions will be put into aspect
+        // ratio not matched list. Then, 1280x720 will be the nearest matched one.
+        assertThat(resultList).containsExactlyElementsIn(
+            arrayOf(
+                // 16:9 items
+                Size(1280, 720),
+                Size(1920, 1080),
+                Size(3840, 2160),
+                Size(960, 544),
+                Size(800, 450),
+                // 4:3 items
+                Size(1280, 960),
+                Size(1920, 1440),
+                Size(4032, 3024),
+                Size(640, 480),
+            )
+        ).inOrder()
+    }
+
+    @Test
+    fun checkFilterOutLargeSizesByMaxResolution() {
+        val useCaseConfig = createUseCaseConfig(maxResolution = Size(1920, 1080))
+        val resultList = supportedOutputSizesSorterLegacy.sortSupportedOutputSizes(
+            DEFAULT_SUPPORTED_SIZES,
+            useCaseConfig
+        )
+        assertThat(resultList).containsExactlyElementsIn(
+            arrayOf(
+                Size(1920, 1080),
+                Size(1280, 960),
+                Size(1280, 720),
+                Size(960, 544),
+                Size(800, 450),
+                Size(640, 480),
+            )
+        ).inOrder()
+    }
+
+    @Test
+    fun canKeepSmallSizesBySmallMaxResolution() {
+        val useCaseConfig = createUseCaseConfig(maxResolution = Size(320, 240))
+        val resultList = supportedOutputSizesSorterLegacy.sortSupportedOutputSizes(
+            DEFAULT_SUPPORTED_SIZES,
+            useCaseConfig
+        )
+        assertThat(resultList).containsExactlyElementsIn(
+            arrayOf(
+                Size(320, 240),
+                Size(320, 180),
+                Size(256, 144),
+            )
+        ).inOrder()
+    }
+
+    @Test
+    fun canSelectCorrect4x3SizeByDefaultResolution() {
+        val useCaseConfig = createUseCaseConfig(
+            targetAspectRatio = AspectRatio.RATIO_4_3,
+            defaultResolution = Size(640, 480)
+        )
+        val resultList = supportedOutputSizesSorterLegacy.sortSupportedOutputSizes(
+            DEFAULT_SUPPORTED_SIZES,
+            useCaseConfig
+        )
+        // The default resolution 640x480 will be used as target resolution to filter out too-large
+        // items in each aspect ratio group
+        assertThat(resultList).containsExactlyElementsIn(
+            arrayOf(
+                // 4:3 items
+                Size(640, 480),
+                Size(1280, 960),
+                Size(1920, 1440),
+                Size(4032, 3024),
+                // 16:9 items
+                Size(960, 544),
+                Size(1280, 720),
+                Size(1920, 1080),
+                Size(3840, 2160),
+                Size(800, 450),
+            )
+        ).inOrder()
+    }
+
+    @Test
+    fun canSelectCorrect16x9SizeByDefaultResolution() {
+        val useCaseConfig = createUseCaseConfig(
+            targetAspectRatio = AspectRatio.RATIO_16_9,
+            defaultResolution = Size(640, 480)
+        )
+        val resultList = supportedOutputSizesSorterLegacy.sortSupportedOutputSizes(
+            DEFAULT_SUPPORTED_SIZES,
+            useCaseConfig
+        )
+        // The default resolution 640x480 will be used as target resolution to filter out too-large
+        // items in each aspect ratio group
+        assertThat(resultList).containsExactlyElementsIn(
+            arrayOf(
+                // 16:9 items
+                Size(960, 544),
+                Size(1280, 720),
+                Size(1920, 1080),
+                Size(3840, 2160),
+                Size(800, 450),
+                // 4:3 items
+                Size(640, 480),
+                Size(1280, 960),
+                Size(1920, 1440),
+                Size(4032, 3024),
+            )
+        ).inOrder()
+    }
+
+    @Test
+    fun targetResolutionCanOverrideDefaultResolution() {
+        val useCaseConfig = createUseCaseConfig(
+            targetResolution = Size(1920, 1080),
+            defaultResolution = Size(640, 480)
+        )
+        val resultList = supportedOutputSizesSorterLegacy.sortSupportedOutputSizes(
+            DEFAULT_SUPPORTED_SIZES,
+            useCaseConfig
+        )
+        // The default resolution 640x480 will be used as target resolution to filter out too-large
+        // items in each aspect ratio group
+        assertThat(resultList).containsExactlyElementsIn(
+            arrayOf(
+                // 16:9 items
+                Size(1920, 1080),
+                Size(3840, 2160),
+                Size(1280, 720),
+                Size(960, 544),
+                Size(800, 450),
+                // 4:3 items
+                Size(1920, 1440),
+                Size(4032, 3024),
+                Size(1280, 960),
+                Size(640, 480),
+            )
+        ).inOrder()
+    }
+
+    @Test
+    fun sortByCompareSizesByArea_canSortSizesCorrectly() {
+        val sizes = arrayOfNulls<Size>(DEFAULT_SUPPORTED_SIZES.size)
+        // Generates a unsorted array from mSupportedSizes.
+        val centerIndex = DEFAULT_SUPPORTED_SIZES.size / 2
+        // Puts 2nd half sizes in the front
+        for (i in centerIndex until DEFAULT_SUPPORTED_SIZES.size) {
+            sizes[i - centerIndex] = DEFAULT_SUPPORTED_SIZES[i]
+        }
+        // Puts 1st half sizes inversely in the tail
+        for (j in centerIndex - 1 downTo 0) {
+            sizes[DEFAULT_SUPPORTED_SIZES.size - j - 1] = DEFAULT_SUPPORTED_SIZES[j]
+        }
+        // The testing sizes array will be equal to mSupportedSizes after sorting.
+        Arrays.sort(sizes, CompareSizesByArea(true))
+        assertThat(sizes.toList()).containsExactlyElementsIn(DEFAULT_SUPPORTED_SIZES).inOrder()
+    }
+
+    private fun createUseCaseConfig(
+        captureType: CaptureType = CaptureType.IMAGE_CAPTURE,
+        targetRotation: Int = Surface.ROTATION_0,
+        targetAspectRatio: Int = TARGET_ASPECT_RATIO_NONE,
+        targetResolution: Size? = null,
+        maxResolution: Size? = null,
+        defaultResolution: Size? = null,
+    ): UseCaseConfig<*> {
+        val builder = FakeUseCaseConfig.Builder(captureType, ImageFormat.JPEG)
+        builder.setTargetRotation(targetRotation)
+        if (targetAspectRatio != TARGET_ASPECT_RATIO_NONE) {
+            builder.setTargetAspectRatio(targetAspectRatio)
+        }
+        targetResolution?.let { builder.setTargetResolution(it) }
+        maxResolution?.let { builder.setMaxResolution(it) }
+        defaultResolution?.let { builder.setDefaultResolution(it) }
+        return builder.useCaseConfig
+    }
+}
\ No newline at end of file
diff --git a/camera/camera-core/src/test/java/androidx/camera/core/internal/SupportedOutputSizesSorterTest.kt b/camera/camera-core/src/test/java/androidx/camera/core/internal/SupportedOutputSizesSorterTest.kt
new file mode 100644
index 0000000..fd9e4ea
--- /dev/null
+++ b/camera/camera-core/src/test/java/androidx/camera/core/internal/SupportedOutputSizesSorterTest.kt
@@ -0,0 +1,127 @@
+/*
+ * Copyright 2023 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.internal
+
+import android.graphics.ImageFormat
+import android.os.Build
+import android.util.Pair
+import android.util.Size
+import androidx.camera.core.impl.UseCaseConfigFactory.CaptureType
+import androidx.camera.testing.fakes.FakeCameraInfoInternal
+import androidx.camera.testing.fakes.FakeUseCaseConfig
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.robolectric.RobolectricTestRunner
+import org.robolectric.annotation.internal.DoNotInstrument
+
+private val LANDSCAPE_ACTIVE_ARRAY_SIZE = Size(4032, 3024)
+private val DEFAULT_SUPPORTED_SIZES = listOf(
+    Size(4032, 3024), // 4:3
+    Size(3840, 2160), // 16:9
+    Size(1920, 1440), // 4:3
+    Size(1920, 1080), // 16:9
+    Size(1280, 960), // 4:3
+    Size(1280, 720), // 16:9
+    Size(960, 544), // a mod16 version of resolution with 16:9 aspect ratio.
+    Size(800, 450), // 16:9
+    Size(640, 480), // 4:3
+    Size(320, 240), // 4:3
+    Size(320, 180), // 16:9
+    Size(256, 144) // 16:9
+)
+
+/**
+ * Unit tests for [SupportedOutputSizesSorter].
+ */
+@RunWith(RobolectricTestRunner::class)
+@DoNotInstrument
+@org.robolectric.annotation.Config(minSdk = Build.VERSION_CODES.LOLLIPOP)
+class SupportedOutputSizesSorterTest {
+
+    @Test
+    fun canSelectCustomOrderedResolutions() {
+        // Arrange
+        val imageFormat = ImageFormat.JPEG
+        val cameraInfoInternal = FakeCameraInfoInternal().apply {
+            setSupportedResolutions(imageFormat, DEFAULT_SUPPORTED_SIZES)
+        }
+        val supportedOutputSizesSorter =
+            SupportedOutputSizesSorter(cameraInfoInternal, LANDSCAPE_ACTIVE_ARRAY_SIZE)
+        // Sets up the custom ordered resolutions
+        val useCaseConfig =
+            FakeUseCaseConfig.Builder(CaptureType.IMAGE_CAPTURE, imageFormat).apply {
+                setCustomOrderedResolutions(
+                    listOf(
+                        Size(1920, 1080),
+                        Size(720, 480),
+                        Size(640, 480)
+                    )
+                )
+            }.useCaseConfig
+
+        // Act
+        val sortedResult = supportedOutputSizesSorter.getSortedSupportedOutputSizes(useCaseConfig)
+
+        // Assert
+        assertThat(sortedResult).containsExactlyElementsIn(
+            listOf(
+                Size(1920, 1080),
+                Size(720, 480),
+                Size(640, 480)
+            )
+        ).inOrder()
+    }
+
+    @Test
+    fun canSelectCustomSupportedResolutions() {
+        // Arrange
+        val imageFormat = ImageFormat.JPEG
+        val cameraInfoInternal = FakeCameraInfoInternal().apply {
+            setSupportedResolutions(imageFormat, DEFAULT_SUPPORTED_SIZES)
+        }
+        val supportedOutputSizesSorter =
+            SupportedOutputSizesSorter(cameraInfoInternal, LANDSCAPE_ACTIVE_ARRAY_SIZE)
+        // Sets up the custom supported resolutions
+        val useCaseConfig =
+            FakeUseCaseConfig.Builder(CaptureType.IMAGE_CAPTURE, imageFormat).apply {
+                setSupportedResolutions(
+                    listOf(
+                        Pair.create(
+                            imageFormat, arrayOf(
+                                Size(1920, 1080),
+                                Size(720, 480),
+                                Size(640, 480)
+                            )
+                        )
+                    )
+                )
+            }.useCaseConfig
+
+        // Act
+        val sortedResult = supportedOutputSizesSorter.getSortedSupportedOutputSizes(useCaseConfig)
+
+        // Assert
+        assertThat(sortedResult).containsExactlyElementsIn(
+            listOf(
+                Size(1920, 1080),
+                Size(720, 480),
+                Size(640, 480)
+            )
+        ).inOrder()
+    }
+}
\ No newline at end of file
diff --git a/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeCameraDeviceSurfaceManager.java b/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeCameraDeviceSurfaceManager.java
index ef49742..cd5d637 100644
--- a/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeCameraDeviceSurfaceManager.java
+++ b/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeCameraDeviceSurfaceManager.java
@@ -89,13 +89,14 @@
                 SurfaceConfig.ConfigSize.PREVIEW);
     }
 
-    @Override
     @NonNull
+    @Override
     public Map<UseCaseConfig<?>, StreamSpec> getSuggestedStreamSpecs(
-            boolean isConcurrentCameraModeOn,
-            @NonNull String cameraId,
+            boolean isConcurrentCameraModeOn, @NonNull String cameraId,
             @NonNull List<AttachedSurfaceInfo> existingSurfaces,
-            @NonNull List<UseCaseConfig<?>> newUseCaseConfigs) {
+            @NonNull Map<UseCaseConfig<?>, List<Size>> newUseCaseConfigsSupportedSizeMap) {
+        List<UseCaseConfig<?>> newUseCaseConfigs =
+                new ArrayList<>(newUseCaseConfigsSupportedSizeMap.keySet());
         checkSurfaceCombo(existingSurfaces, newUseCaseConfigs);
         Map<UseCaseConfig<?>, StreamSpec> suggestedStreamSpecs = new HashMap<>();
         for (UseCaseConfig<?> useCaseConfig : newUseCaseConfigs) {
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 931d5f1..40ce0b7 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
@@ -16,6 +16,8 @@
 
 package androidx.camera.testing.fakes;
 
+import static androidx.camera.core.impl.ImageFormatConstants.INTERNAL_DEFINED_IMAGE_FORMAT_PRIVATE;
+
 import android.util.Pair;
 import android.util.Size;
 
@@ -26,7 +28,6 @@
 import androidx.camera.core.UseCase;
 import androidx.camera.core.impl.CaptureConfig;
 import androidx.camera.core.impl.Config;
-import androidx.camera.core.impl.ImageFormatConstants;
 import androidx.camera.core.impl.ImageOutputConfig;
 import androidx.camera.core.impl.MutableConfig;
 import androidx.camera.core.impl.MutableOptionsBundle;
@@ -57,7 +58,7 @@
     @Override
     public int getInputFormat() {
         return retrieveOption(OPTION_INPUT_FORMAT,
-                ImageFormatConstants.INTERNAL_DEFINED_IMAGE_FORMAT_PRIVATE);
+                INTERNAL_DEFINED_IMAGE_FORMAT_PRIVATE);
     }
 
     /** Builder for an empty Config */
@@ -81,6 +82,11 @@
             this(MutableOptionsBundle.create(), captureType);
         }
 
+        public Builder(@NonNull CaptureType captureType, int inputFormat) {
+            this(MutableOptionsBundle.create(), captureType);
+            mOptionsBundle.insertOption(OPTION_INPUT_FORMAT, inputFormat);
+        }
+
         public Builder(@NonNull Config config, @NonNull CaptureType captureType) {
             mOptionsBundle = MutableOptionsBundle.from(config);
             setTargetClass(FakeUseCase.class);
diff --git a/camera/camera-testing/src/test/java/androidx/camera/testing/fakes/FakeCameraDeviceSurfaceManagerTest.java b/camera/camera-testing/src/test/java/androidx/camera/testing/fakes/FakeCameraDeviceSurfaceManagerTest.java
index 7bd2de4..0f3fa1c 100644
--- a/camera/camera-testing/src/test/java/androidx/camera/testing/fakes/FakeCameraDeviceSurfaceManagerTest.java
+++ b/camera/camera-testing/src/test/java/androidx/camera/testing/fakes/FakeCameraDeviceSurfaceManagerTest.java
@@ -23,7 +23,6 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import static java.util.Arrays.asList;
 import static java.util.Collections.emptyList;
 import static java.util.Collections.singletonList;
 
@@ -31,6 +30,7 @@
 import android.util.Range;
 import android.util.Size;
 
+import androidx.annotation.NonNull;
 import androidx.camera.core.ImageAnalysis;
 import androidx.camera.core.impl.AttachedSurfaceInfo;
 import androidx.camera.core.impl.StreamSpec;
@@ -45,6 +45,7 @@
 import org.robolectric.annotation.internal.DoNotInstrument;
 
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
@@ -69,8 +70,6 @@
 
     private FakeUseCaseConfig mFakeUseCaseConfig;
 
-    private List<UseCaseConfig<?>> mUseCaseConfigList;
-
     @Before
     public void setUp() {
         mFakeCameraDeviceSurfaceManager = new FakeCameraDeviceSurfaceManager();
@@ -82,8 +81,6 @@
         mFakeCameraDeviceSurfaceManager.setSuggestedStreamSpec(FAKE_CAMERA_ID1,
                 mFakeUseCaseConfig.getClass(),
                 StreamSpec.builder(new Size(FAKE_WIDTH1, FAKE_HEIGHT1)).build());
-
-        mUseCaseConfigList = singletonList(mFakeUseCaseConfig);
     }
 
     @Test
@@ -94,7 +91,7 @@
                 /* isConcurrentCameraModeOn = */false,
                 FAKE_CAMERA_ID0,
                 emptyList(),
-                asList(preview, analysis));
+                createConfigOutputSizesMap(preview, analysis));
     }
 
     @Test(expected = IllegalArgumentException.class)
@@ -109,7 +106,7 @@
         mFakeCameraDeviceSurfaceManager.getSuggestedStreamSpecs(
                 /* isConcurrentCameraModeOn = */false,
                 FAKE_CAMERA_ID0,
-                singletonList(analysis), asList(preview, video));
+                singletonList(analysis), createConfigOutputSizesMap(preview, video));
     }
 
     @Test(expected = IllegalArgumentException.class)
@@ -120,7 +117,7 @@
         mFakeCameraDeviceSurfaceManager.getSuggestedStreamSpecs(
                 /* isConcurrentCameraModeOn = */false,
                 FAKE_CAMERA_ID0,
-                Collections.emptyList(), asList(preview, video, analysis));
+                Collections.emptyList(), createConfigOutputSizesMap(preview, video, analysis));
     }
 
     @Test
@@ -129,12 +126,12 @@
                 mFakeCameraDeviceSurfaceManager.getSuggestedStreamSpecs(
                         /* isConcurrentCameraModeOn = */false,
                         FAKE_CAMERA_ID0,
-                        Collections.emptyList(), mUseCaseConfigList);
+                        Collections.emptyList(), createConfigOutputSizesMap(mFakeUseCaseConfig));
         Map<UseCaseConfig<?>, StreamSpec> suggestedStreamSpecCamera1 =
                 mFakeCameraDeviceSurfaceManager.getSuggestedStreamSpecs(
                         /* isConcurrentCameraModeOn = */false,
                         FAKE_CAMERA_ID1,
-                        Collections.emptyList(), mUseCaseConfigList);
+                        Collections.emptyList(), createConfigOutputSizesMap(mFakeUseCaseConfig));
 
         assertThat(suggestedStreamSpecsCamera0.get(mFakeUseCaseConfig)).isEqualTo(
                 StreamSpec.builder(new Size(FAKE_WIDTH0, FAKE_HEIGHT0)).build());
@@ -142,4 +139,12 @@
                 StreamSpec.builder(new Size(FAKE_WIDTH1, FAKE_HEIGHT1)).build());
     }
 
+    private Map<UseCaseConfig<?>, List<Size>> createConfigOutputSizesMap(
+            @NonNull UseCaseConfig<?>... useCaseConfigs) {
+        Map<UseCaseConfig<?>, List<Size>> configOutputSizesMap = new HashMap<>();
+        for (UseCaseConfig<?> useCaseConfig : useCaseConfigs) {
+            configOutputSizesMap.put(useCaseConfig, Collections.emptyList());
+        }
+        return configOutputSizesMap;
+    }
 }