Camera: Relax logical camera stream combination requirement

- Update CTS to handle relaxed requirement for both public API and NDK.
- Added stream combination support query in Camera ITS.

Test: Camera CTS and ITS
Bug: 119325664
Change-Id: Ibcf418baccfcd9672d83f36eaf81671f3d514334
diff --git a/apps/CameraITS/pymodules/its/device.py b/apps/CameraITS/pymodules/its/device.py
index 0f7aad5..f8aad38 100644
--- a/apps/CameraITS/pymodules/its/device.py
+++ b/apps/CameraITS/pymodules/its/device.py
@@ -530,6 +530,33 @@
             raise its.error.Error('3A failed to converge')
         return ae_sens, ae_exp, awb_gains, awb_transform, af_dist
 
+    def is_stream_combination_supported(self, out_surfaces):
+        """Query whether a output surfaces combination is supported by the camera device.
+
+        This function hooks up to the isSessionConfigurationSupported() camera API
+        to query whether a particular stream combination is supported.
+
+        Refer to do_capture function for specification of out_surfaces field.
+        """
+        cmd = {}
+        cmd['cmdName'] = 'isStreamCombinationSupported'
+
+        if not isinstance(out_surfaces, list):
+            cmd['outputSurfaces'] = [out_surfaces]
+        else:
+            cmd['outputSurfaces'] = out_surfaces
+        formats = [c['format'] if 'format' in c else 'yuv'
+                   for c in cmd['outputSurfaces']]
+        formats = [s if s != 'jpg' else 'jpeg' for s in formats]
+
+        self.sock.send(json.dumps(cmd) + '\n')
+
+        data,_ = self.__read_response_from_socket()
+        if data['tag'] != 'streamCombinationSupport':
+            its.error.Error('Failed to query stream combination')
+
+        return data['strValue'] == 'supportedCombination'
+
     def do_capture(self, cap_request,
             out_surfaces=None, reprocess_format=None, repeat_request=None):
         """Issue capture request(s), and read back the image(s) and metadata.
diff --git a/apps/CameraITS/tests/scene4/test_multi_camera_alignment.py b/apps/CameraITS/tests/scene4/test_multi_camera_alignment.py
index 021d59b..c5feeab 100644
--- a/apps/CameraITS/tests/scene4/test_multi_camera_alignment.py
+++ b/apps/CameraITS/tests/scene4/test_multi_camera_alignment.py
@@ -275,12 +275,6 @@
         w, h = its.objects.get_available_output_sizes(
                 'yuv', props, match_ar_size=max_raw_size)[0]
 
-        # Do 3A and get the values
-        s, e, _, _, fd = cam.do_3a(get_results=True,
-                                   lock_ae=True, lock_awb=True)
-        e *= 2  # brighten RAW images
-        req = its.objects.manual_capture_request(s, e, fd, True, props)
-
         # get physical camera properties
         ids = its.caps.logical_multi_camera_physical_ids(props)
         props_physical = {}
@@ -292,6 +286,16 @@
         out_surfaces = [{'format': 'yuv', 'width': w, 'height': h},
                         {'format': 'raw', 'physicalCamera': ids[0]},
                         {'format': 'raw', 'physicalCamera': ids[1]}]
+
+        out_surfaces_supported = cam.is_stream_combination_supported(out_surfaces)
+        its.caps.skip_unless(out_surfaces_supported)
+
+        # Do 3A and get the values
+        s, e, _, _, fd = cam.do_3a(get_results=True,
+                                   lock_ae=True, lock_awb=True)
+        e *= 2  # brighten RAW images
+        req = its.objects.manual_capture_request(s, e, fd, True, props)
+
         _, cap_raw[ids[0]], cap_raw[ids[1]] = cam.do_capture(req, out_surfaces)
 
     size_raw = {}
diff --git a/apps/CameraITS/tests/sensor_fusion/test_multi_camera_frame_sync.py b/apps/CameraITS/tests/sensor_fusion/test_multi_camera_frame_sync.py
index 2174126..5a6c77b 100644
--- a/apps/CameraITS/tests/sensor_fusion/test_multi_camera_frame_sync.py
+++ b/apps/CameraITS/tests/sensor_fusion/test_multi_camera_frame_sync.py
@@ -123,6 +123,9 @@
                         {"format": "yuv", "width": W, "height": H,
                          "physicalCamera": ids[1]}]
 
+        out_surfaces_supported = cam.is_stream_combination_supported(out_surfaces)
+        its.caps.skip_unless(out_surfaces_supported)
+
         capture_1_list, capture_2_list = cam.do_capture(
             [req]*NUM_CAPTURES, out_surfaces)
 
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/ItsService.java b/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/ItsService.java
index 1b18a5a..d9f3ff7 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/ItsService.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/ItsService.java
@@ -36,6 +36,7 @@
 import android.hardware.camera2.params.InputConfiguration;
 import android.hardware.camera2.params.MeteringRectangle;
 import android.hardware.camera2.params.OutputConfiguration;
+import android.hardware.camera2.params.SessionConfiguration;
 import android.hardware.Sensor;
 import android.hardware.SensorEvent;
 import android.hardware.SensorEventListener;
@@ -92,6 +93,7 @@
 import java.util.Map;
 import java.util.concurrent.BlockingQueue;
 import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Executor;
 import java.util.concurrent.LinkedBlockingDeque;
 import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.Semaphore;
@@ -677,6 +679,8 @@
                     doReprocessCapture(cmdObj);
                 } else if ("getItsVersion".equals(cmdObj.getString("cmdName"))) {
                     mSocketRunnableObj.sendResponse("ItsVersion", ITS_SERVICE_VERSION);
+                } else if ("isStreamCombinationSupported".equals(cmdObj.getString("cmdName"))) {
+                    doCheckStreamCombination(cmdObj);
                 } else {
                     throw new ItsException("Unknown command: " + cmd);
                 }
@@ -967,6 +971,55 @@
         }
     }
 
+    private static class HandlerExecutor implements Executor {
+        private final Handler mHandler;
+
+        public HandlerExecutor(Handler handler) {
+            mHandler = handler;
+        }
+
+        @Override
+        public void execute(Runnable runCmd) {
+            mHandler.post(runCmd);
+        }
+    }
+
+    private void doCheckStreamCombination(JSONObject params) throws ItsException {
+        try {
+            JSONObject obj = new JSONObject();
+            JSONArray jsonOutputSpecs = ItsUtils.getOutputSpecs(params);
+            prepareImageReadersWithOutputSpecs(jsonOutputSpecs, /*inputSize*/null,
+                    /*inputFormat*/0, /*maxInputBuffers*/0, /*backgroundRequest*/false);
+            int numSurfaces = mOutputImageReaders.length;
+            List<OutputConfiguration> outputConfigs =
+                    new ArrayList<OutputConfiguration>(numSurfaces);
+            for (int i = 0; i < numSurfaces; i++) {
+                OutputConfiguration config = new OutputConfiguration(
+                        mOutputImageReaders[i].getSurface());
+                if (mPhysicalStreamMap.get(i) != null) {
+                    config.setPhysicalCameraId(mPhysicalStreamMap.get(i));
+                }
+                outputConfigs.add(config);
+            }
+
+            BlockingSessionCallback sessionListener = new BlockingSessionCallback();
+            SessionConfiguration sessionConfig = new SessionConfiguration(
+                SessionConfiguration.SESSION_REGULAR, outputConfigs,
+                new HandlerExecutor(mCameraHandler), sessionListener);
+            boolean supported = mCamera.isSessionConfigurationSupported(sessionConfig);
+
+            String supportString = supported ? "supportedCombination" : "unsupportedCombination";
+            mSocketRunnableObj.sendResponse("streamCombinationSupport", supportString);
+
+        } catch (UnsupportedOperationException e) {
+            mSocketRunnableObj.sendResponse("streamCombinationSupport", "unsupportedOperation");
+        } catch (IllegalArgumentException e) {
+            throw new ItsException("Error checking stream combination", e);
+        } catch (CameraAccessException e) {
+            throw new ItsException("Error checking stream combination", e);
+        }
+    }
+
     private void prepareImageReaders(Size[] outputSizes, int[] outputFormats, Size inputSize,
             int inputFormat, int maxInputBuffers) {
         closeImageReaders();
diff --git a/tests/camera/libctscamera2jni/native-camera-jni.cpp b/tests/camera/libctscamera2jni/native-camera-jni.cpp
index 89bb31b..0c430b6 100644
--- a/tests/camera/libctscamera2jni/native-camera-jni.cpp
+++ b/tests/camera/libctscamera2jni/native-camera-jni.cpp
@@ -1113,7 +1113,8 @@
 
     camera_status_t createCaptureSessionWithLog(
             const std::vector<ACaptureSessionOutput*> extraOutputs,
-            bool isPreviewShared = false, ACaptureRequest *sessionParameters = nullptr) {
+            bool isPreviewShared = false, ACaptureRequest *sessionParameters = nullptr,
+            bool sessionConfigurationDefault = true) {
         if (mSession) {
             LOG_ERROR(errorString, "Cannot create session before closing existing one");
             return ACAMERA_ERROR_UNKNOWN;
@@ -1133,6 +1134,10 @@
             return ret;
         }
 
+        if (ret == ACAMERA_ERROR_UNSUPPORTED_OPERATION && !sessionConfigurationDefault) {
+            return ret;
+        }
+
         ret = ACameraDevice_createCaptureSessionWithSessionParameters(
                 mDevice, mOutputs, sessionParameters, &mSessionCb, &mSession);
         if (ret != ACAMERA_OK || mSession == nullptr) {
@@ -2918,8 +2923,11 @@
             goto cleanup;
         }
 
-        ret = testCase.createCaptureSessionWithLog(readerSessionOutputs);
-        if (ret != ACAMERA_OK) {
+        ret = testCase.createCaptureSessionWithLog(readerSessionOutputs, false /*isPreviewShared*/,
+                nullptr /*sessionParameters*/, false /*sessionConfigurationDefault*/);
+        if (ret == ACAMERA_ERROR_UNSUPPORTED_OPERATION) {
+            continue;
+        } else if (ret != ACAMERA_OK) {
             // Don't log error here. testcase did it
             goto cleanup;
         }
diff --git a/tests/camera/src/android/hardware/camera2/cts/ImageReaderTest.java b/tests/camera/src/android/hardware/camera2/cts/ImageReaderTest.java
index 391a62a..abd6bb6 100644
--- a/tests/camera/src/android/hardware/camera2/cts/ImageReaderTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/ImageReaderTest.java
@@ -1025,8 +1025,8 @@
                 }
 
                 if (checkSession) {
-                    assertTrue("Camera capture session validation for format: " + format +
-                            " failed", checkImageReaderSessionConfiguration());
+                    checkImageReaderSessionConfiguration(
+                            "Camera capture session validation for format: " + format + "failed");
                 }
 
                 // Start capture.
diff --git a/tests/camera/src/android/hardware/camera2/cts/LogicalCameraDeviceTest.java b/tests/camera/src/android/hardware/camera2/cts/LogicalCameraDeviceTest.java
index 664f9ba..a2a2c70 100644
--- a/tests/camera/src/android/hardware/camera2/cts/LogicalCameraDeviceTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/LogicalCameraDeviceTest.java
@@ -37,6 +37,7 @@
 import android.hardware.camera2.cts.helpers.StaticMetadata.CheckLevel;
 import android.hardware.camera2.cts.testcases.Camera2SurfaceViewTestCase;
 import android.hardware.camera2.params.OutputConfiguration;
+import android.hardware.camera2.params.SessionConfiguration;
 import android.hardware.camera2.params.StreamConfigurationMap;
 import android.media.CamcorderProfile;
 import android.media.Image;
@@ -135,8 +136,7 @@
     }
 
     /**
-     * Test for making sure that streaming from physical streams work as expected, and
-     * FPS isn't slowed down.
+     * Test for making sure that streaming from physical streams work as expected.
      */
     @Test
     public void testBasicPhysicalStreaming() throws Exception {
@@ -231,6 +231,15 @@
                 outputConfigs.add(new OutputConfiguration(yuvTargetLogical.getSurface()));
                 imageReaderListeners.add(readerListenerLogical);
 
+                SessionConfigSupport sessionConfigSupport = isSessionConfigSupported(
+                        mCamera, mHandler, outputConfigs, /*inputConfig*/ null,
+                        SessionConfiguration.SESSION_REGULAR, false/*defaultSupport*/);
+                assertTrue("Session configuration query for logical camera failed with error",
+                        !sessionConfigSupport.error);
+                if (!sessionConfigSupport.callSupported || !sessionConfigSupport.configSupported) {
+                    continue;
+                }
+
                 mSessionListener = new BlockingSessionCallback();
                 mSession = configureCameraSessionWithConfig(mCamera, outputConfigs,
                         mSessionListener, mHandler);
@@ -350,6 +359,15 @@
                     imageReaderListeners.add(readerListener);
                 }
 
+                SessionConfigSupport sessionConfigSupport = isSessionConfigSupported(
+                        mCamera, mHandler, outputConfigs, /*inputConfig*/ null,
+                        SessionConfiguration.SESSION_REGULAR, false/*defaultSupport*/);
+                assertTrue("Session configuration query for logical camera failed with error",
+                        !sessionConfigSupport.error);
+                if (!sessionConfigSupport.callSupported || !sessionConfigSupport.configSupported) {
+                    continue;
+                }
+
                 CaptureRequest.Builder requestBuilder =
                     mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW, physicalIdSet);
                 for (OutputConfiguration c : outputConfigs) {
@@ -853,6 +871,15 @@
             physicalTargets.add(physicalTarget);
         }
 
+        SessionConfigSupport sessionConfigSupport = isSessionConfigSupported(
+                mCamera, mHandler, outputConfigs, /*inputConfig*/ null,
+                SessionConfiguration.SESSION_REGULAR, false/*defaultSupport*/);
+        assertTrue("Session configuration query for logical camera failed with error",
+                !sessionConfigSupport.error);
+        if (!sessionConfigSupport.callSupported || !sessionConfigSupport.configSupported) {
+            return;
+        }
+
         mSessionListener = new BlockingSessionCallback();
         mSession = configureCameraSessionWithConfig(mCamera, outputConfigs,
                 mSessionListener, mHandler);
@@ -975,13 +1002,13 @@
         double logicalAvgDurationMs2 = (logicalTimestamps2[NUM_FRAMES_CHECKED-1] -
                 logicalTimestamps2[0])/(NS_PER_MS*(NUM_FRAMES_CHECKED-1));
 
-        mCollector.expectLessOrEqual("The average frame duration increase of all physical "
-                + "streams is larger than threshold: "
-                + String.format("increase = %.2f, threshold = %.2f",
-                  (logicalAvgDurationMs2 - logicalAvgDurationMs)/logicalAvgDurationMs,
-                  FRAME_DURATION_THRESHOLD),
-                logicalAvgDurationMs*(1+FRAME_DURATION_THRESHOLD),
-                logicalAvgDurationMs2);
+        // Check framerate slow down with physical streams, but do not enforce.
+        double fpsRatio = (logicalAvgDurationMs2 - logicalAvgDurationMs)/logicalAvgDurationMs;
+        if (fpsRatio > FRAME_DURATION_THRESHOLD) {
+            Log.w(TAG, "The average frame duration with concurrent physical streams is" +
+                logicalAvgDurationMs2 + " ms vs " + logicalAvgDurationMs +
+                " ms for logical streams only");
+        }
 
         // Stop preview
         if (mSession != null) {
diff --git a/tests/camera/src/android/hardware/camera2/cts/RecordingTest.java b/tests/camera/src/android/hardware/camera2/cts/RecordingTest.java
index 81fbcae..b5be8e6 100644
--- a/tests/camera/src/android/hardware/camera2/cts/RecordingTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/RecordingTest.java
@@ -873,10 +873,9 @@
         requestBuilder.set(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE, fpsRange);
         requestBuilder.addTarget(mPreviewSurface);
         CaptureRequest initialRequest = requestBuilder.build();
-        assertTrue("Constrained session configuration query failed",
-                CameraTestUtils.checkSessionConfigurationWithSurfaces(mCamera, mHandler,
+        CameraTestUtils.checkSessionConfigurationWithSurfaces(mCamera, mHandler,
                 outputSurfaces, /*inputConfig*/ null, SessionConfiguration.SESSION_HIGH_SPEED,
-                /*expectedResult*/ true));
+                /*defaultSupport*/ true, "Constrained session configuration query failed");
         mSession = buildConstrainedCameraSession(mCamera, outputSurfaces, mSessionListener,
                 mHandler, initialRequest);
         slowMoRequests = ((CameraConstrainedHighSpeedCaptureSession) mSession).
diff --git a/tests/camera/src/android/hardware/camera2/cts/RobustnessTest.java b/tests/camera/src/android/hardware/camera2/cts/RobustnessTest.java
index 988a7f9..dc3eaba 100644
--- a/tests/camera/src/android/hardware/camera2/cts/RobustnessTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/RobustnessTest.java
@@ -162,69 +162,6 @@
     }
 
     /**
-     * Test for making sure the required logical output combinations for each hardware level and
-     * capability work as expected.
-     */
-    public void testMandatoryLogicalOutputCombinations() throws Exception {
-        /**
-         * Tables for maximum sizes to try for each hardware level and capability.
-         *
-         * Keep in sync with the tables in
-         * frameworks/base/core/java/android/hardware/camera2/CameraDevice.java#createCaptureSession
-         *
-         * Each row of the table is a set of (format, max resolution) pairs, using the below consts
-         */
-
-        final int[][] RAW_COMBINATIONS = {
-            // No-preview DNG capture.
-            {RAW,  MAXIMUM },
-            // Standard DNG capture.
-            {PRIV, PREVIEW,  RAW,  MAXIMUM },
-            // In-app processing plus DNG capture.
-            {YUV,  PREVIEW,  RAW,  MAXIMUM },
-            // Video recording with DNG capture.
-            {PRIV, PREVIEW,  PRIV, PREVIEW,  RAW, MAXIMUM},
-            // Preview with in-app processing and DNG capture.
-            {PRIV, PREVIEW,  YUV,  PREVIEW,  RAW, MAXIMUM},
-            // Two-input in-app processing plus DNG capture.
-            {YUV,  PREVIEW,  YUV,  PREVIEW,  RAW, MAXIMUM},
-            // Still capture with simultaneous JPEG and DNG.
-            {PRIV, PREVIEW,  JPEG, MAXIMUM,  RAW, MAXIMUM},
-            // In-app processing with simultaneous JPEG and DNG.
-            {YUV,  PREVIEW,  JPEG, MAXIMUM,  RAW, MAXIMUM}
-        };
-
-        final int[][][] TABLES = { RAW_COMBINATIONS };
-
-        sanityCheckConfigurationTables(TABLES);
-
-        for (String id : mCameraIds) {
-            openDevice(id);
-
-            // Find the concrete max sizes for each format/resolution combination
-            MaxStreamSizes maxSizes = new MaxStreamSizes(mStaticInfo, id, getContext());
-
-            String streamConfigurationMapString =
-                    mStaticInfo.getCharacteristics().get(
-                            CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP).toString();
-            if (VERBOSE) {
-                Log.v(TAG, "StreamConfigurationMap: " + streamConfigurationMapString);
-            }
-
-            // Then run higher-level tests if applicable
-            if (!mStaticInfo.isHardwareLevelLegacy()) {
-                if (mStaticInfo.isLogicalMultiCamera()) {
-                    for (int[] config : RAW_COMBINATIONS) {
-                        testMultiCameraOutputCombination(id, config, maxSizes);
-                    }
-                }
-            }
-
-            closeDevice(id);
-        }
-    }
-
-    /**
      * Test for making sure the mandatory stream combinations work as expected.
      */
     public void testMandatoryOutputCombinations() throws Exception {
@@ -242,9 +179,36 @@
             try {
                 for (MandatoryStreamCombination combination : combinations) {
                     if (!combination.isReprocessable()) {
-                        testMandatoryStreamCombination(id, combination);
+                        testMandatoryStreamCombination(id, mStaticInfo,
+                                null/*physicalCameraId*/, combination);
                     }
                 }
+
+                // Make sure mandatory stream combinations for each physical camera work
+                // as expected.
+                if (mStaticInfo.isLogicalMultiCamera()) {
+                    Set<String> physicalCameraIds =
+                            mStaticInfo.getCharacteristics().getPhysicalCameraIds();
+                    for (String physicalId : physicalCameraIds) {
+                        if (Arrays.asList(mCameraIds).contains(physicalId)) {
+                            // If physicalId is advertised in camera ID list, do not need to test
+                            // its stream combination through logical camera.
+                            continue;
+                        }
+                        StaticMetadata physicalStaticInfo = mAllStaticInfo.get(physicalId);
+                        MandatoryStreamCombination[] phyCombinations =
+                                physicalStaticInfo.getCharacteristics().get(
+                                        CameraCharacteristics.SCALER_MANDATORY_STREAM_COMBINATIONS);
+
+                        for (MandatoryStreamCombination combination : phyCombinations) {
+                            if (!combination.isReprocessable()) {
+                                testMandatoryStreamCombination(id, physicalStaticInfo,
+                                        physicalId, combination);
+                            }
+                        }
+                    }
+                }
+
             } finally {
                 closeDevice(id);
             }
@@ -257,8 +221,7 @@
             List<ImageReader> rawTargets, List<ImageReader> heicTargets,
             List<OutputConfiguration> outputConfigs,
             int numBuffers, boolean substituteY8, boolean substituteHeic,
-            MandatoryStreamInformation overrideStreamInfo,
-            List<String> overridePhysicalCameraIds, List<Size> overridePhysicalCameraSizes) {
+            String overridePhysicalCameraId) {
 
         ImageDropperListener imageDropperListener = new ImageDropperListener();
 
@@ -277,102 +240,94 @@
             availableSizes = streamInfo.getAvailableSizes().toArray(availableSizes);
             Size targetSize = CameraTestUtils.getMaxSize(availableSizes);
 
-            int numConfigs = 1;
-            if ((overrideStreamInfo == streamInfo) && overridePhysicalCameraIds != null &&
-                    overridePhysicalCameraIds.size() > 1) {
-                numConfigs = overridePhysicalCameraIds.size();
-            }
-            for (int j = 0; j < numConfigs; j++) {
-                targetSize = (numConfigs == 1) ? targetSize : overridePhysicalCameraSizes.get(j);
-                switch (format) {
-                    case ImageFormat.PRIVATE: {
-                        SurfaceTexture target = new SurfaceTexture(/*random int*/1);
-                        target.setDefaultBufferSize(targetSize.getWidth(), targetSize.getHeight());
-                        OutputConfiguration config = new OutputConfiguration(new Surface(target));
-                        if (numConfigs > 1) {
-                            config.setPhysicalCameraId(overridePhysicalCameraIds.get(j));
-                        }
-                        outputConfigs.add(config);
-                        privTargets.add(target);
-                        break;
+            switch (format) {
+                case ImageFormat.PRIVATE: {
+                    SurfaceTexture target = new SurfaceTexture(/*random int*/1);
+                    target.setDefaultBufferSize(targetSize.getWidth(), targetSize.getHeight());
+                    OutputConfiguration config = new OutputConfiguration(new Surface(target));
+                    if (overridePhysicalCameraId != null) {
+                        config.setPhysicalCameraId(overridePhysicalCameraId);
                     }
-                    case ImageFormat.JPEG: {
-                        ImageReader target = ImageReader.newInstance(targetSize.getWidth(),
-                                targetSize.getHeight(), format, numBuffers);
-                        target.setOnImageAvailableListener(imageDropperListener, mHandler);
-                        OutputConfiguration config = new OutputConfiguration(target.getSurface());
-                        if (numConfigs > 1) {
-                            config.setPhysicalCameraId(overridePhysicalCameraIds.get(j));
-                        }
-                        outputConfigs.add(config);
-                        jpegTargets.add(target);
-                        break;
-                    }
-                    case ImageFormat.YUV_420_888: {
-                        ImageReader target = ImageReader.newInstance(targetSize.getWidth(),
-                                targetSize.getHeight(), format, numBuffers);
-                        target.setOnImageAvailableListener(imageDropperListener, mHandler);
-                        OutputConfiguration config = new OutputConfiguration(target.getSurface());
-                        if (numConfigs > 1) {
-                            config.setPhysicalCameraId(overridePhysicalCameraIds.get(j));
-                        }
-                        outputConfigs.add(config);
-                        yuvTargets.add(target);
-                        break;
-                    }
-                    case ImageFormat.Y8: {
-                        ImageReader target = ImageReader.newInstance(targetSize.getWidth(),
-                                targetSize.getHeight(), format, numBuffers);
-                        target.setOnImageAvailableListener(imageDropperListener, mHandler);
-                        OutputConfiguration config = new OutputConfiguration(target.getSurface());
-                        if (numConfigs > 1) {
-                            config.setPhysicalCameraId(overridePhysicalCameraIds.get(j));
-                        }
-                        outputConfigs.add(config);
-                        y8Targets.add(target);
-                        break;
-                    }
-                    case ImageFormat.RAW_SENSOR: {
-                        // targetSize could be null in the logical camera case where only
-                        // physical camera supports RAW stream.
-                        if (targetSize != null) {
-                            ImageReader target = ImageReader.newInstance(targetSize.getWidth(),
-                                    targetSize.getHeight(), format, numBuffers);
-                            target.setOnImageAvailableListener(imageDropperListener, mHandler);
-                            OutputConfiguration config =
-                                    new OutputConfiguration(target.getSurface());
-                            if (numConfigs > 1) {
-                                config.setPhysicalCameraId(overridePhysicalCameraIds.get(j));
-                            }
-                            outputConfigs.add(config);
-                            rawTargets.add(target);
-                        }
-                        break;
-                    }
-                    case ImageFormat.HEIC: {
-                        ImageReader target = ImageReader.newInstance(targetSize.getWidth(),
-                                targetSize.getHeight(), format, numBuffers);
-                        target.setOnImageAvailableListener(imageDropperListener, mHandler);
-                        OutputConfiguration config = new OutputConfiguration(target.getSurface());
-                        if (numConfigs > 1) {
-                            config.setPhysicalCameraId(overridePhysicalCameraIds.get(j));
-                        }
-                        outputConfigs.add(config);
-                        heicTargets.add(target);
-                        break;
-                    }
-                    default:
-                        fail("Unknown output format " + format);
+                    outputConfigs.add(config);
+                    privTargets.add(target);
+                    break;
                 }
+                case ImageFormat.JPEG: {
+                    ImageReader target = ImageReader.newInstance(targetSize.getWidth(),
+                            targetSize.getHeight(), format, numBuffers);
+                    target.setOnImageAvailableListener(imageDropperListener, mHandler);
+                    OutputConfiguration config = new OutputConfiguration(target.getSurface());
+                    if (overridePhysicalCameraId != null) {
+                        config.setPhysicalCameraId(overridePhysicalCameraId);
+                    }
+                    outputConfigs.add(config);
+                    jpegTargets.add(target);
+                    break;
+                }
+                case ImageFormat.YUV_420_888: {
+                    ImageReader target = ImageReader.newInstance(targetSize.getWidth(),
+                            targetSize.getHeight(), format, numBuffers);
+                    target.setOnImageAvailableListener(imageDropperListener, mHandler);
+                    OutputConfiguration config = new OutputConfiguration(target.getSurface());
+                    if (overridePhysicalCameraId != null) {
+                        config.setPhysicalCameraId(overridePhysicalCameraId);
+                    }
+                    outputConfigs.add(config);
+                    yuvTargets.add(target);
+                    break;
+                }
+                case ImageFormat.Y8: {
+                    ImageReader target = ImageReader.newInstance(targetSize.getWidth(),
+                            targetSize.getHeight(), format, numBuffers);
+                    target.setOnImageAvailableListener(imageDropperListener, mHandler);
+                    OutputConfiguration config = new OutputConfiguration(target.getSurface());
+                    if (overridePhysicalCameraId != null) {
+                        config.setPhysicalCameraId(overridePhysicalCameraId);
+                    }
+                    outputConfigs.add(config);
+                    y8Targets.add(target);
+                    break;
+                }
+                case ImageFormat.RAW_SENSOR: {
+                    // targetSize could be null in the logical camera case where only
+                    // physical camera supports RAW stream.
+                    if (targetSize != null) {
+                        ImageReader target = ImageReader.newInstance(targetSize.getWidth(),
+                                targetSize.getHeight(), format, numBuffers);
+                        target.setOnImageAvailableListener(imageDropperListener, mHandler);
+                        OutputConfiguration config =
+                                new OutputConfiguration(target.getSurface());
+                        if (overridePhysicalCameraId != null) {
+                            config.setPhysicalCameraId(overridePhysicalCameraId);
+                        }
+                        outputConfigs.add(config);
+                        rawTargets.add(target);
+                    }
+                    break;
+                }
+                case ImageFormat.HEIC: {
+                    ImageReader target = ImageReader.newInstance(targetSize.getWidth(),
+                            targetSize.getHeight(), format, numBuffers);
+                    target.setOnImageAvailableListener(imageDropperListener, mHandler);
+                    OutputConfiguration config = new OutputConfiguration(target.getSurface());
+                    if (overridePhysicalCameraId != null) {
+                        config.setPhysicalCameraId(overridePhysicalCameraId);
+                    }
+                    outputConfigs.add(config);
+                    heicTargets.add(target);
+                    break;
+                }
+                default:
+                    fail("Unknown output format " + format);
             }
         }
     }
 
-    private void testMandatoryStreamCombination(String cameraId,
-            MandatoryStreamCombination combination) throws Exception {
+    private void testMandatoryStreamCombination(String cameraId, StaticMetadata staticInfo,
+            String physicalCameraId, MandatoryStreamCombination combination) throws Exception {
         // Check whether substituting YUV_888 format with Y8 format
         boolean substituteY8 = false;
-        if (mStaticInfo.isMonochromeWithY8()) {
+        if (staticInfo.isMonochromeWithY8()) {
             List<MandatoryStreamInformation> streamsInfo = combination.getStreamsInformation();
             for (MandatoryStreamInformation streamInfo : streamsInfo) {
                 if (streamInfo.getFormat() == ImageFormat.YUV_420_888) {
@@ -384,7 +339,7 @@
 
         // Check whether substituting JPEG format with HEIC format
         boolean substituteHeic = false;
-        if (mStaticInfo.isHeicSupported()) {
+        if (staticInfo.isHeicSupported()) {
             List<MandatoryStreamInformation> streamsInfo = combination.getStreamsInformation();
             for (MandatoryStreamInformation streamInfo : streamsInfo) {
                 if (streamInfo.getFormat() == ImageFormat.JPEG) {
@@ -395,173 +350,35 @@
         }
 
         // Test camera output combination
-        Log.i(TAG, "Testing mandatory stream combination: " + combination.getDescription() +
-                " on camera: " + cameraId);
-        testMandatoryStreamCombination(cameraId, combination, /*substituteY8*/false,
-                /*substituteHeic*/false);
+        String log = "Testing mandatory stream combination: " + combination.getDescription() +
+                " on camera: " + cameraId;
+        if (physicalCameraId != null) {
+            log += ", physical sub-camera: " + physicalCameraId;
+        }
+        Log.i(TAG, log);
+        testMandatoryStreamCombination(cameraId, staticInfo, physicalCameraId, combination,
+                /*substituteY8*/false, /*substituteHeic*/false);
 
         if (substituteY8) {
-            Log.i(TAG, "Testing mandatory stream combination: " + combination.getDescription() +
-                    " on camera: " + cameraId + " with Y8");
-            testMandatoryStreamCombination(cameraId, combination, /*substituteY8*/true,
-                    /*substituteHeic*/false);
+            Log.i(TAG, log + " with Y8");
+            testMandatoryStreamCombination(cameraId, staticInfo, physicalCameraId, combination,
+                    /*substituteY8*/true, /*substituteHeic*/false);
         }
 
         if (substituteHeic) {
-            Log.i(TAG, "Testing mandatory stream combination: " + combination.getDescription() +
-                    " on camera: " + cameraId + " with HEIC");
-            testMandatoryStreamCombination(cameraId, combination,
+            Log.i(TAG, log + " with HEIC");
+            testMandatoryStreamCombination(cameraId, staticInfo, physicalCameraId, combination,
                     /*substituteY8*/false, /*substituteHeic*/true);
         }
-
-        // Test substituting YUV_888/RAW with physical streams for logical camera
-        if (mStaticInfo.isLogicalMultiCamera()) {
-            Log.i(TAG, String.format("Testing logical Camera %s, combination: %s",
-                    cameraId, combination.getDescription()));
-
-            testMultiCameraOutputCombination(cameraId, combination, /*substituteY8*/false);
-
-            if (substituteY8) {
-                testMultiCameraOutputCombination(cameraId, combination, /*substituteY8*/true);
-            }
-        }
-    }
-
-    private void testMultiCameraOutputCombination(String cameraId,
-            MandatoryStreamCombination combination, boolean substituteY8) throws Exception {
-
-        // Timeout is relaxed by 1 second for LEGACY devices to reduce false positive rate in CTS
-        final int TIMEOUT_FOR_RESULT_MS = (mStaticInfo.isHardwareLevelLegacy()) ? 2000 : 1000;
-        final int MIN_RESULT_COUNT = 3;
-        Set<String> physicalCameraIds = mStaticInfo.getCharacteristics().getPhysicalCameraIds();
-
-        List<MandatoryStreamInformation> streamsInfo = combination.getStreamsInformation();
-        for (MandatoryStreamInformation streamInfo : streamsInfo) {
-            int format = streamInfo.getFormat();
-            if (substituteY8 && (format == ImageFormat.YUV_420_888)) {
-                format = ImageFormat.Y8;
-            }
-            if (format != ImageFormat.YUV_420_888 && format != ImageFormat.Y8 &&
-                    format != ImageFormat.RAW_SENSOR) {
-                continue;
-            }
-
-            // Find physical cameras with matching size.
-            Size[] availableSizes = new Size[streamInfo.getAvailableSizes().size()];
-            availableSizes = streamInfo.getAvailableSizes().toArray(availableSizes);
-            Size targetSize = CameraTestUtils.getMaxSize(availableSizes);
-
-            List<String> physicalCamerasForSize = new ArrayList<String>();
-            List<Size> physicalCameraSizes = new ArrayList<Size>();
-            for (String physicalId : physicalCameraIds) {
-                Size[] sizes = mAllStaticInfo.get(physicalId).getAvailableSizesForFormatChecked(
-                        format, StaticMetadata.StreamDirection.Output);
-                if (targetSize != null) {
-                    if (Arrays.asList(sizes).contains(targetSize)) {
-                        physicalCameraSizes.add(targetSize);
-                        physicalCamerasForSize.add(physicalId);
-                    }
-                } else if (format == ImageFormat.RAW_SENSOR && sizes.length > 0) {
-                    physicalCamerasForSize.add(physicalId);
-                    physicalCameraSizes.add(CameraTestUtils.getMaxSize(sizes));
-                }
-                if (physicalCamerasForSize.size() == 2) {
-                    break;
-                }
-            }
-            if (physicalCamerasForSize.size() < 2) {
-                continue;
-            }
-
-            // Set up outputs
-            List<OutputConfiguration> outputConfigs = new ArrayList<OutputConfiguration>();
-            List<SurfaceTexture> privTargets = new ArrayList<SurfaceTexture>();
-            List<ImageReader> jpegTargets = new ArrayList<ImageReader>();
-            List<ImageReader> yuvTargets = new ArrayList<ImageReader>();
-            List<ImageReader> y8Targets = new ArrayList<ImageReader>();
-            List<ImageReader> rawTargets = new ArrayList<ImageReader>();
-            List<ImageReader> heicTargets = new ArrayList<ImageReader>();
-
-            setupConfigurationTargets(streamsInfo, privTargets, jpegTargets, yuvTargets,
-                    y8Targets, rawTargets, heicTargets, outputConfigs, MIN_RESULT_COUNT,
-                    substituteY8, /*substituteHeic*/false, streamInfo, physicalCamerasForSize,
-                    physicalCameraSizes);
-
-            boolean haveSession = false;
-            try {
-                CaptureRequest.Builder requestBuilder =
-                        mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
-
-                for (OutputConfiguration c : outputConfigs) {
-                    requestBuilder.addTarget(c.getSurface());
-                }
-
-                CameraCaptureSession.CaptureCallback mockCaptureCallback =
-                        mock(CameraCaptureSession.CaptureCallback.class);
-
-                assertTrue(String.format("Session configuration query %s failed",
-                        combination.getDescription()),
-                        checkSessionConfiguration(mCamera, mHandler, outputConfigs,
-                        /*inputConfig*/ null, SessionConfiguration.SESSION_REGULAR,
-                        /*expectedResult*/ true));
-
-                createSessionByConfigs(outputConfigs);
-                haveSession = true;
-                CaptureRequest request = requestBuilder.build();
-                mCameraSession.setRepeatingRequest(request, mockCaptureCallback, mHandler);
-
-                verify(mockCaptureCallback,
-                        timeout(TIMEOUT_FOR_RESULT_MS * MIN_RESULT_COUNT).atLeast(MIN_RESULT_COUNT))
-                        .onCaptureCompleted(
-                            eq(mCameraSession),
-                            eq(request),
-                            isA(TotalCaptureResult.class));
-                verify(mockCaptureCallback, never()).
-                        onCaptureFailed(
-                            eq(mCameraSession),
-                            eq(request),
-                            isA(CaptureFailure.class));
-
-            } catch (Throwable e) {
-                mCollector.addMessage(String.format("Output combination: %s failed due to: %s",
-                        combination.getDescription(), e.getMessage()));
-            }
-            if (haveSession) {
-                try {
-                    Log.i(TAG, String.format("Done camera %s, combination: %s, closing session",
-                                    cameraId, combination.getDescription()));
-                    stopCapture(/*fast*/false);
-                } catch (Throwable e) {
-                    mCollector.addMessage(
-                        String.format("Closing down for output combination: %s failed due to: %s",
-                                combination.getDescription(), e.getMessage()));
-                }
-            }
-
-            for (SurfaceTexture target : privTargets) {
-                target.release();
-            }
-            for (ImageReader target : jpegTargets) {
-                target.close();
-            }
-            for (ImageReader target : yuvTargets) {
-                target.close();
-            }
-            for (ImageReader target : y8Targets) {
-                target.close();
-            }
-            for (ImageReader target : rawTargets) {
-                target.close();
-            }
-        }
     }
 
     private void testMandatoryStreamCombination(String cameraId,
+            StaticMetadata staticInfo, String physicalCameraId,
             MandatoryStreamCombination combination,
             boolean substituteY8, boolean substituteHeic) throws Exception {
 
         // Timeout is relaxed by 1 second for LEGACY devices to reduce false positive rate in CTS
-        final int TIMEOUT_FOR_RESULT_MS = (mStaticInfo.isHardwareLevelLegacy()) ? 2000 : 1000;
+        final int TIMEOUT_FOR_RESULT_MS = (staticInfo.isHardwareLevelLegacy()) ? 2000 : 1000;
         final int MIN_RESULT_COUNT = 3;
 
         // Set up outputs
@@ -575,9 +392,7 @@
 
         setupConfigurationTargets(combination.getStreamsInformation(), privTargets, jpegTargets,
                 yuvTargets, y8Targets, rawTargets, heicTargets, outputConfigs, MIN_RESULT_COUNT,
-                substituteY8, substituteHeic,
-                null /*overrideStreamInfo*/, null /*overridePhysicalCameraIds*/,
-                null /* overridePhysicalCameraSizes) */);
+                substituteY8, substituteHeic, physicalCameraId);
 
         boolean haveSession = false;
         try {
@@ -591,10 +406,26 @@
             CameraCaptureSession.CaptureCallback mockCaptureCallback =
                     mock(CameraCaptureSession.CaptureCallback.class);
 
-            assertTrue(String.format("Session configuration query fro combination: %s failed",
-                    combination.getDescription()), checkSessionConfiguration(mCamera,
-                    mHandler, outputConfigs, /*inputConfig*/ null,
-                    SessionConfiguration.SESSION_REGULAR, /*expectedResult*/ true));
+            if (physicalCameraId == null) {
+                checkSessionConfigurationSupported(mCamera, mHandler, outputConfigs,
+                        /*inputConfig*/ null, SessionConfiguration.SESSION_REGULAR,
+                        true/*defaultSupport*/, String.format(
+                        "Session configuration query from combination: %s failed",
+                        combination.getDescription()));
+            } else {
+                SessionConfigSupport sessionConfigSupport = isSessionConfigSupported(
+                        mCamera, mHandler, outputConfigs, /*inputConfig*/ null,
+                        SessionConfiguration.SESSION_REGULAR, false/*defaultSupport*/);
+                assertTrue(
+                        String.format("Session configuration query from combination: %s failed",
+                        combination.getDescription()), !sessionConfigSupport.error);
+                if (!sessionConfigSupport.callSupported) {
+                    return;
+                }
+                assertTrue(
+                        String.format("Session configuration must be supported for combination: " +
+                        "%s", combination.getDescription()), sessionConfigSupport.configSupported);
+            }
 
             createSessionByConfigs(outputConfigs);
             haveSession = true;
@@ -758,8 +589,7 @@
             setupConfigurationTargets(streamInfo.subList(2, streamInfo.size()), privTargets,
                     jpegTargets, yuvTargets, y8Targets, rawTargets, heicTargets, outputConfigs,
                     NUM_REPROCESS_CAPTURES_PER_CONFIG, substituteY8,  substituteHeic,
-                    null /*overrideStreamInfo*/, null /*overridePhysicalCameraIds*/,
-                    null /* overridePhysicalCameraSizes) */);
+                    null/*overridePhysicalCameraId*/);
 
             outputSurfaces.ensureCapacity(outputConfigs.size());
             for (OutputConfiguration config : outputConfigs) {
@@ -788,10 +618,10 @@
             inputReader.setOnImageAvailableListener(inputReaderListener, mHandler);
             outputSurfaces.add(inputReader.getSurface());
 
-            assertTrue(String.format("Session configuration query %s failed",
-                    combination.getDescription()),
-                    checkSessionConfigurationWithSurfaces(mCamera, mHandler, outputSurfaces,
-                    inputConfig, SessionConfiguration.SESSION_REGULAR, /*expectedResult*/ true));
+            checkSessionConfigurationWithSurfaces(mCamera, mHandler, outputSurfaces,
+                    inputConfig, SessionConfiguration.SESSION_REGULAR, /*defaultSupport*/ true,
+                    String.format("Session configuration query %s failed",
+                    combination.getDescription()));
 
             // Verify we can create a reprocessable session with the input and all outputs.
             BlockingSessionCallback sessionListener = new BlockingSessionCallback();
@@ -2164,129 +1994,6 @@
         }
     }
 
-    private void testMultiCameraOutputCombination(String cameraId, int[] config,
-        MaxStreamSizes maxSizes) throws Exception {
-
-        // Timeout is relaxed by 1 second for LEGACY devices to reduce false positive rate in CTS
-        final int TIMEOUT_FOR_RESULT_MS = (mStaticInfo.isHardwareLevelLegacy()) ? 2000 : 1000;
-        final int MIN_RESULT_COUNT = 3;
-        Set<String> physicalCameraIds = mStaticInfo.getCharacteristics().getPhysicalCameraIds();
-
-        for (int i = 0; i < config.length; i += 2) {
-            int format = config[i];
-            int sizeLimit = config[i+1];
-            if (format != YUV && format != Y8 && format != RAW) {
-                continue;
-            }
-
-            // Find physical cameras with matching size.
-            Size targetSize = (format == YUV) ? maxSizes.maxYuvSizes[sizeLimit] :
-                    (format == Y8) ? maxSizes.maxY8Sizes[sizeLimit] :
-                    maxSizes.maxRawSize;
-            List<String> physicalCamerasForSize = new ArrayList<String>();
-            List<Size> physicalCameraSizes = new ArrayList<Size>();
-            for (String physicalId : physicalCameraIds) {
-                Size[] sizes = mAllStaticInfo.get(physicalId).getAvailableSizesForFormatChecked(
-                        format, StaticMetadata.StreamDirection.Output);
-                if (targetSize != null) {
-                    if (Arrays.asList(sizes).contains(targetSize)) {
-                        physicalCameraSizes.add(targetSize);
-                        physicalCamerasForSize.add(physicalId);
-                    }
-                } else if (format == RAW && sizes.length > 0) {
-                    physicalCamerasForSize.add(physicalId);
-                    physicalCameraSizes.add(CameraTestUtils.getMaxSize(sizes));
-                }
-                if (physicalCamerasForSize.size() == 2) {
-                    break;
-                }
-            }
-            if (physicalCamerasForSize.size() < 2) {
-                continue;
-            }
-
-            // Set up outputs
-            List<OutputConfiguration> outputConfigs = new ArrayList<OutputConfiguration>();
-            List<SurfaceTexture> privTargets = new ArrayList<SurfaceTexture>();
-            List<ImageReader> jpegTargets = new ArrayList<ImageReader>();
-            List<ImageReader> yuvTargets = new ArrayList<ImageReader>();
-            List<ImageReader> y8Targets = new ArrayList<ImageReader>();
-            List<ImageReader> rawTargets = new ArrayList<ImageReader>();
-            List<ImageReader> heicTargets = new ArrayList<ImageReader>();
-
-            setupConfigurationTargets(config, maxSizes, privTargets, jpegTargets, yuvTargets,
-                    y8Targets, rawTargets, heicTargets, outputConfigs, MIN_RESULT_COUNT, i,
-                    physicalCamerasForSize, physicalCameraSizes);
-
-            boolean haveSession = false;
-            try {
-                CaptureRequest.Builder requestBuilder =
-                        mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
-
-                for (OutputConfiguration c : outputConfigs) {
-                    requestBuilder.addTarget(c.getSurface());
-                }
-
-                CameraCaptureSession.CaptureCallback mockCaptureCallback =
-                        mock(CameraCaptureSession.CaptureCallback.class);
-
-                assertTrue(String.format("Session configuration query %s failed",
-                        MaxStreamSizes.configToString(config)),
-                        checkSessionConfiguration(mCamera, mHandler, outputConfigs,
-                        /*inputConfig*/ null, SessionConfiguration.SESSION_REGULAR,
-                        /*expectedResult*/ true));
-
-                createSessionByConfigs(outputConfigs);
-                haveSession = true;
-                CaptureRequest request = requestBuilder.build();
-                mCameraSession.setRepeatingRequest(request, mockCaptureCallback, mHandler);
-
-                verify(mockCaptureCallback,
-                        timeout(TIMEOUT_FOR_RESULT_MS * MIN_RESULT_COUNT).atLeast(MIN_RESULT_COUNT))
-                        .onCaptureCompleted(
-                            eq(mCameraSession),
-                            eq(request),
-                            isA(TotalCaptureResult.class));
-                verify(mockCaptureCallback, never()).
-                        onCaptureFailed(
-                            eq(mCameraSession),
-                            eq(request),
-                            isA(CaptureFailure.class));
-
-            } catch (Throwable e) {
-                mCollector.addMessage(String.format("Output combination %s failed due to: %s",
-                        MaxStreamSizes.configToString(config), e.getMessage()));
-            }
-            if (haveSession) {
-                try {
-                    Log.i(TAG, String.format("Done with camera %s, config %s, closing session",
-                                    cameraId, MaxStreamSizes.configToString(config)));
-                    stopCapture(/*fast*/false);
-                } catch (Throwable e) {
-                    mCollector.addMessage(
-                        String.format("Closing down for output combination %s failed due to: %s",
-                                MaxStreamSizes.configToString(config), e.getMessage()));
-                }
-            }
-
-            for (SurfaceTexture target : privTargets) {
-                target.release();
-            }
-            for (ImageReader target : jpegTargets) {
-                target.close();
-            }
-            for (ImageReader target : yuvTargets) {
-                target.close();
-            }
-            for (ImageReader target : y8Targets) {
-                target.close();
-            }
-            for (ImageReader target : rawTargets) {
-                target.close();
-            }
-        }
-    }
-
     private void setupConfigurationTargets(int[] configs, MaxStreamSizes maxSizes,
             List<SurfaceTexture> privTargets, List<ImageReader> jpegTargets,
             List<ImageReader> yuvTargets, List<ImageReader> y8Targets,
diff --git a/tests/camera/src/android/hardware/camera2/cts/SurfaceViewPreviewTest.java b/tests/camera/src/android/hardware/camera2/cts/SurfaceViewPreviewTest.java
index d59cf7d..3d84d38 100644
--- a/tests/camera/src/android/hardware/camera2/cts/SurfaceViewPreviewTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/SurfaceViewPreviewTest.java
@@ -527,10 +527,9 @@
         }
 
         // Check whether session configuration is supported
-        assertTrue("Deferred session configuration query failed",
-                CameraTestUtils.checkSessionConfiguration(mCamera, mHandler, outputSurfaces,
+        CameraTestUtils.checkSessionConfigurationSupported(mCamera, mHandler, outputSurfaces,
                 /*inputConfig*/ null, SessionConfiguration.SESSION_REGULAR,
-                /*expectedResult*/ true));
+                /*defaultSupport*/ true, "Deferred session configuration query failed");
 
         // Create session
 
diff --git a/tests/camera/src/android/hardware/camera2/cts/testcases/Camera2AndroidTestCase.java b/tests/camera/src/android/hardware/camera2/cts/testcases/Camera2AndroidTestCase.java
index 194266a..56e08a0 100644
--- a/tests/camera/src/android/hardware/camera2/cts/testcases/Camera2AndroidTestCase.java
+++ b/tests/camera/src/android/hardware/camera2/cts/testcases/Camera2AndroidTestCase.java
@@ -431,12 +431,12 @@
         }
     }
 
-    protected boolean checkImageReaderSessionConfiguration() throws Exception {
+    protected void checkImageReaderSessionConfiguration(String msg) throws Exception {
         List<OutputConfiguration> outputConfigs = new ArrayList<OutputConfiguration>();
         outputConfigs.add(new OutputConfiguration(mReaderSurface));
 
-        return checkSessionConfiguration(mCamera, mHandler, outputConfigs, /*inputConfig*/ null,
-                SessionConfiguration.SESSION_REGULAR, /*expectedResult*/ true);
+        checkSessionConfigurationSupported(mCamera, mHandler, outputConfigs, /*inputConfig*/ null,
+                SessionConfiguration.SESSION_REGULAR, /*expectedResult*/ true, msg);
     }
 
     protected CaptureRequest prepareCaptureRequest() throws Exception {
diff --git a/tests/camera/src/android/hardware/camera2/cts/testcases/Camera2MultiViewTestCase.java b/tests/camera/src/android/hardware/camera2/cts/testcases/Camera2MultiViewTestCase.java
index 20ba949..672a01e 100644
--- a/tests/camera/src/android/hardware/camera2/cts/testcases/Camera2MultiViewTestCase.java
+++ b/tests/camera/src/android/hardware/camera2/cts/testcases/Camera2MultiViewTestCase.java
@@ -545,10 +545,9 @@
         public int startPreviewWithConfigs(List<OutputConfiguration> outputConfigs,
                 CaptureCallback listener)
                 throws Exception {
-            assertTrue("Session configuration query should not fail",
-                    checkSessionConfiguration(mCamera, mHandler, outputConfigs,
+            checkSessionConfigurationSupported(mCamera, mHandler, outputConfigs,
                     /*inputConfig*/ null, SessionConfiguration.SESSION_REGULAR,
-                    /*expectedResult*/ true));
+                    /*defaultSupport*/ true, "Session configuration query should not fail");
             createSessionWithConfigs(outputConfigs);
 
             CaptureRequest.Builder captureBuilder =
@@ -580,9 +579,9 @@
         }
 
         public boolean isSessionConfigurationSupported(List<OutputConfiguration> configs) {
-            return checkSessionConfiguration(mCamera, mHandler, configs,
+            return isSessionConfigSupported(mCamera, mHandler, configs,
                     /*inputConig*/ null, SessionConfiguration.SESSION_REGULAR,
-                    /*expectedResult*/ true);
+                    /*expectedResult*/ true).configSupported;
         }
 
         public void capture(CaptureRequest request, CaptureCallback listener)
diff --git a/tests/camera/utils/src/android/hardware/camera2/cts/CameraTestUtils.java b/tests/camera/utils/src/android/hardware/camera2/cts/CameraTestUtils.java
index 3f1ac64..6f2d488 100644
--- a/tests/camera/utils/src/android/hardware/camera2/cts/CameraTestUtils.java
+++ b/tests/camera/utils/src/android/hardware/camera2/cts/CameraTestUtils.java
@@ -2679,27 +2679,51 @@
         }
     }
 
-    /**
-     * Query whether a particular stream combination is supported.
-     */
-    public static boolean checkSessionConfigurationWithSurfaces(CameraDevice camera,
-            Handler handler, List<Surface> outputSurfaces, InputConfiguration inputConfig,
-            int operatingMode, boolean expectedResult) {
-        List<OutputConfiguration> outConfigurations = new ArrayList<>(outputSurfaces.size());
-        for (Surface surface : outputSurfaces) {
-            outConfigurations.add(new OutputConfiguration(surface));
-        }
+    public final static class SessionConfigSupport {
+        public final boolean error;
+        public final boolean callSupported;
+        public final boolean configSupported;
 
-        return checkSessionConfiguration(camera, handler, outConfigurations, inputConfig,
-                operatingMode, expectedResult);
+        public SessionConfigSupport(boolean error,
+                boolean callSupported, boolean configSupported) {
+            this.error = error;
+            this.callSupported = callSupported;
+            this.configSupported = configSupported;
+        }
     }
 
     /**
      * Query whether a particular stream combination is supported.
      */
-    public static boolean checkSessionConfiguration(CameraDevice camera, Handler handler,
-            List<OutputConfiguration> outputConfigs, InputConfiguration inputConfig,
-            int operatingMode, boolean expectedResult) {
+    public static void checkSessionConfigurationWithSurfaces(CameraDevice camera,
+            Handler handler, List<Surface> outputSurfaces, InputConfiguration inputConfig,
+            int operatingMode, boolean defaultSupport, String msg) {
+        List<OutputConfiguration> outConfigurations = new ArrayList<>(outputSurfaces.size());
+        for (Surface surface : outputSurfaces) {
+            outConfigurations.add(new OutputConfiguration(surface));
+        }
+
+        checkSessionConfigurationSupported(camera, handler, outConfigurations,
+                inputConfig, operatingMode, defaultSupport, msg);
+    }
+
+    public static void checkSessionConfigurationSupported(CameraDevice camera,
+            Handler handler, List<OutputConfiguration> outputConfigs,
+            InputConfiguration inputConfig, int operatingMode, boolean defaultSupport,
+            String msg) {
+        SessionConfigSupport sessionConfigSupported =
+                isSessionConfigSupported(camera, handler, outputConfigs, inputConfig,
+                operatingMode, defaultSupport);
+
+        assertTrue(msg, !sessionConfigSupported.error && sessionConfigSupported.configSupported);
+    }
+
+    /**
+     * Query whether a particular stream combination is supported.
+     */
+    public static SessionConfigSupport isSessionConfigSupported(CameraDevice camera,
+            Handler handler, List<OutputConfiguration> outputConfigs,
+            InputConfiguration inputConfig, int operatingMode, boolean defaultSupport) {
         boolean ret;
         BlockingSessionCallback sessionListener = new BlockingSessionCallback();
 
@@ -2712,15 +2736,19 @@
         try {
             ret = camera.isSessionConfigurationSupported(sessionConfig);
         } catch (UnsupportedOperationException e) {
-            // Camera doesn't support session configuration query, return expected result
-            return true;
+            // Camera doesn't support session configuration query
+            return new SessionConfigSupport(false/*error*/,
+                    false/*callSupported*/, defaultSupport/*configSupported*/);
         } catch (IllegalArgumentException e) {
-            return false;
+            return new SessionConfigSupport(true/*error*/,
+                    false/*callSupported*/, false/*configSupported*/);
         } catch (android.hardware.camera2.CameraAccessException e) {
-            return false;
+            return new SessionConfigSupport(true/*error*/,
+                    false/*callSupported*/, false/*configSupported*/);
         }
 
-        return !(expectedResult ^ ret);
+        return new SessionConfigSupport(false/*error*/,
+                true/*callSupported*/, ret/*configSupported*/);
     }
 
     /**