Merge "Camera: Add a new tag for multi-resolution stream support" into sc-dev
diff --git a/camera/metadata/3.6/types.hal b/camera/metadata/3.6/types.hal
index 709cfb3..97cac7c 100644
--- a/camera/metadata/3.6/types.hal
+++ b/camera/metadata/3.6/types.hal
@@ -131,6 +131,12 @@
      */
     ANDROID_SCALER_AVAILABLE_INPUT_OUTPUT_FORMATS_MAP_MAXIMUM_RESOLUTION,
 
+    /** android.scaler.multiResolutionStreamSupported [static, enum, ndk_public]
+     *
+     * <p>Whether the camera device supports multi-resolution input or output streams</p>
+     */
+    ANDROID_SCALER_MULTI_RESOLUTION_STREAM_SUPPORTED,
+
     ANDROID_SCALER_END_3_6,
 
     /** android.sensor.opaqueRawSizeMaximumResolution [static, int32[], system]
@@ -334,6 +340,14 @@
     ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_MAXIMUM_RESOLUTION_INPUT,
 };
 
+/** android.scaler.multiResolutionStreamSupported enumeration values
+ * @see ANDROID_SCALER_MULTI_RESOLUTION_STREAM_SUPPORTED
+ */
+enum CameraMetadataEnumAndroidScalerMultiResolutionStreamSupported : uint32_t {
+    ANDROID_SCALER_MULTI_RESOLUTION_STREAM_SUPPORTED_FALSE,
+    ANDROID_SCALER_MULTI_RESOLUTION_STREAM_SUPPORTED_TRUE,
+};
+
 /** android.sensor.pixelMode enumeration values
  * @see ANDROID_SENSOR_PIXEL_MODE
  */
diff --git a/camera/provider/2.4/vts/functional/VtsHalCameraProviderV2_4TargetTest.cpp b/camera/provider/2.4/vts/functional/VtsHalCameraProviderV2_4TargetTest.cpp
index ed3b1fa..7bb300e 100644
--- a/camera/provider/2.4/vts/functional/VtsHalCameraProviderV2_4TargetTest.cpp
+++ b/camera/provider/2.4/vts/functional/VtsHalCameraProviderV2_4TargetTest.cpp
@@ -578,7 +578,7 @@
      uint32_t id;
      ASSERT_TRUE(parseProviderName(service_name, &mProviderType, &id));
 
-     castProvider(mProvider, &mProvider2_5, &mProvider2_6);
+     castProvider(mProvider, &mProvider2_5, &mProvider2_6, &mProvider2_7);
      notifyDeviceState(provider::V2_5::DeviceState::NORMAL);
  }
  virtual void TearDown() override {}
@@ -759,22 +759,24 @@
             ::android::sp<ICameraDevice> *device = nullptr/*out*/);
     void castProvider(const sp<provider::V2_4::ICameraProvider>& provider,
                       sp<provider::V2_5::ICameraProvider>* provider2_5 /*out*/,
-                      sp<provider::V2_6::ICameraProvider>* provider2_6 /*out*/);
+                      sp<provider::V2_6::ICameraProvider>* provider2_6 /*out*/,
+                      sp<provider::V2_7::ICameraProvider>* provider2_7 /*out*/);
     void castSession(const sp<ICameraDeviceSession> &session, int32_t deviceVersion,
             sp<device::V3_3::ICameraDeviceSession> *session3_3 /*out*/,
             sp<device::V3_4::ICameraDeviceSession> *session3_4 /*out*/,
             sp<device::V3_5::ICameraDeviceSession> *session3_5 /*out*/,
             sp<device::V3_6::ICameraDeviceSession> *session3_6 /*out*/,
             sp<device::V3_7::ICameraDeviceSession> *session3_7 /*out*/);
-    void castDevice(const sp<device::V3_2::ICameraDevice> &device, int32_t deviceVersion,
-            sp<device::V3_5::ICameraDevice> *device3_5/*out*/);
-    void castDevice3_7(const sp<device::V3_2::ICameraDevice>& device, int32_t deviceVersion,
-                       sp<device::V3_7::ICameraDevice>* device3_7 /*out*/);
-    void createStreamConfiguration(const ::android::hardware::hidl_vec<V3_2::Stream>& streams3_2,
+    void castDevice(const sp<device::V3_2::ICameraDevice>& device, int32_t deviceVersion,
+                    sp<device::V3_5::ICameraDevice>* device3_5 /*out*/,
+                    sp<device::V3_7::ICameraDevice>* device3_7 /*out*/);
+    void createStreamConfiguration(
+            const ::android::hardware::hidl_vec<V3_2::Stream>& streams3_2,
             StreamConfigurationMode configMode,
-            ::android::hardware::camera::device::V3_2::StreamConfiguration *config3_2,
-            ::android::hardware::camera::device::V3_4::StreamConfiguration *config3_4,
-            ::android::hardware::camera::device::V3_5::StreamConfiguration *config3_5,
+            ::android::hardware::camera::device::V3_2::StreamConfiguration* config3_2,
+            ::android::hardware::camera::device::V3_4::StreamConfiguration* config3_4,
+            ::android::hardware::camera::device::V3_5::StreamConfiguration* config3_5,
+            ::android::hardware::camera::device::V3_7::StreamConfiguration* config3_7,
             uint32_t jpegBufferSize = 0);
 
     void configureOfflineStillStream(const std::string &name, int32_t deviceVersion,
@@ -834,7 +836,8 @@
             uint32_t* partialResultCount /*out*/, bool* useHalBufManager /*out*/,
             sp<DeviceCb>* cb /*out*/, uint32_t streamConfigCounter = 0);
 
-    void verifyLogicalCameraMetadata(const std::string& cameraName,
+    void verifyLogicalOrUltraHighResCameraMetadata(
+            const std::string& cameraName,
             const ::android::sp<::android::hardware::camera::device::V3_2::ICameraDevice>& device,
             const CameraMetadata& chars, int deviceVersion,
             const hidl_vec<hidl_string>& deviceNames);
@@ -845,8 +848,11 @@
     void verifyMonochromeCharacteristics(const CameraMetadata& chars, int deviceVersion);
     void verifyMonochromeCameraResult(
             const ::android::hardware::camera::common::V1_0::helper::CameraMetadata& metadata);
-    void verifyStreamCombination(sp<device::V3_5::ICameraDevice> cameraDevice3_5,
-            const ::android::hardware::camera::device::V3_4::StreamConfiguration &config3_4,
+    void verifyStreamCombination(
+            sp<device::V3_7::ICameraDevice> cameraDevice3_7,
+            const ::android::hardware::camera::device::V3_7::StreamConfiguration& config3_7,
+            sp<device::V3_5::ICameraDevice> cameraDevice3_5,
+            const ::android::hardware::camera::device::V3_4::StreamConfiguration& config3_4,
             bool expectedStatus, bool expectStreamCombQuery);
     void verifyLogicalCameraResult(const camera_metadata_t* staticMetadata,
             const ::android::hardware::camera::common::V1_0::helper::CameraMetadata& resultMetadata);
@@ -915,6 +921,11 @@
     static Status isMonochromeCamera(const camera_metadata_t *staticMeta);
     static Status getSystemCameraKind(const camera_metadata_t* staticMeta,
                                       SystemCameraKind* systemCameraKind);
+    static void getMultiResolutionStreamConfigurations(
+            camera_metadata_ro_entry* multiResStreamConfigs,
+            camera_metadata_ro_entry* streamConfigs,
+            camera_metadata_ro_entry* maxResolutionStreamConfigs,
+            const camera_metadata_t* staticMetadata);
 
     static V3_2::DataspaceFlags getDataspace(PixelFormat format);
 
@@ -1047,6 +1058,7 @@
     sp<ICameraProvider> mProvider;
     sp<::android::hardware::camera::provider::V2_5::ICameraProvider> mProvider2_5;
     sp<::android::hardware::camera::provider::V2_6::ICameraProvider> mProvider2_6;
+    sp<::android::hardware::camera::provider::V2_7::ICameraProvider> mProvider2_7;
 
     // Camera provider type.
     std::string mProviderType;
@@ -2805,8 +2817,8 @@
                     verifyCameraCharacteristics(status, chars);
                     verifyMonochromeCharacteristics(chars, deviceVersion);
                     verifyRecommendedConfigs(chars);
-                    verifyLogicalCameraMetadata(name, device3_x, chars, deviceVersion,
-                            cameraDeviceNames);
+                    verifyLogicalOrUltraHighResCameraMetadata(name, device3_x, chars, deviceVersion,
+                                                              cameraDeviceNames);
                 });
                 ASSERT_TRUE(ret.isOk());
 
@@ -3264,11 +3276,12 @@
         sp<device::V3_7::ICameraDeviceSession> session3_7;
         sp<device::V3_2::ICameraDevice> cameraDevice;
         sp<device::V3_5::ICameraDevice> cameraDevice3_5;
+        sp<device::V3_7::ICameraDevice> cameraDevice3_7;
         openEmptyDeviceSession(name, mProvider,
                 &session /*out*/, &staticMeta /*out*/, &cameraDevice /*out*/);
         castSession(session, deviceVersion, &session3_3, &session3_4, &session3_5,
                 &session3_6, &session3_7);
-        castDevice(cameraDevice, deviceVersion, &cameraDevice3_5);
+        castDevice(cameraDevice, deviceVersion, &cameraDevice3_5, &cameraDevice3_7);
 
         outputStreams.clear();
         ASSERT_EQ(Status::OK, getAvailableOutputStreams(staticMeta, outputStreams));
@@ -3292,15 +3305,29 @@
                              dataspaceFlag,
                              StreamRotation::ROTATION_0};
             ::android::hardware::hidl_vec<V3_2::Stream> streams3_2 = {stream3_2};
+            ::android::hardware::camera::device::V3_7::StreamConfiguration config3_7;
             ::android::hardware::camera::device::V3_5::StreamConfiguration config3_5;
             ::android::hardware::camera::device::V3_4::StreamConfiguration config3_4;
             ::android::hardware::camera::device::V3_2::StreamConfiguration config3_2;
-            createStreamConfiguration(streams3_2, StreamConfigurationMode::NORMAL_MODE,
-                                      &config3_2, &config3_4, &config3_5, jpegBufferSize);
+            createStreamConfiguration(streams3_2, StreamConfigurationMode::NORMAL_MODE, &config3_2,
+                                      &config3_4, &config3_5, &config3_7, jpegBufferSize);
+
             if (session3_5 != nullptr) {
                 bool expectStreamCombQuery = (isLogicalMultiCamera(staticMeta) == Status::OK);
-                verifyStreamCombination(cameraDevice3_5, config3_4,
-                        /*expectedStatus*/ true, expectStreamCombQuery);
+                verifyStreamCombination(cameraDevice3_7, config3_7, cameraDevice3_5, config3_4,
+                                        /*expectedStatus*/ true, expectStreamCombQuery);
+            }
+
+            if (session3_7 != nullptr) {
+                config3_7.streamConfigCounter = streamConfigCounter++;
+                ret = session3_7->configureStreams_3_7(
+                        config3_7,
+                        [streamId](Status s, device::V3_6::HalStreamConfiguration halConfig) {
+                            ASSERT_EQ(Status::OK, s);
+                            ASSERT_EQ(1u, halConfig.streams.size());
+                            ASSERT_EQ(halConfig.streams[0].v3_4.v3_3.v3_2.id, streamId);
+                        });
+            } else if (session3_5 != nullptr) {
                 config3_5.streamConfigCounter = streamConfigCounter++;
                 ret = session3_5->configureStreams_3_5(config3_5,
                         [streamId](Status s, device::V3_4::HalStreamConfiguration halConfig) {
@@ -3352,6 +3379,8 @@
         sp<device::V3_7::ICameraDeviceSession> session3_7;
         sp<device::V3_2::ICameraDevice> cameraDevice;
         sp<device::V3_5::ICameraDevice> cameraDevice3_5;
+        sp<device::V3_7::ICameraDevice> cameraDevice3_7;
+        ::android::hardware::camera::device::V3_7::StreamConfiguration config3_7;
         ::android::hardware::camera::device::V3_5::StreamConfiguration config3_5;
         ::android::hardware::camera::device::V3_4::StreamConfiguration config3_4;
         ::android::hardware::camera::device::V3_2::StreamConfiguration config3_2;
@@ -3388,7 +3417,7 @@
                                    &cti.staticMeta /*out*/, &cti.cameraDevice /*out*/);
             castSession(cti.session, deviceVersion, &cti.session3_3, &cti.session3_4,
                         &cti.session3_5, &cti.session3_6, &cti.session3_7);
-            castDevice(cti.cameraDevice, deviceVersion, &cti.cameraDevice3_5);
+            castDevice(cti.cameraDevice, deviceVersion, &cti.cameraDevice3_5, &cti.cameraDevice3_7);
 
             outputStreams.clear();
             ASSERT_EQ(Status::OK, getMandatoryConcurrentStreams(cti.staticMeta, &outputStreams));
@@ -3420,7 +3449,7 @@
             // Add the created stream configs to cameraIdsAndStreamCombinations
             createStreamConfiguration(streams3_2, StreamConfigurationMode::NORMAL_MODE,
                                       &cti.config3_2, &cti.config3_4, &cti.config3_5,
-                                      jpegBufferSize);
+                                      &cti.config3_7, jpegBufferSize);
 
             cti.config3_5.streamConfigCounter = outputStreams.size();
             CameraIdAndStreamCombination cameraIdAndStreamCombination;
@@ -3443,8 +3472,19 @@
         for (const auto& cti : cameraTestInfos) {
             if (cti.session3_5 != nullptr) {
                 bool expectStreamCombQuery = (isLogicalMultiCamera(cti.staticMeta) == Status::OK);
-                verifyStreamCombination(cti.cameraDevice3_5, cti.config3_4,
+                verifyStreamCombination(cti.cameraDevice3_7, cti.config3_7, cti.cameraDevice3_5,
+                                        cti.config3_4,
                                         /*expectedStatus*/ true, expectStreamCombQuery);
+            }
+
+            if (cti.session3_7 != nullptr) {
+                ret = cti.session3_7->configureStreams_3_7(
+                        cti.config3_7,
+                        [&cti](Status s, device::V3_6::HalStreamConfiguration halConfig) {
+                            ASSERT_EQ(Status::OK, s);
+                            ASSERT_EQ(cti.config3_7.streams.size(), halConfig.streams.size());
+                        });
+            } else if (cti.session3_5 != nullptr) {
                 ret = cti.session3_5->configureStreams_3_5(
                         cti.config3_5,
                         [&cti](Status s, device::V3_4::HalStreamConfiguration halConfig) {
@@ -3508,11 +3548,12 @@
         sp<device::V3_7::ICameraDeviceSession> session3_7;
         sp<device::V3_2::ICameraDevice> cameraDevice;
         sp<device::V3_5::ICameraDevice> cameraDevice3_5;
+        sp<device::V3_7::ICameraDevice> cameraDevice3_7;
         openEmptyDeviceSession(name, mProvider, &session /*out*/, &staticMeta /*out*/,
                 &cameraDevice /*out*/);
         castSession(session, deviceVersion, &session3_3, &session3_4, &session3_5,
                 &session3_6, &session3_7);
-        castDevice(cameraDevice, deviceVersion, &cameraDevice3_5);
+        castDevice(cameraDevice, deviceVersion, &cameraDevice3_5, &cameraDevice3_7);
 
         outputStreams.clear();
         ASSERT_EQ(Status::OK, getAvailableOutputStreams(staticMeta, outputStreams));
@@ -3533,14 +3574,26 @@
                          StreamRotation::ROTATION_0};
         uint32_t streamConfigCounter = 0;
         ::android::hardware::hidl_vec<V3_2::Stream> streams = {stream3_2};
+        ::android::hardware::camera::device::V3_7::StreamConfiguration config3_7;
         ::android::hardware::camera::device::V3_5::StreamConfiguration config3_5;
         ::android::hardware::camera::device::V3_4::StreamConfiguration config3_4;
         ::android::hardware::camera::device::V3_2::StreamConfiguration config3_2;
-        createStreamConfiguration(streams, StreamConfigurationMode::NORMAL_MODE,
-                                  &config3_2, &config3_4, &config3_5, jpegBufferSize);
+        createStreamConfiguration(streams, StreamConfigurationMode::NORMAL_MODE, &config3_2,
+                                  &config3_4, &config3_5, &config3_7, jpegBufferSize);
+
         if (session3_5 != nullptr) {
-            verifyStreamCombination(cameraDevice3_5, config3_4, /*expectedStatus*/ false,
-                    /*expectStreamCombQuery*/false);
+            verifyStreamCombination(cameraDevice3_7, config3_7, cameraDevice3_5, config3_4,
+                                    /*expectedStatus*/ false, /*expectStreamCombQuery*/ false);
+        }
+
+        if (session3_7 != nullptr) {
+            config3_7.streamConfigCounter = streamConfigCounter++;
+            ret = session3_7->configureStreams_3_7(
+                    config3_7, [](Status s, device::V3_6::HalStreamConfiguration) {
+                        ASSERT_TRUE((Status::ILLEGAL_ARGUMENT == s) ||
+                                    (Status::INTERNAL_ERROR == s));
+                    });
+        } else if (session3_5 != nullptr) {
             config3_5.streamConfigCounter = streamConfigCounter++;
             ret = session3_5->configureStreams_3_5(config3_5,
                     [](Status s, device::V3_4::HalStreamConfiguration) {
@@ -3553,7 +3606,7 @@
                         ASSERT_TRUE((Status::ILLEGAL_ARGUMENT == s) ||
                                 (Status::INTERNAL_ERROR == s));
                     });
-        } else if(session3_3 != nullptr) {
+        } else if (session3_3 != nullptr) {
             ret = session3_3->configureStreams_3_3(config3_2,
                     [](Status s, device::V3_3::HalStreamConfiguration) {
                         ASSERT_TRUE((Status::ILLEGAL_ARGUMENT == s) ||
@@ -3577,8 +3630,8 @@
                   0,
                   StreamRotation::ROTATION_0};
         streams[0] = stream3_2;
-        createStreamConfiguration(streams, StreamConfigurationMode::NORMAL_MODE,
-                &config3_2, &config3_4, &config3_5, jpegBufferSize);
+        createStreamConfiguration(streams, StreamConfigurationMode::NORMAL_MODE, &config3_2,
+                                  &config3_4, &config3_5, &config3_7, jpegBufferSize);
         if (session3_5 != nullptr) {
             config3_5.streamConfigCounter = streamConfigCounter++;
             ret = session3_5->configureStreams_3_5(config3_5, [](Status s,
@@ -3613,8 +3666,8 @@
                       0,
                       StreamRotation::ROTATION_0};
             streams[0] = stream3_2;
-            createStreamConfiguration(streams, StreamConfigurationMode::NORMAL_MODE,
-                    &config3_2, &config3_4, &config3_5, jpegBufferSize);
+            createStreamConfiguration(streams, StreamConfigurationMode::NORMAL_MODE, &config3_2,
+                                      &config3_4, &config3_5, &config3_7, jpegBufferSize);
             if (session3_5 != nullptr) {
                 config3_5.streamConfigCounter = streamConfigCounter++;
                 ret = session3_5->configureStreams_3_5(config3_5,
@@ -3648,8 +3701,8 @@
                       0,
                       static_cast<StreamRotation>(UINT32_MAX)};
             streams[0] = stream3_2;
-            createStreamConfiguration(streams, StreamConfigurationMode::NORMAL_MODE,
-                    &config3_2, &config3_4, &config3_5, jpegBufferSize);
+            createStreamConfiguration(streams, StreamConfigurationMode::NORMAL_MODE, &config3_2,
+                                      &config3_4, &config3_5, &config3_7, jpegBufferSize);
             if (session3_5 != nullptr) {
                 config3_5.streamConfigCounter = streamConfigCounter++;
                 ret = session3_5->configureStreams_3_5(config3_5,
@@ -3708,11 +3761,12 @@
         sp<device::V3_7::ICameraDeviceSession> session3_7;
         sp<device::V3_2::ICameraDevice> cameraDevice;
         sp<device::V3_5::ICameraDevice> cameraDevice3_5;
+        sp<device::V3_7::ICameraDevice> cameraDevice3_7;
         openEmptyDeviceSession(name, mProvider, &session /*out*/, &staticMeta /*out*/,
                 &cameraDevice /*out*/);
         castSession(session, deviceVersion, &session3_3, &session3_4, &session3_5,
                 &session3_6, &session3_7);
-        castDevice(cameraDevice, deviceVersion, &cameraDevice3_5);
+        castDevice(cameraDevice, deviceVersion, &cameraDevice3_5, &cameraDevice3_7);
 
         Status rc = isZSLModeAvailable(staticMeta);
         if (Status::METHOD_NOT_SUPPORTED == rc) {
@@ -3797,14 +3851,27 @@
 
                 ::android::hardware::hidl_vec<V3_2::Stream> streams = {inputStream, zslStream,
                                                                  outputStream};
+                ::android::hardware::camera::device::V3_7::StreamConfiguration config3_7;
                 ::android::hardware::camera::device::V3_5::StreamConfiguration config3_5;
                 ::android::hardware::camera::device::V3_4::StreamConfiguration config3_4;
                 ::android::hardware::camera::device::V3_2::StreamConfiguration config3_2;
-                createStreamConfiguration(streams, StreamConfigurationMode::NORMAL_MODE,
-                                          &config3_2, &config3_4, &config3_5, jpegBufferSize);
+                createStreamConfiguration(streams, StreamConfigurationMode::NORMAL_MODE, &config3_2,
+                                          &config3_4, &config3_5, &config3_7, jpegBufferSize);
                 if (session3_5 != nullptr) {
-                    verifyStreamCombination(cameraDevice3_5, config3_4,
-                            /*expectedStatus*/ true, /*expectStreamCombQuery*/ false);
+                    verifyStreamCombination(cameraDevice3_7, config3_7, cameraDevice3_5, config3_4,
+                                            /*expectedStatus*/ true,
+                                            /*expectStreamCombQuery*/ false);
+                }
+
+                if (session3_7 != nullptr) {
+                    config3_7.streamConfigCounter = streamConfigCounter++;
+                    ret = session3_7->configureStreams_3_7(
+                            config3_7,
+                            [](Status s, device::V3_6::HalStreamConfiguration halConfig) {
+                                ASSERT_EQ(Status::OK, s);
+                                ASSERT_EQ(3u, halConfig.streams.size());
+                            });
+                } else if (session3_5 != nullptr) {
                     config3_5.streamConfigCounter = streamConfigCounter++;
                     ret = session3_5->configureStreams_3_5(config3_5,
                             [](Status s, device::V3_4::HalStreamConfiguration halConfig) {
@@ -3927,6 +3994,7 @@
         ::android::hardware::hidl_vec<V3_4::Stream> streams = {previewStream};
         ::android::hardware::camera::device::V3_4::StreamConfiguration config;
         ::android::hardware::camera::device::V3_5::StreamConfiguration config3_5;
+        ::android::hardware::camera::device::V3_7::StreamConfiguration config3_7;
         config.streams = streams;
         config.operationMode = StreamConfigurationMode::NORMAL_MODE;
         modifiedSessionParams = sessionParams;
@@ -3935,6 +4003,13 @@
                 get_camera_metadata_size(sessionParamsBuffer));
         config3_5.v3_4 = config;
         config3_5.streamConfigCounter = 0;
+        config3_7.streams = {{previewStream, -1, {ANDROID_SENSOR_PIXEL_MODE_DEFAULT}}};
+        config3_7.operationMode = config.operationMode;
+        config3_7.sessionParams.setToExternal(reinterpret_cast<uint8_t*>(sessionParamsBuffer),
+                                              get_camera_metadata_size(sessionParamsBuffer));
+        config3_7.streamConfigCounter = 0;
+        config3_7.multiResolutionInputImage = false;
+
         if (session3_5 != nullptr) {
             bool newSessionParamsAvailable = false;
             for (const auto& it : availableSessionKeys) {
@@ -3950,7 +4025,15 @@
                         modifiedSessionParamsBuffer);
                 modifiedSessionParams.acquire(modifiedSessionParamsBuffer);
             }
+        }
 
+        if (session3_7 != nullptr) {
+            ret = session3_7->configureStreams_3_7(
+                    config3_7, [](Status s, device::V3_6::HalStreamConfiguration halConfig) {
+                        ASSERT_EQ(Status::OK, s);
+                        ASSERT_EQ(1u, halConfig.streams.size());
+                    });
+        } else if (session3_5 != nullptr) {
             ret = session3_5->configureStreams_3_5(config3_5,
                     [](Status s, device::V3_4::HalStreamConfiguration halConfig) {
                         ASSERT_EQ(Status::OK, s);
@@ -4003,11 +4086,12 @@
         sp<device::V3_7::ICameraDeviceSession> session3_7;
         sp<device::V3_2::ICameraDevice> cameraDevice;
         sp<device::V3_5::ICameraDevice> cameraDevice3_5;
+        sp<device::V3_7::ICameraDevice> cameraDevice3_7;
         openEmptyDeviceSession(name, mProvider, &session /*out*/, &staticMeta /*out*/,
                 &cameraDevice /*out*/);
         castSession(session, deviceVersion, &session3_3, &session3_4, &session3_5,
                 &session3_6, &session3_7);
-        castDevice(cameraDevice, deviceVersion, &cameraDevice3_5);
+        castDevice(cameraDevice, deviceVersion, &cameraDevice3_5, &cameraDevice3_7);
 
         // Check if camera support depth only
         if (isDepthOnly(staticMeta)) {
@@ -4054,14 +4138,27 @@
                                      StreamRotation::ROTATION_0};
                 ::android::hardware::hidl_vec<V3_2::Stream> streams = {previewStream,
                                                                  blobStream};
+                ::android::hardware::camera::device::V3_7::StreamConfiguration config3_7;
                 ::android::hardware::camera::device::V3_5::StreamConfiguration config3_5;
                 ::android::hardware::camera::device::V3_4::StreamConfiguration config3_4;
                 ::android::hardware::camera::device::V3_2::StreamConfiguration config3_2;
-                createStreamConfiguration(streams, StreamConfigurationMode::NORMAL_MODE,
-                                          &config3_2, &config3_4, &config3_5, jpegBufferSize);
+                createStreamConfiguration(streams, StreamConfigurationMode::NORMAL_MODE, &config3_2,
+                                          &config3_4, &config3_5, &config3_7, jpegBufferSize);
                 if (session3_5 != nullptr) {
-                    verifyStreamCombination(cameraDevice3_5, config3_4,
-                            /*expectedStatus*/ true, /*expectStreamCombQuery*/ false);
+                    verifyStreamCombination(cameraDevice3_7, config3_7, cameraDevice3_5, config3_4,
+                                            /*expectedStatus*/ true,
+                                            /*expectStreamCombQuery*/ false);
+                }
+
+                if (session3_7 != nullptr) {
+                    config3_7.streamConfigCounter = streamConfigCounter++;
+                    ret = session3_7->configureStreams_3_7(
+                            config3_7,
+                            [](Status s, device::V3_6::HalStreamConfiguration halConfig) {
+                                ASSERT_EQ(Status::OK, s);
+                                ASSERT_EQ(2u, halConfig.streams.size());
+                            });
+                } else if (session3_5 != nullptr) {
                     config3_5.streamConfigCounter = streamConfigCounter++;
                     ret = session3_5->configureStreams_3_5(config3_5,
                             [](Status s, device::V3_4::HalStreamConfiguration halConfig) {
@@ -4123,11 +4220,12 @@
         sp<device::V3_7::ICameraDeviceSession> session3_7;
         sp<device::V3_2::ICameraDevice> cameraDevice;
         sp<device::V3_5::ICameraDevice> cameraDevice3_5;
+        sp<device::V3_7::ICameraDevice> cameraDevice3_7;
         openEmptyDeviceSession(name, mProvider, &session /*out*/, &staticMeta /*out*/,
                 &cameraDevice /*out*/);
         castSession(session, deviceVersion, &session3_3, &session3_4, &session3_5,
                 &session3_6, &session3_7);
-        castDevice(cameraDevice, deviceVersion, &cameraDevice3_5);
+        castDevice(cameraDevice, deviceVersion, &cameraDevice3_5, &cameraDevice3_7);
 
         Status rc = isConstrainedModeAvailable(staticMeta);
         if (Status::METHOD_NOT_SUPPORTED == rc) {
@@ -4152,14 +4250,27 @@
                          0,
                          StreamRotation::ROTATION_0};
         ::android::hardware::hidl_vec<V3_2::Stream> streams = {stream};
+        ::android::hardware::camera::device::V3_7::StreamConfiguration config3_7;
         ::android::hardware::camera::device::V3_5::StreamConfiguration config3_5;
         ::android::hardware::camera::device::V3_4::StreamConfiguration config3_4;
         ::android::hardware::camera::device::V3_2::StreamConfiguration config3_2;
         createStreamConfiguration(streams, StreamConfigurationMode::CONSTRAINED_HIGH_SPEED_MODE,
-                                  &config3_2, &config3_4, &config3_5);
+                                  &config3_2, &config3_4, &config3_5, &config3_7);
         if (session3_5 != nullptr) {
-            verifyStreamCombination(cameraDevice3_5, config3_4,
-                    /*expectedStatus*/ true, /*expectStreamCombQuery*/ false);
+            verifyStreamCombination(cameraDevice3_7, config3_7, cameraDevice3_5, config3_4,
+                                    /*expectedStatus*/ true, /*expectStreamCombQuery*/ false);
+        }
+
+        if (session3_7 != nullptr) {
+            config3_7.streamConfigCounter = streamConfigCounter++;
+            ret = session3_7->configureStreams_3_7(
+                    config3_7,
+                    [streamId](Status s, device::V3_6::HalStreamConfiguration halConfig) {
+                        ASSERT_EQ(Status::OK, s);
+                        ASSERT_EQ(1u, halConfig.streams.size());
+                        ASSERT_EQ(halConfig.streams[0].v3_4.v3_3.v3_2.id, streamId);
+                    });
+        } else if (session3_5 != nullptr) {
             config3_5.streamConfigCounter = streamConfigCounter++;
             ret = session3_5->configureStreams_3_5(config3_5,
                     [streamId](Status s, device::V3_4::HalStreamConfiguration halConfig) {
@@ -4201,8 +4312,15 @@
                   StreamRotation::ROTATION_0};
         streams[0] = stream;
         createStreamConfiguration(streams, StreamConfigurationMode::CONSTRAINED_HIGH_SPEED_MODE,
-                                  &config3_2, &config3_4, &config3_5);
-        if (session3_5 != nullptr) {
+                                  &config3_2, &config3_4, &config3_5, &config3_7);
+        if (session3_7 != nullptr) {
+            config3_7.streamConfigCounter = streamConfigCounter++;
+            ret = session3_7->configureStreams_3_7(
+                    config3_7, [](Status s, device::V3_6::HalStreamConfiguration) {
+                        ASSERT_TRUE((Status::ILLEGAL_ARGUMENT == s) ||
+                                    (Status::INTERNAL_ERROR == s));
+                    });
+        } else if (session3_5 != nullptr) {
             config3_5.streamConfigCounter = streamConfigCounter++;
             ret = session3_5->configureStreams_3_5(config3_5,
                     [](Status s, device::V3_4::HalStreamConfiguration) {
@@ -4240,8 +4358,14 @@
                   StreamRotation::ROTATION_0};
         streams[0] = stream;
         createStreamConfiguration(streams, StreamConfigurationMode::CONSTRAINED_HIGH_SPEED_MODE,
-                                  &config3_2, &config3_4, &config3_5);
-        if (session3_5 != nullptr) {
+                                  &config3_2, &config3_4, &config3_5, &config3_7);
+        if (session3_7 != nullptr) {
+            config3_7.streamConfigCounter = streamConfigCounter++;
+            ret = session3_7->configureStreams_3_7(
+                    config3_7, [](Status s, device::V3_6::HalStreamConfiguration) {
+                        ASSERT_EQ(Status::ILLEGAL_ARGUMENT, s);
+                    });
+        } else if (session3_5 != nullptr) {
             config3_5.streamConfigCounter = streamConfigCounter++;
             ret = session3_5->configureStreams_3_5(config3_5,
                     [](Status s, device::V3_4::HalStreamConfiguration) {
@@ -4275,8 +4399,14 @@
                   StreamRotation::ROTATION_0};
         streams[0] = stream;
         createStreamConfiguration(streams, StreamConfigurationMode::CONSTRAINED_HIGH_SPEED_MODE,
-                                  &config3_2, &config3_4, &config3_5);
-        if (session3_5 != nullptr) {
+                                  &config3_2, &config3_4, &config3_5, &config3_7);
+        if (session3_7 != nullptr) {
+            config3_7.streamConfigCounter = streamConfigCounter++;
+            ret = session3_7->configureStreams_3_7(
+                    config3_7, [](Status s, device::V3_6::HalStreamConfiguration) {
+                        ASSERT_EQ(Status::ILLEGAL_ARGUMENT, s);
+                    });
+        } else if (session3_5 != nullptr) {
             config3_5.streamConfigCounter = streamConfigCounter++;
             ret = session3_5->configureStreams_3_5(config3_5,
                     [](Status s, device::V3_4::HalStreamConfiguration) {
@@ -4337,11 +4467,12 @@
         sp<device::V3_7::ICameraDeviceSession> session3_7;
         sp<device::V3_2::ICameraDevice> cameraDevice;
         sp<device::V3_5::ICameraDevice> cameraDevice3_5;
+        sp<device::V3_7::ICameraDevice> cameraDevice3_7;
         openEmptyDeviceSession(name, mProvider, &session /*out*/, &staticMeta /*out*/,
                 &cameraDevice /*out*/);
         castSession(session, deviceVersion, &session3_3, &session3_4, &session3_5,
                 &session3_6, &session3_7);
-        castDevice(cameraDevice, deviceVersion, &cameraDevice3_5);
+        castDevice(cameraDevice, deviceVersion, &cameraDevice3_5, &cameraDevice3_7);
 
         // Check if camera support depth only
         if (isDepthOnly(staticMeta)) {
@@ -4388,14 +4519,27 @@
                                      static_cast<V3_2::DataspaceFlags>(Dataspace::V0_JFIF),
                                      StreamRotation::ROTATION_0};
                 ::android::hardware::hidl_vec<V3_2::Stream> streams = {videoStream, blobStream};
+                ::android::hardware::camera::device::V3_7::StreamConfiguration config3_7;
                 ::android::hardware::camera::device::V3_5::StreamConfiguration config3_5;
                 ::android::hardware::camera::device::V3_4::StreamConfiguration config3_4;
                 ::android::hardware::camera::device::V3_2::StreamConfiguration config3_2;
-                createStreamConfiguration(streams, StreamConfigurationMode::NORMAL_MODE,
-                                          &config3_2, &config3_4, &config3_5, jpegBufferSize);
+                createStreamConfiguration(streams, StreamConfigurationMode::NORMAL_MODE, &config3_2,
+                                          &config3_4, &config3_5, &config3_7, jpegBufferSize);
                 if (session3_5 != nullptr) {
-                    verifyStreamCombination(cameraDevice3_5, config3_4,
-                            /*expectedStatus*/ true, /*expectStreamCombQuery*/ false);
+                    verifyStreamCombination(cameraDevice3_7, config3_7, cameraDevice3_5, config3_4,
+                                            /*expectedStatus*/ true,
+                                            /*expectStreamCombQuery*/ false);
+                }
+
+                if (session3_7 != nullptr) {
+                    config3_7.streamConfigCounter = streamConfigCounter++;
+                    ret = session3_7->configureStreams_3_7(
+                            config3_7,
+                            [](Status s, device::V3_6::HalStreamConfiguration halConfig) {
+                                ASSERT_EQ(Status::OK, s);
+                                ASSERT_EQ(2u, halConfig.streams.size());
+                            });
+                } else if (session3_5 != nullptr) {
                     config3_5.streamConfigCounter = streamConfigCounter++;
                     ret = session3_5->configureStreams_3_5(config3_5,
                             [](Status s, device::V3_4::HalStreamConfiguration halConfig) {
@@ -6142,6 +6286,28 @@
     return ret;
 }
 
+void CameraHidlTest::getMultiResolutionStreamConfigurations(
+        camera_metadata_ro_entry* multiResStreamConfigs, camera_metadata_ro_entry* streamConfigs,
+        camera_metadata_ro_entry* maxResolutionStreamConfigs,
+        const camera_metadata_t* staticMetadata) {
+    ASSERT_NE(multiResStreamConfigs, nullptr);
+    ASSERT_NE(streamConfigs, nullptr);
+    ASSERT_NE(maxResolutionStreamConfigs, nullptr);
+    ASSERT_NE(staticMetadata, nullptr);
+
+    int retcode = find_camera_metadata_ro_entry(
+            staticMetadata, ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS, streamConfigs);
+    ASSERT_TRUE(0 == retcode);
+    retcode = find_camera_metadata_ro_entry(
+            staticMetadata, ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_MAXIMUM_RESOLUTION,
+            maxResolutionStreamConfigs);
+    ASSERT_TRUE(-ENOENT == retcode || 0 == retcode);
+    retcode = find_camera_metadata_ro_entry(
+            staticMetadata, ANDROID_SCALER_PHYSICAL_CAMERA_MULTI_RESOLUTION_STREAM_CONFIGURATIONS,
+            multiResStreamConfigs);
+    ASSERT_TRUE(-ENOENT == retcode || 0 == retcode);
+}
+
 // Select an appropriate dataspace given a specific pixel format.
 V3_2::DataspaceFlags CameraHidlTest::getDataspace(PixelFormat format) {
     switch (format) {
@@ -6245,15 +6411,18 @@
 void CameraHidlTest::createStreamConfiguration(
         const ::android::hardware::hidl_vec<V3_2::Stream>& streams3_2,
         StreamConfigurationMode configMode,
-        ::android::hardware::camera::device::V3_2::StreamConfiguration *config3_2 /*out*/,
-        ::android::hardware::camera::device::V3_4::StreamConfiguration *config3_4 /*out*/,
-        ::android::hardware::camera::device::V3_5::StreamConfiguration *config3_5 /*out*/,
+        ::android::hardware::camera::device::V3_2::StreamConfiguration* config3_2 /*out*/,
+        ::android::hardware::camera::device::V3_4::StreamConfiguration* config3_4 /*out*/,
+        ::android::hardware::camera::device::V3_5::StreamConfiguration* config3_5 /*out*/,
+        ::android::hardware::camera::device::V3_7::StreamConfiguration* config3_7 /*out*/,
         uint32_t jpegBufferSize) {
     ASSERT_NE(nullptr, config3_2);
     ASSERT_NE(nullptr, config3_4);
     ASSERT_NE(nullptr, config3_5);
+    ASSERT_NE(nullptr, config3_7);
 
     ::android::hardware::hidl_vec<V3_4::Stream> streams3_4(streams3_2.size());
+    ::android::hardware::hidl_vec<V3_7::Stream> streams3_7(streams3_2.size());
     size_t idx = 0;
     for (auto& stream3_2 : streams3_2) {
         V3_4::Stream stream;
@@ -6263,9 +6432,12 @@
                 stream3_2.dataSpace == static_cast<V3_2::DataspaceFlags>(Dataspace::V0_JFIF)) {
             stream.bufferSize = jpegBufferSize;
         }
-        streams3_4[idx++] = stream;
+        streams3_4[idx] = stream;
+        streams3_7[idx] = {stream, /*groupId*/ -1, {ANDROID_SENSOR_PIXEL_MODE_DEFAULT}};
+        idx++;
     }
     // Caller is responsible to fill in non-zero config3_5->streamConfigCounter after this returns
+    *config3_7 = {streams3_7, configMode, {}, 0, false};
     *config3_5 = {{streams3_4, configMode, {}}, 0};
     *config3_4 = config3_5->v3_4;
     *config3_2 = {streams3_2, configMode};
@@ -6379,8 +6551,9 @@
     ASSERT_TRUE(ret.isOk());
 
     ASSERT_TRUE(deviceVersion >= CAMERA_DEVICE_API_VERSION_3_7);
+    sp<device::V3_5::ICameraDevice> cameraDevice3_5 = nullptr;
     sp<device::V3_7::ICameraDevice> cameraDevice3_7 = nullptr;
-    castDevice3_7(device3_x, deviceVersion, &cameraDevice3_7);
+    castDevice(device3_x, deviceVersion, &cameraDevice3_5, &cameraDevice3_7);
     ASSERT_NE(cameraDevice3_7, nullptr);
     bool supported = false;
     ret = cameraDevice3_7->isStreamCombinationSupported_3_7(
@@ -6532,7 +6705,8 @@
     ASSERT_TRUE(!allowUnsupport || deviceVersion >= CAMERA_DEVICE_API_VERSION_3_5);
     if (allowUnsupport) {
         sp<device::V3_5::ICameraDevice> cameraDevice3_5;
-        castDevice(device3_x, deviceVersion, &cameraDevice3_5);
+        sp<device::V3_7::ICameraDevice> cameraDevice3_7;
+        castDevice(device3_x, deviceVersion, &cameraDevice3_5, &cameraDevice3_7);
 
         bool supported = false;
         ret = cameraDevice3_5->isStreamCombinationSupported(config3_4,
@@ -6891,9 +7065,32 @@
     ::android::hardware::camera::device::V3_2::StreamConfiguration config3_2;
     ::android::hardware::camera::device::V3_4::StreamConfiguration config3_4;
     ::android::hardware::camera::device::V3_5::StreamConfiguration config3_5;
-    createStreamConfiguration(streams3_2, StreamConfigurationMode::NORMAL_MODE,
-                              &config3_2, &config3_4, &config3_5, jpegBufferSize);
-    if (session3_5 != nullptr) {
+    ::android::hardware::camera::device::V3_7::StreamConfiguration config3_7;
+    createStreamConfiguration(streams3_2, StreamConfigurationMode::NORMAL_MODE, &config3_2,
+                              &config3_4, &config3_5, &config3_7, jpegBufferSize);
+    if (session3_7 != nullptr) {
+        ret = session3_7->constructDefaultRequestSettings(
+                reqTemplate, [&config3_7](auto status, const auto& req) {
+                    ASSERT_EQ(Status::OK, status);
+                    config3_7.sessionParams = req;
+                });
+        ASSERT_TRUE(ret.isOk());
+        config3_7.streamConfigCounter = streamConfigCounter;
+        ret = session3_7->configureStreams_3_7(
+                config3_7, [&](Status s, device::V3_6::HalStreamConfiguration halConfig) {
+                    ASSERT_EQ(Status::OK, s);
+                    ASSERT_EQ(1u, halConfig.streams.size());
+                    halStreamConfig->streams.resize(1);
+                    halStreamConfig->streams[0] = halConfig.streams[0].v3_4.v3_3.v3_2;
+                    if (*useHalBufManager) {
+                        hidl_vec<V3_4::Stream> streams(1);
+                        hidl_vec<V3_2::HalStream> halStreams(1);
+                        streams[0] = config3_4.streams[0];
+                        halStreams[0] = halConfig.streams[0].v3_4.v3_3.v3_2;
+                        cb->setCurrentStreamConfig(streams, halStreams);
+                    }
+                });
+    } else if (session3_5 != nullptr) {
         ret = session3_5->constructDefaultRequestSettings(reqTemplate,
                                                        [&config3_5](auto status, const auto& req) {
                                                            ASSERT_EQ(Status::OK, status);
@@ -6953,31 +7150,30 @@
     ASSERT_TRUE(ret.isOk());
 }
 
-void CameraHidlTest::castDevice3_7(const sp<device::V3_2::ICameraDevice>& device,
-                                   int32_t deviceVersion,
-                                   sp<device::V3_7::ICameraDevice>* device3_7 /*out*/) {
-    ASSERT_NE(nullptr, device3_7);
-    if (deviceVersion >= CAMERA_DEVICE_API_VERSION_3_7) {
-        auto castResult = device::V3_7::ICameraDevice::castFrom(device);
-        ASSERT_TRUE(castResult.isOk());
-        *device3_7 = castResult;
-    }
-}
-
-void CameraHidlTest::castDevice(const sp<device::V3_2::ICameraDevice> &device,
-        int32_t deviceVersion, sp<device::V3_5::ICameraDevice> *device3_5/*out*/) {
+void CameraHidlTest::castDevice(const sp<device::V3_2::ICameraDevice>& device,
+                                int32_t deviceVersion,
+                                sp<device::V3_5::ICameraDevice>* device3_5 /*out*/,
+                                sp<device::V3_7::ICameraDevice>* device3_7 /*out*/) {
     ASSERT_NE(nullptr, device3_5);
     if (deviceVersion == CAMERA_DEVICE_API_VERSION_3_5) {
         auto castResult = device::V3_5::ICameraDevice::castFrom(device);
         ASSERT_TRUE(castResult.isOk());
         *device3_5 = castResult;
     }
+
+    ASSERT_NE(nullptr, device3_7);
+    if (deviceVersion == CAMERA_DEVICE_API_VERSION_3_7) {
+        auto castResult = device::V3_7::ICameraDevice::castFrom(device);
+        ASSERT_TRUE(castResult.isOk());
+        *device3_7 = castResult;
+    }
 }
 
 //Cast camera provider to corresponding version if available
 void CameraHidlTest::castProvider(const sp<ICameraProvider>& provider,
                                   sp<provider::V2_5::ICameraProvider>* provider2_5 /*out*/,
-                                  sp<provider::V2_6::ICameraProvider>* provider2_6 /*out*/) {
+                                  sp<provider::V2_6::ICameraProvider>* provider2_6 /*out*/,
+                                  sp<provider::V2_7::ICameraProvider>* provider2_7 /*out*/) {
     ASSERT_NE(nullptr, provider2_5);
     auto castResult2_5 = provider::V2_5::ICameraProvider::castFrom(provider);
     if (castResult2_5.isOk()) {
@@ -6989,6 +7185,12 @@
     if (castResult2_6.isOk()) {
         *provider2_6 = castResult2_6;
     }
+
+    ASSERT_NE(nullptr, provider2_7);
+    auto castResult2_7 = provider::V2_7::ICameraProvider::castFrom(provider);
+    if (castResult2_7.isOk()) {
+        *provider2_7 = castResult2_7;
+    }
 }
 
 //Cast camera device session to corresponding version
@@ -7041,9 +7243,24 @@
     }
 }
 
-void CameraHidlTest::verifyStreamCombination(sp<device::V3_5::ICameraDevice> cameraDevice3_5,
-        const ::android::hardware::camera::device::V3_4::StreamConfiguration &config3_4,
+void CameraHidlTest::verifyStreamCombination(
+        sp<device::V3_7::ICameraDevice> cameraDevice3_7,
+        const ::android::hardware::camera::device::V3_7::StreamConfiguration& config3_7,
+        sp<device::V3_5::ICameraDevice> cameraDevice3_5,
+        const ::android::hardware::camera::device::V3_4::StreamConfiguration& config3_4,
         bool expectedStatus, bool expectMethodSupported) {
+    if (cameraDevice3_7.get() != nullptr) {
+        auto ret = cameraDevice3_7->isStreamCombinationSupported_3_7(
+                config3_7, [expectedStatus, expectMethodSupported](Status s, bool combStatus) {
+                    ASSERT_TRUE((Status::OK == s) ||
+                                (!expectMethodSupported && Status::METHOD_NOT_SUPPORTED == s));
+                    if (Status::OK == s) {
+                        ASSERT_TRUE(combStatus == expectedStatus);
+                    }
+                });
+        ASSERT_TRUE(ret.isOk());
+    }
+
     if (cameraDevice3_5.get() != nullptr) {
         auto ret = cameraDevice3_5->isStreamCombinationSupported(config3_4,
                 [expectedStatus, expectMethodSupported] (Status s, bool combStatus) {
@@ -7057,11 +7274,11 @@
     }
 }
 
-// Verify logical camera static metadata
-void CameraHidlTest::verifyLogicalCameraMetadata(const std::string& cameraName,
+// Verify logical or ultra high resolution camera static metadata
+void CameraHidlTest::verifyLogicalOrUltraHighResCameraMetadata(
+        const std::string& cameraName,
         const ::android::sp<::android::hardware::camera::device::V3_2::ICameraDevice>& device,
-        const CameraMetadata &chars, int deviceVersion,
-        const hidl_vec<hidl_string>& deviceNames) {
+        const CameraMetadata& chars, int deviceVersion, const hidl_vec<hidl_string>& deviceNames) {
     const camera_metadata_t* metadata = (camera_metadata_t*)chars.data();
     ASSERT_NE(nullptr, metadata);
     SystemCameraKind systemCameraKind = SystemCameraKind::PUBLIC;
@@ -7069,7 +7286,9 @@
     ASSERT_EQ(rc, Status::OK);
     rc = isLogicalMultiCamera(metadata);
     ASSERT_TRUE(Status::OK == rc || Status::METHOD_NOT_SUPPORTED == rc);
-    if (Status::METHOD_NOT_SUPPORTED == rc) {
+    bool isMultiCamera = (Status::OK == rc);
+    bool isUltraHighResCamera = isUltraHighResolution(metadata);
+    if (!isMultiCamera && !isUltraHighResCamera) {
         return;
     }
 
@@ -7077,13 +7296,36 @@
     int retcode = find_camera_metadata_ro_entry(metadata,
             ANDROID_CONTROL_ZOOM_RATIO_RANGE, &entry);
     bool hasZoomRatioRange = (0 == retcode && entry.count == 2);
+    retcode = find_camera_metadata_ro_entry(
+            metadata, ANDROID_INFO_SUPPORTED_BUFFER_MANAGEMENT_VERSION, &entry);
+    bool hasHalBufferManager =
+            (0 == retcode && 1 == entry.count &&
+             entry.data.i32[0] == ANDROID_INFO_SUPPORTED_BUFFER_MANAGEMENT_VERSION_HIDL_DEVICE_3_5);
+    retcode = find_camera_metadata_ro_entry(
+            metadata, ANDROID_SCALER_MULTI_RESOLUTION_STREAM_SUPPORTED, &entry);
+    bool multiResolutionStreamSupported =
+            (0 == retcode && 1 == entry.count &&
+             entry.data.u8[0] == ANDROID_SCALER_MULTI_RESOLUTION_STREAM_SUPPORTED_TRUE);
+    if (multiResolutionStreamSupported) {
+        ASSERT_TRUE(hasHalBufferManager);
+    }
 
     std::string version, cameraId;
     ASSERT_TRUE(::matchDeviceName(cameraName, mProviderType, &version, &cameraId));
     std::unordered_set<std::string> physicalIds;
-    ASSERT_TRUE(Status::OK == getPhysicalCameraIds(metadata, &physicalIds));
+    rc = getPhysicalCameraIds(metadata, &physicalIds);
+    ASSERT_TRUE(isUltraHighResCamera || Status::OK == rc);
     for (auto physicalId : physicalIds) {
         ASSERT_NE(physicalId, cameraId);
+    }
+    if (physicalIds.size() == 0) {
+        ASSERT_TRUE(isUltraHighResCamera && !isMultiCamera);
+        physicalIds.insert(cameraId);
+    }
+    // Map from image format to number of multi-resolution sizes for that format
+    std::unordered_map<int32_t, size_t> multiResOutputFormatCounterMap;
+    std::unordered_map<int32_t, size_t> multiResInputFormatCounterMap;
+    for (auto physicalId : physicalIds) {
         bool isPublicId = false;
         std::string fullPublicId;
         SystemCameraKind physSystemCameraKind = SystemCameraKind::PUBLIC;
@@ -7096,6 +7338,11 @@
                 break;
             }
         }
+
+        camera_metadata_ro_entry physicalMultiResStreamConfigs;
+        camera_metadata_ro_entry physicalStreamConfigs;
+        camera_metadata_ro_entry physicalMaxResolutionStreamConfigs;
+        bool isUltraHighRes = false;
         if (isPublicId) {
             ::android::sp<::android::hardware::camera::device::V3_2::ICameraDevice> subDevice;
             Return<void> ret;
@@ -7107,62 +7354,195 @@
                 });
             ASSERT_TRUE(ret.isOk());
 
-            ret = subDevice->getCameraCharacteristics(
-                    [&](auto status, const auto& chars) {
+            ret = subDevice->getCameraCharacteristics([&](auto status, const auto& chars) {
                 ASSERT_EQ(Status::OK, status);
-                const camera_metadata_t* staticMeta =
+                const camera_metadata_t* staticMetadata =
                         reinterpret_cast<const camera_metadata_t*>(chars.data());
-                rc = getSystemCameraKind(staticMeta, &physSystemCameraKind);
+                rc = getSystemCameraKind(staticMetadata, &physSystemCameraKind);
                 ASSERT_EQ(rc, Status::OK);
                 // Make sure that the system camera kind of a non-hidden
                 // physical cameras is the same as the logical camera associated
                 // with it.
                 ASSERT_EQ(physSystemCameraKind, systemCameraKind);
-                retcode = find_camera_metadata_ro_entry(staticMeta,
+                retcode = find_camera_metadata_ro_entry(staticMetadata,
                                                         ANDROID_CONTROL_ZOOM_RATIO_RANGE, &entry);
                 bool subCameraHasZoomRatioRange = (0 == retcode && entry.count == 2);
                 ASSERT_EQ(hasZoomRatioRange, subCameraHasZoomRatioRange);
+
+                getMultiResolutionStreamConfigurations(
+                        &physicalMultiResStreamConfigs, &physicalStreamConfigs,
+                        &physicalMaxResolutionStreamConfigs, staticMetadata);
+                isUltraHighRes = isUltraHighResolution(staticMetadata);
             });
             ASSERT_TRUE(ret.isOk());
-            continue;
+        } else {
+            ASSERT_TRUE(deviceVersion >= CAMERA_DEVICE_API_VERSION_3_5);
+            auto castResult = device::V3_5::ICameraDevice::castFrom(device);
+            ASSERT_TRUE(castResult.isOk());
+            ::android::sp<::android::hardware::camera::device::V3_5::ICameraDevice> device3_5 =
+                    castResult;
+            ASSERT_NE(device3_5, nullptr);
+
+            // Check camera characteristics for hidden camera id
+            Return<void> ret = device3_5->getPhysicalCameraCharacteristics(
+                    physicalId, [&](auto status, const auto& chars) {
+                        verifyCameraCharacteristics(status, chars);
+                        verifyMonochromeCharacteristics(chars, deviceVersion);
+
+                        auto staticMetadata = (const camera_metadata_t*)chars.data();
+                        retcode = find_camera_metadata_ro_entry(
+                                staticMetadata, ANDROID_CONTROL_ZOOM_RATIO_RANGE, &entry);
+                        bool subCameraHasZoomRatioRange = (0 == retcode && entry.count == 2);
+                        ASSERT_EQ(hasZoomRatioRange, subCameraHasZoomRatioRange);
+
+                        getMultiResolutionStreamConfigurations(
+                                &physicalMultiResStreamConfigs, &physicalStreamConfigs,
+                                &physicalMaxResolutionStreamConfigs, staticMetadata);
+                        isUltraHighRes = isUltraHighResolution(staticMetadata);
+                    });
+            ASSERT_TRUE(ret.isOk());
+
+            // Check calling getCameraDeviceInterface_V3_x() on hidden camera id returns
+            // ILLEGAL_ARGUMENT.
+            std::stringstream s;
+            s << "device@" << version << "/" << mProviderType << "/" << physicalId;
+            hidl_string fullPhysicalId(s.str());
+            ret = mProvider->getCameraDeviceInterface_V3_x(
+                    fullPhysicalId, [&](auto status, const auto& device3_x) {
+                        ASSERT_EQ(Status::ILLEGAL_ARGUMENT, status);
+                        ASSERT_EQ(device3_x, nullptr);
+                    });
+            ASSERT_TRUE(ret.isOk());
         }
 
-        ASSERT_TRUE(deviceVersion >= CAMERA_DEVICE_API_VERSION_3_5);
-        auto castResult = device::V3_5::ICameraDevice::castFrom(device);
-        ASSERT_TRUE(castResult.isOk());
-        ::android::sp<::android::hardware::camera::device::V3_5::ICameraDevice> device3_5 =
-                castResult;
-        ASSERT_NE(device3_5, nullptr);
+        if (physicalMultiResStreamConfigs.count > 0) {
+            ASSERT_GE(deviceVersion, CAMERA_DEVICE_API_VERSION_3_7);
+            ASSERT_EQ(physicalMultiResStreamConfigs.count % 4, 0);
 
-        // Check camera characteristics for hidden camera id
-        Return<void> ret = device3_5->getPhysicalCameraCharacteristics(
-                physicalId, [&](auto status, const auto& chars) {
-                    verifyCameraCharacteristics(status, chars);
-                    verifyMonochromeCharacteristics(chars, deviceVersion);
-                    retcode =
-                            find_camera_metadata_ro_entry((const camera_metadata_t*)chars.data(),
-                                                          ANDROID_CONTROL_ZOOM_RATIO_RANGE, &entry);
-                    bool subCameraHasZoomRatioRange = (0 == retcode && entry.count == 2);
-                    ASSERT_EQ(hasZoomRatioRange, subCameraHasZoomRatioRange);
-                });
-        ASSERT_TRUE(ret.isOk());
+            // Each supported size must be max size for that format,
+            for (size_t i = 0; i < physicalMultiResStreamConfigs.count / 4; i++) {
+                int32_t multiResFormat = physicalMultiResStreamConfigs.data.i32[i * 4];
+                int32_t multiResWidth = physicalMultiResStreamConfigs.data.i32[i * 4 + 1];
+                int32_t multiResHeight = physicalMultiResStreamConfigs.data.i32[i * 4 + 2];
+                int32_t multiResInput = physicalMultiResStreamConfigs.data.i32[i * 4 + 3];
 
-        // Check calling getCameraDeviceInterface_V3_x() on hidden camera id returns
-        // ILLEGAL_ARGUMENT.
-        std::stringstream s;
-        s << "device@" << version << "/" << mProviderType << "/" << physicalId;
-        hidl_string fullPhysicalId(s.str());
-        ret = mProvider->getCameraDeviceInterface_V3_x(fullPhysicalId,
-                [&](auto status, const auto& device3_x) {
-            ASSERT_EQ(Status::ILLEGAL_ARGUMENT, status);
-            ASSERT_EQ(device3_x, nullptr);
-        });
-        ASSERT_TRUE(ret.isOk());
+                // Check if the resolution is the max resolution in stream
+                // configuration map
+                bool supported = false;
+                bool isMaxSize = true;
+                for (size_t j = 0; j < physicalStreamConfigs.count / 4; j++) {
+                    int32_t format = physicalStreamConfigs.data.i32[j * 4];
+                    int32_t width = physicalStreamConfigs.data.i32[j * 4 + 1];
+                    int32_t height = physicalStreamConfigs.data.i32[j * 4 + 2];
+                    int32_t input = physicalStreamConfigs.data.i32[j * 4 + 3];
+                    if (format == multiResFormat && input == multiResInput) {
+                        if (width == multiResWidth && height == multiResHeight) {
+                            supported = true;
+                        } else if (width * height > multiResWidth * multiResHeight) {
+                            isMaxSize = false;
+                        }
+                    }
+                }
+                // Check if the resolution is the max resolution in max
+                // resolution stream configuration map
+                bool supportedUltraHighRes = false;
+                bool isUltraHighResMaxSize = true;
+                for (size_t j = 0; j < physicalMaxResolutionStreamConfigs.count / 4; j++) {
+                    int32_t format = physicalMaxResolutionStreamConfigs.data.i32[j * 4];
+                    int32_t width = physicalMaxResolutionStreamConfigs.data.i32[j * 4 + 1];
+                    int32_t height = physicalMaxResolutionStreamConfigs.data.i32[j * 4 + 2];
+                    int32_t input = physicalMaxResolutionStreamConfigs.data.i32[j * 4 + 3];
+                    if (format == multiResFormat && input == multiResInput) {
+                        if (width == multiResWidth && height == multiResHeight) {
+                            supportedUltraHighRes = true;
+                        } else if (width * height > multiResWidth * multiResHeight) {
+                            isUltraHighResMaxSize = false;
+                        }
+                    }
+                }
+
+                if (isUltraHighRes) {
+                    // For ultra high resolution camera, the configuration must
+                    // be the maximum size in stream configuration map, or max
+                    // resolution stream configuration map
+                    ASSERT_TRUE((supported && isMaxSize) ||
+                                (supportedUltraHighRes && isUltraHighResMaxSize));
+                } else {
+                    // The configuration must be the maximum size in stream
+                    // configuration map
+                    ASSERT_TRUE(supported && isMaxSize);
+                    ASSERT_FALSE(supportedUltraHighRes);
+                }
+
+                // Increment the counter for the configuration's format.
+                auto& formatCounterMap = multiResInput ? multiResInputFormatCounterMap
+                                                       : multiResOutputFormatCounterMap;
+                if (formatCounterMap.count(multiResFormat) == 0) {
+                    formatCounterMap[multiResFormat] = 1;
+                } else {
+                    formatCounterMap[multiResFormat]++;
+                }
+            }
+
+            // There must be no duplicates
+            for (size_t i = 0; i < physicalMultiResStreamConfigs.count / 4 - 1; i++) {
+                for (size_t j = i + 1; j < physicalMultiResStreamConfigs.count / 4; j++) {
+                    // Input/output doesn't match
+                    if (physicalMultiResStreamConfigs.data.i32[i * 4 + 3] !=
+                        physicalMultiResStreamConfigs.data.i32[j * 4 + 3]) {
+                        continue;
+                    }
+                    // Format doesn't match
+                    if (physicalMultiResStreamConfigs.data.i32[i * 4] !=
+                        physicalMultiResStreamConfigs.data.i32[j * 4]) {
+                        continue;
+                    }
+                    // Width doesn't match
+                    if (physicalMultiResStreamConfigs.data.i32[i * 4 + 1] !=
+                        physicalMultiResStreamConfigs.data.i32[j * 4 + 1]) {
+                        continue;
+                    }
+                    // Height doesn't match
+                    if (physicalMultiResStreamConfigs.data.i32[i * 4 + 2] !=
+                        physicalMultiResStreamConfigs.data.i32[j * 4 + 2]) {
+                        continue;
+                    }
+                    // input/output, format, width, and height all match
+                    ADD_FAILURE();
+                }
+            }
+        }
+    }
+
+    // If a multi-resolution stream is supported, there must be at least one
+    // format with more than one resolutions
+    if (multiResolutionStreamSupported) {
+        size_t numMultiResFormats = 0;
+        for (const auto& [format, sizeCount] : multiResOutputFormatCounterMap) {
+            if (sizeCount >= 2) {
+                numMultiResFormats++;
+            }
+        }
+        for (const auto& [format, sizeCount] : multiResInputFormatCounterMap) {
+            if (sizeCount >= 2) {
+                numMultiResFormats++;
+
+                // If multi-resolution reprocessing is supported, the logical
+                // camera or ultra-high resolution sensor camera must support
+                // the corresponding reprocessing capability.
+                if (format == static_cast<uint32_t>(PixelFormat::IMPLEMENTATION_DEFINED)) {
+                    ASSERT_EQ(isZSLModeAvailable(metadata, PRIV_REPROCESS), Status::OK);
+                } else if (format == static_cast<int32_t>(PixelFormat::YCBCR_420_888)) {
+                    ASSERT_EQ(isZSLModeAvailable(metadata, YUV_REPROCESS), Status::OK);
+                }
+            }
+        }
+        ASSERT_GT(numMultiResFormats, 0);
     }
 
     // Make sure ANDROID_LOGICAL_MULTI_CAMERA_ACTIVE_PHYSICAL_ID is available in
     // result keys.
-    if (deviceVersion >= CAMERA_DEVICE_API_VERSION_3_5) {
+    if (isMultiCamera && deviceVersion >= CAMERA_DEVICE_API_VERSION_3_5) {
         retcode = find_camera_metadata_ro_entry(metadata,
                 ANDROID_REQUEST_AVAILABLE_RESULT_KEYS, &entry);
         if ((0 == retcode) && (entry.count > 0)) {