OSDN Git Service

Camera: Extend Camera VTS testing
authorEmilian Peev <epeev@google.com>
Wed, 8 Feb 2017 17:15:16 +0000 (17:15 +0000)
committerEmilian Peev <epeev@google.com>
Wed, 22 Feb 2017 09:50:09 +0000 (09:50 +0000)
Further tests concerning camera configurations. The
additional tests corver:
- The available stream configurations.
- Invalid stream parameters.
- Constrained mode if available.
- ZSL mode if available.
- Stream combinations including preview and still capture.
- Stream combinations including video and snapshot.

BUG: 32022758
Test: compile and run the gtest binary on device
Change-Id: I5111ac96b4aaa7ad9f163f990f6b0d0c229993f9

camera/provider/2.4/vts/functional/VtsHalCameraProviderV2_4TargetTest.cpp

index 0186371..3423b26 100644 (file)
@@ -21,6 +21,8 @@
 #include <gtest/gtest.h>
 #include <regex>
 #include "system/camera_metadata.h"
+#include <hardware/gralloc.h>
+#include <unordered_map>
 
 using ::android::hardware::Return;
 using ::android::hardware::Void;
@@ -28,6 +30,7 @@ using ::android::hardware::hidl_handle;
 using ::android::hardware::hidl_string;
 using ::android::hardware::hidl_vec;
 using ::android::sp;
+using ::android::hardware::graphics::common::V1_0::PixelFormat;
 using ::android::hardware::camera::common::V1_0::Status;
 using ::android::hardware::camera::common::V1_0::CameraDeviceStatus;
 using ::android::hardware::camera::common::V1_0::TorchMode;
@@ -40,8 +43,30 @@ using ::android::hardware::camera::device::V3_2::ICameraDeviceCallback;
 using ::android::hardware::camera::device::V3_2::ICameraDeviceSession;
 using ::android::hardware::camera::device::V3_2::NotifyMsg;
 using ::android::hardware::camera::device::V3_2::RequestTemplate;
+using ::android::hardware::camera::device::V3_2::Stream;
+using ::android::hardware::camera::device::V3_2::StreamType;
+using ::android::hardware::camera::device::V3_2::StreamRotation;
+using ::android::hardware::camera::device::V3_2::StreamConfiguration;
+using ::android::hardware::camera::device::V3_2::StreamConfigurationMode;
+using ::android::hardware::camera::device::V3_2::CameraMetadata;
+using ::android::hardware::camera::device::V3_2::HalStreamConfiguration;
 
 #define CAMERA_PASSTHROUGH_SERVICE_NAME "legacy/0"
+#define MAX_PREVIEW_WIDTH  1920
+#define MAX_PREVIEW_HEIGHT 1080
+#define MAX_VIDEO_WIDTH    4096
+#define MAX_VIDEO_HEIGHT   2160
+
+struct AvailableStream {
+    int32_t width;
+    int32_t height;
+    int32_t format;
+};
+
+struct AvailableZSLInputOutput {
+    int32_t inputFormat;
+    int32_t outputFormat;
+};
 
 namespace {
     // "device@<version>/legacy/<id>"
@@ -126,6 +151,19 @@ public:
             return Void();
         }
     };
+
+    static Status getAvailableOutputStreams(camera_metadata_t *staticMeta,
+            std::vector<AvailableStream> &outputStreams,
+            AvailableStream *threshold = nullptr);
+    static Status isConstrainedModeAvailable(camera_metadata_t *staticMeta);
+    static Status pickConstrainedModeSize(camera_metadata_t *staticMeta,
+            AvailableStream &hfrStream);
+    static Status isZSLModeAvailable(camera_metadata_t *staticMeta);
+    static Status getZSLInputOutputMap(camera_metadata_t *staticMeta,
+            std::vector<AvailableZSLInputOutput> &inputOutputMap);
+    static Status findLargestSize(
+            const std::vector<AvailableStream> &streamSizes,
+            int32_t format, AvailableStream &result);
 };
 
 hidl_vec<hidl_string> CameraHidlTest::getCameraDeviceNames() {
@@ -234,6 +272,8 @@ TEST_F(CameraHidlTest, getCameraDeviceInterface_V3_x) {
     }
 }
 
+// Verify that the device resource cost can be retrieved and the values are
+// sane.
 TEST_F(CameraHidlTest, getResourceCost) {
     CameraHidlEnvironment* env = CameraHidlEnvironment::Instance();
     hidl_vec<hidl_string> cameraDeviceNames = getCameraDeviceNames();
@@ -265,6 +305,8 @@ TEST_F(CameraHidlTest, getResourceCost) {
     }
 }
 
+// Verify that the static camera characteristics can be retrieved
+// successfully.
 TEST_F(CameraHidlTest, getCameraCharacteristics) {
     CameraHidlEnvironment* env = CameraHidlEnvironment::Instance();
     hidl_vec<hidl_string> cameraDeviceNames = getCameraDeviceNames();
@@ -299,6 +341,7 @@ TEST_F(CameraHidlTest, getCameraCharacteristics) {
     }
 }
 
+//In case it is supported verify that torch can be enabled.
 TEST_F(CameraHidlTest, setTorchMode) {
     CameraHidlEnvironment* env = CameraHidlEnvironment::Instance();
     hidl_vec<hidl_string> cameraDeviceNames = getCameraDeviceNames();
@@ -340,6 +383,7 @@ TEST_F(CameraHidlTest, setTorchMode) {
     }
 }
 
+// Check dump functionality.
 TEST_F(CameraHidlTest, dumpState) {
     CameraHidlEnvironment* env = CameraHidlEnvironment::Instance();
     hidl_vec<hidl_string> cameraDeviceNames = getCameraDeviceNames();
@@ -408,6 +452,8 @@ TEST_F(CameraHidlTest, openClose) {
     }
 }
 
+// Check whether all common default request settings can be sucessfully
+// constructed.
 TEST_F(CameraHidlTest, constructDefaultRequestSettings) {
     CameraHidlEnvironment* env = CameraHidlEnvironment::Instance();
     hidl_vec<hidl_string> cameraDeviceNames = getCameraDeviceNames();
@@ -472,9 +518,481 @@ TEST_F(CameraHidlTest, constructDefaultRequestSettings) {
     }
 }
 
-TEST_F(CameraHidlTest, configureStreams) {
+// Verify that all supported stream formats and sizes can be configured
+// successfully.
+TEST_F(CameraHidlTest, configureStreamsAvailableOutputs) {
+    CameraHidlEnvironment* env = CameraHidlEnvironment::Instance();
+    hidl_vec<hidl_string> cameraDeviceNames = getCameraDeviceNames();
+    std::vector<AvailableStream> outputStreams;
+
+    for (const auto& name : cameraDeviceNames) {
+        if (getCameraDeviceVersion(name) == CAMERA_DEVICE_API_VERSION_3_2) {
+            ::android::sp<::android::hardware::camera::device::V3_2::ICameraDevice> device3_2;
+            ALOGI("configureStreams: Testing camera device %s", name.c_str());
+            env->mProvider->getCameraDeviceInterface_V3_x(
+                name,
+                [&](auto status, const auto& device) {
+                    ALOGI("getCameraDeviceInterface_V3_x returns status:%d", (int)status);
+                    ASSERT_EQ(Status::OK, status);
+                    ASSERT_NE(device, nullptr);
+                    device3_2 = device;
+                });
+
+            sp<EmptyDeviceCb> cb = new EmptyDeviceCb;
+            sp<ICameraDeviceSession> session;
+            device3_2->open(
+                cb,
+                [&](auto status, const auto& newSession) {
+                    ALOGI("device::open returns status:%d", (int)status);
+                    ASSERT_EQ(Status::OK, status);
+                    ASSERT_NE(newSession, nullptr);
+                    session = newSession;
+                });
+
+            camera_metadata_t *staticMeta;
+            device3_2->getCameraCharacteristics([&] (Status s,
+                    CameraMetadata metadata) {
+                ASSERT_EQ(Status::OK, s);
+                staticMeta =
+                        reinterpret_cast<camera_metadata_t*>(metadata.data());
+            });
+            outputStreams.clear();
+            ASSERT_EQ(Status::OK, getAvailableOutputStreams(staticMeta,
+                    outputStreams));
+            ASSERT_NE(0u, outputStreams.size());
+
+            int32_t streamId = 0;
+            for (auto &it : outputStreams) {
+                Stream stream = {streamId, StreamType::OUTPUT,
+                        static_cast<uint32_t> (it.width),
+                        static_cast<uint32_t> (it.height),
+                        static_cast<PixelFormat> (it.format), 0, 0,
+                        StreamRotation::ROTATION_0};
+                ::android::hardware::hidl_vec<Stream> streams = {stream};
+                StreamConfiguration config = {streams,
+                        StreamConfigurationMode::NORMAL_MODE};
+                session->configureStreams(config, [streamId] (Status s,
+                        HalStreamConfiguration halConfig) {
+                    ASSERT_EQ(Status::OK, s);
+                    ASSERT_EQ(1u, halConfig.streams.size());
+                    ASSERT_EQ(halConfig.streams[0].id, streamId);
+                });
+                streamId++;
+            }
+
+            session->close();
+        }
+    }
+}
+
+// Check for correct handling of invalid/incorrect configuration parameters.
+TEST_F(CameraHidlTest, configureStreamsInvalidOutputs) {
+    CameraHidlEnvironment* env = CameraHidlEnvironment::Instance();
+    hidl_vec<hidl_string> cameraDeviceNames = getCameraDeviceNames();
+    std::vector<AvailableStream> outputStreams;
+
+    for (const auto& name : cameraDeviceNames) {
+        if (getCameraDeviceVersion(name) == CAMERA_DEVICE_API_VERSION_3_2) {
+            ::android::sp<::android::hardware::camera::device::V3_2::ICameraDevice> device3_2;
+            ALOGI("configureStreams: Testing camera device %s", name.c_str());
+            env->mProvider->getCameraDeviceInterface_V3_x(
+                name,
+                [&](auto status, const auto& device) {
+                    ALOGI("getCameraDeviceInterface_V3_x returns status:%d", (int)status);
+                    ASSERT_EQ(Status::OK, status);
+                    ASSERT_NE(device, nullptr);
+                    device3_2 = device;
+                });
+
+            sp<EmptyDeviceCb> cb = new EmptyDeviceCb;
+            sp<ICameraDeviceSession> session;
+            device3_2->open(
+                cb,
+                [&](auto status, const auto& newSession) {
+                    ALOGI("device::open returns status:%d", (int)status);
+                    ASSERT_EQ(Status::OK, status);
+                    ASSERT_NE(newSession, nullptr);
+                    session = newSession;
+                });
+
+            camera_metadata_t *staticMeta;
+            device3_2->getCameraCharacteristics([&] (Status s,
+                    CameraMetadata metadata) {
+                ASSERT_EQ(Status::OK, s);
+                staticMeta =
+                        reinterpret_cast<camera_metadata_t*>(metadata.data());
+            });
+            outputStreams.clear();
+            ASSERT_EQ(Status::OK, getAvailableOutputStreams(staticMeta,
+                    outputStreams));
+            ASSERT_NE(0u, outputStreams.size());
+
+            int32_t streamId = 0;
+            Stream stream = {streamId++, StreamType::OUTPUT,
+                    static_cast<uint32_t> (0),
+                    static_cast<uint32_t> (0),
+                    static_cast<PixelFormat> (outputStreams[0].format),
+                    0, 0, StreamRotation::ROTATION_0};
+            ::android::hardware::hidl_vec<Stream> streams = {stream};
+            StreamConfiguration config = {streams,
+                    StreamConfigurationMode::NORMAL_MODE};
+            session->configureStreams(config, [] (Status s,
+                    HalStreamConfiguration) {
+                ASSERT_EQ(Status::ILLEGAL_ARGUMENT, s);
+            });
+
+            stream = {streamId++, StreamType::OUTPUT,
+                    static_cast<uint32_t> (UINT32_MAX),
+                    static_cast<uint32_t> (UINT32_MAX),
+                    static_cast<PixelFormat> (outputStreams[0].format),
+                    0, 0, StreamRotation::ROTATION_0};
+            streams[0] = stream;
+            config = {streams,
+                    StreamConfigurationMode::NORMAL_MODE};
+            session->configureStreams(config, [] (Status s,
+                    HalStreamConfiguration) {
+                ASSERT_EQ(Status::ILLEGAL_ARGUMENT, s);
+            });
+
+            for (auto &it : outputStreams) {
+                stream = {streamId++, StreamType::OUTPUT,
+                        static_cast<uint32_t> (it.width),
+                        static_cast<uint32_t> (it.height),
+                        static_cast<PixelFormat> (UINT32_MAX),
+                        0, 0, StreamRotation::ROTATION_0};
+                streams[0] = stream;
+                config = {streams,
+                        StreamConfigurationMode::NORMAL_MODE};
+                session->configureStreams(config, [] (Status s,
+                        HalStreamConfiguration) {
+                    ASSERT_EQ(Status::ILLEGAL_ARGUMENT, s);
+                });
+
+                stream = {streamId++, StreamType::OUTPUT,
+                        static_cast<uint32_t> (it.width),
+                        static_cast<uint32_t> (it.height),
+                        static_cast<PixelFormat> (it.format),
+                        0, 0, static_cast<StreamRotation> (UINT32_MAX)};
+                streams[0] = stream;
+                config = {streams,
+                        StreamConfigurationMode::NORMAL_MODE};
+                session->configureStreams(config, [] (Status s,
+                        HalStreamConfiguration) {
+                    ASSERT_EQ(Status::ILLEGAL_ARGUMENT, s);
+                });
+            }
+
+            session->close();
+        }
+    }
+}
+
+// Check whether all supported ZSL output stream combinations can be
+// configured successfully.
+TEST_F(CameraHidlTest, configureStreamsZSLInputOutputs) {
+    CameraHidlEnvironment* env = CameraHidlEnvironment::Instance();
+    hidl_vec<hidl_string> cameraDeviceNames = getCameraDeviceNames();
+    std::vector<AvailableStream> inputStreams;
+    std::vector<AvailableZSLInputOutput> inputOutputMap;
+
+    for (const auto& name : cameraDeviceNames) {
+        if (getCameraDeviceVersion(name) == CAMERA_DEVICE_API_VERSION_3_2) {
+            ::android::sp<::android::hardware::camera::device::V3_2::ICameraDevice> device3_2;
+            ALOGI("configureStreams: Testing camera device %s", name.c_str());
+            env->mProvider->getCameraDeviceInterface_V3_x(
+                name,
+                [&](auto status, const auto& device) {
+                    ALOGI("getCameraDeviceInterface_V3_x returns status:%d", (int)status);
+                    ASSERT_EQ(Status::OK, status);
+                    ASSERT_NE(device, nullptr);
+                    device3_2 = device;
+                });
+
+            sp<EmptyDeviceCb> cb = new EmptyDeviceCb;
+            sp<ICameraDeviceSession> session;
+            device3_2->open(
+                cb,
+                [&](auto status, const auto& newSession) {
+                    ALOGI("device::open returns status:%d", (int)status);
+                    ASSERT_EQ(Status::OK, status);
+                    ASSERT_NE(newSession, nullptr);
+                    session = newSession;
+                });
+
+            camera_metadata_t *staticMeta;
+            device3_2->getCameraCharacteristics([&] (Status s,
+                    CameraMetadata metadata) {
+                ASSERT_EQ(Status::OK, s);
+                staticMeta =
+                        reinterpret_cast<camera_metadata_t*>(metadata.data());
+            });
+            Status ret = isZSLModeAvailable(staticMeta);
+            if (Status::METHOD_NOT_SUPPORTED == ret) {
+                session->close();
+                continue;
+            }
+            ASSERT_EQ(Status::OK, ret);
+
+            inputStreams.clear();
+            ASSERT_EQ(Status::OK, getAvailableOutputStreams(staticMeta,
+                    inputStreams));
+            ASSERT_NE(0u, inputStreams.size());
+
+            inputOutputMap.clear();
+            ASSERT_EQ(Status::OK, getZSLInputOutputMap(staticMeta,
+                    inputOutputMap));
+            ASSERT_NE(0u, inputOutputMap.size());
+
+            int32_t streamId = 0;
+            for (auto &inputIter : inputOutputMap) {
+                AvailableStream input, output;
+                ASSERT_EQ(Status::OK,
+                        findLargestSize(inputStreams, inputIter.inputFormat, input));
+                ASSERT_NE(0u, inputStreams.size());
+
+                AvailableStream outputThreshold = {INT32_MAX, INT32_MAX,
+                        inputIter.outputFormat};
+                std::vector<AvailableStream> outputStreams;
+                ASSERT_EQ(Status::OK, getAvailableOutputStreams(staticMeta,
+                        outputStreams, &outputThreshold));
+                for (auto &outputIter : outputStreams) {
+                    Stream zslStream = {streamId++, StreamType::OUTPUT,
+                            static_cast<uint32_t> (input.width),
+                            static_cast<uint32_t> (input.height),
+                            static_cast<PixelFormat> (input.format),
+                            GRALLOC_USAGE_HW_CAMERA_ZSL, 0,
+                            StreamRotation::ROTATION_0};
+                    Stream inputStream = {streamId++, StreamType::INPUT,
+                            static_cast<uint32_t> (input.width),
+                            static_cast<uint32_t> (input.height),
+                            static_cast<PixelFormat> (input.format), 0, 0,
+                            StreamRotation::ROTATION_0};
+                    Stream outputStream = {streamId++, StreamType::OUTPUT,
+                            static_cast<uint32_t> (outputIter.width),
+                            static_cast<uint32_t> (outputIter.height),
+                            static_cast<PixelFormat> (outputIter.format), 0, 0,
+                            StreamRotation::ROTATION_0};
+
+                    ::android::hardware::hidl_vec<Stream> streams = {
+                            inputStream, zslStream, outputStream};
+                    StreamConfiguration config = {streams,
+                            StreamConfigurationMode::NORMAL_MODE};
+                    session->configureStreams(config, [streamId] (Status s,
+                            HalStreamConfiguration halConfig) {
+                        ASSERT_EQ(Status::OK, s);
+                        ASSERT_EQ(3u, halConfig.streams.size());
+                    });
+                }
+            }
+
+            session->close();
+        }
+    }
+}
+
+// Verify that all supported preview + still capture stream combinations
+// can be configured successfully.
+TEST_F(CameraHidlTest, configureStreamsPreviewStillOutputs) {
+    CameraHidlEnvironment* env = CameraHidlEnvironment::Instance();
+    hidl_vec<hidl_string> cameraDeviceNames = getCameraDeviceNames();
+    std::vector<AvailableStream> outputBlobStreams;
+    std::vector<AvailableStream> outputPreviewStreams;
+    AvailableStream previewThreshold = {MAX_PREVIEW_WIDTH, MAX_PREVIEW_HEIGHT,
+            static_cast<int32_t>(PixelFormat::IMPLEMENTATION_DEFINED)};
+    AvailableStream blobThreshold = {INT32_MAX, INT32_MAX,
+            static_cast<int32_t>(PixelFormat::BLOB)};
+
+    for (const auto& name : cameraDeviceNames) {
+        if (getCameraDeviceVersion(name) == CAMERA_DEVICE_API_VERSION_3_2) {
+            ::android::sp<::android::hardware::camera::device::V3_2::ICameraDevice> device3_2;
+            ALOGI("configureStreams: Testing camera device %s", name.c_str());
+            env->mProvider->getCameraDeviceInterface_V3_x(
+                name,
+                [&](auto status, const auto& device) {
+                    ALOGI("getCameraDeviceInterface_V3_x returns status:%d", (int)status);
+                    ASSERT_EQ(Status::OK, status);
+                    ASSERT_NE(device, nullptr);
+                    device3_2 = device;
+                });
+
+            sp<EmptyDeviceCb> cb = new EmptyDeviceCb;
+            sp<ICameraDeviceSession> session;
+            device3_2->open(
+                cb,
+                [&](auto status, const auto& newSession) {
+                    ALOGI("device::open returns status:%d", (int)status);
+                    ASSERT_EQ(Status::OK, status);
+                    ASSERT_NE(newSession, nullptr);
+                    session = newSession;
+                });
+
+            camera_metadata_t *staticMeta;
+            device3_2->getCameraCharacteristics([&] (Status s,
+                    CameraMetadata metadata) {
+                ASSERT_EQ(Status::OK, s);
+                staticMeta =
+                        reinterpret_cast<camera_metadata_t*>(metadata.data());
+            });
+            outputBlobStreams.clear();
+            ASSERT_EQ(Status::OK, getAvailableOutputStreams(staticMeta,
+                    outputBlobStreams, &blobThreshold));
+            ASSERT_NE(0u, outputBlobStreams.size());
+
+            outputPreviewStreams.clear();
+            ASSERT_EQ(Status::OK, getAvailableOutputStreams(staticMeta,
+                    outputPreviewStreams, &previewThreshold));
+            ASSERT_NE(0u, outputPreviewStreams.size());
+
+            int32_t streamId = 0;
+            for (auto &blobIter : outputBlobStreams) {
+                for (auto &previewIter : outputPreviewStreams) {
+                    Stream previewStream = {streamId++, StreamType::OUTPUT,
+                            static_cast<uint32_t> (previewIter.width),
+                            static_cast<uint32_t> (previewIter.height),
+                            static_cast<PixelFormat> (previewIter.format), 0, 0,
+                            StreamRotation::ROTATION_0};
+                    Stream blobStream = {streamId++, StreamType::OUTPUT,
+                            static_cast<uint32_t> (blobIter.width),
+                            static_cast<uint32_t> (blobIter.height),
+                            static_cast<PixelFormat> (blobIter.format), 0, 0,
+                            StreamRotation::ROTATION_0};
+                    ::android::hardware::hidl_vec<Stream> streams = {
+                            previewStream, blobStream};
+                    StreamConfiguration config = {streams,
+                            StreamConfigurationMode::NORMAL_MODE};
+                    session->configureStreams(config, [streamId] (Status s,
+                            HalStreamConfiguration halConfig) {
+                        ASSERT_EQ(Status::OK, s);
+                        ASSERT_EQ(2u, halConfig.streams.size());
+                    });
+                }
+            }
+
+            session->close();
+        }
+    }
+}
+
+// In case constrained mode is supported, test whether it can be
+// configured. Additionally check for common invalid inputs when
+// using this mode.
+TEST_F(CameraHidlTest, configureStreamsConstrainedOutputs) {
+    CameraHidlEnvironment* env = CameraHidlEnvironment::Instance();
+    hidl_vec<hidl_string> cameraDeviceNames = getCameraDeviceNames();
+
+    for (const auto& name : cameraDeviceNames) {
+        if (getCameraDeviceVersion(name) == CAMERA_DEVICE_API_VERSION_3_2) {
+            ::android::sp<::android::hardware::camera::device::V3_2::ICameraDevice> device3_2;
+            ALOGI("configureStreams: Testing camera device %s", name.c_str());
+            env->mProvider->getCameraDeviceInterface_V3_x(
+                name,
+                [&](auto status, const auto& device) {
+                    ALOGI("getCameraDeviceInterface_V3_x returns status:%d", (int)status);
+                    ASSERT_EQ(Status::OK, status);
+                    ASSERT_NE(device, nullptr);
+                    device3_2 = device;
+                });
+
+            sp<EmptyDeviceCb> cb = new EmptyDeviceCb;
+            sp<ICameraDeviceSession> session;
+            device3_2->open(
+                cb,
+                [&](auto status, const auto& newSession) {
+                    ALOGI("device::open returns status:%d", (int)status);
+                    ASSERT_EQ(Status::OK, status);
+                    ASSERT_NE(newSession, nullptr);
+                    session = newSession;
+                });
+
+            camera_metadata_t *staticMeta;
+            device3_2->getCameraCharacteristics([&] (Status s,
+                    CameraMetadata metadata) {
+                ASSERT_EQ(Status::OK, s);
+                staticMeta =
+                        reinterpret_cast<camera_metadata_t*>(metadata.data());
+            });
+            Status rc = isConstrainedModeAvailable(staticMeta);
+            if (Status::METHOD_NOT_SUPPORTED == rc) {
+                session->close();
+                continue;
+            }
+            ASSERT_EQ(Status::OK, rc);
+
+            AvailableStream hfrStream;
+            rc = pickConstrainedModeSize(staticMeta, hfrStream);
+            ASSERT_EQ(Status::OK, rc);
+
+            int32_t streamId = 0;
+            Stream stream = {streamId, StreamType::OUTPUT,
+                    static_cast<uint32_t> (hfrStream.width),
+                    static_cast<uint32_t> (hfrStream.height),
+                    static_cast<PixelFormat> (hfrStream.format), 0, 0,
+                    StreamRotation::ROTATION_0};
+            ::android::hardware::hidl_vec<Stream> streams = {stream};
+            StreamConfiguration config = {streams,
+                    StreamConfigurationMode::CONSTRAINED_HIGH_SPEED_MODE};
+            session->configureStreams(config, [streamId] (Status s,
+                    HalStreamConfiguration halConfig) {
+                ASSERT_EQ(Status::OK, s);
+                ASSERT_EQ(1u, halConfig.streams.size());
+                ASSERT_EQ(halConfig.streams[0].id, streamId);
+            });
+
+            stream = {streamId++, StreamType::OUTPUT,
+                    static_cast<uint32_t> (0),
+                    static_cast<uint32_t> (0),
+                    static_cast<PixelFormat> (hfrStream.format), 0, 0,
+                    StreamRotation::ROTATION_0};
+            streams[0] = stream;
+            config = {streams,
+                    StreamConfigurationMode::CONSTRAINED_HIGH_SPEED_MODE};
+            session->configureStreams(config, [streamId] (Status s,
+                    HalStreamConfiguration) {
+                ASSERT_EQ(Status::ILLEGAL_ARGUMENT, s);
+            });
+
+            stream = {streamId++, StreamType::OUTPUT,
+                    static_cast<uint32_t> (UINT32_MAX),
+                    static_cast<uint32_t> (UINT32_MAX),
+                    static_cast<PixelFormat> (hfrStream.format), 0, 0,
+                    StreamRotation::ROTATION_0};
+            streams[0] = stream;
+            config = {streams,
+                    StreamConfigurationMode::CONSTRAINED_HIGH_SPEED_MODE};
+            session->configureStreams(config, [streamId] (Status s,
+                    HalStreamConfiguration) {
+                ASSERT_EQ(Status::ILLEGAL_ARGUMENT, s);
+            });
+
+            stream = {streamId++, StreamType::OUTPUT,
+                    static_cast<uint32_t> (hfrStream.width),
+                    static_cast<uint32_t> (hfrStream.height),
+                    static_cast<PixelFormat> (UINT32_MAX), 0, 0,
+                    StreamRotation::ROTATION_0};
+            streams[0] = stream;
+            config = {streams,
+                    StreamConfigurationMode::CONSTRAINED_HIGH_SPEED_MODE};
+            session->configureStreams(config, [streamId] (Status s,
+                    HalStreamConfiguration) {
+                ASSERT_EQ(Status::ILLEGAL_ARGUMENT, s);
+            });
+
+            session->close();
+        }
+    }
+}
+
+// Verify that all supported video + snapshot stream combinations can
+// be configured successfully.
+TEST_F(CameraHidlTest, configureStreamsVideoStillOutputs) {
     CameraHidlEnvironment* env = CameraHidlEnvironment::Instance();
     hidl_vec<hidl_string> cameraDeviceNames = getCameraDeviceNames();
+    std::vector<AvailableStream> outputBlobStreams;
+    std::vector<AvailableStream> outputVideoStreams;
+    AvailableStream videoThreshold = {MAX_VIDEO_WIDTH, MAX_VIDEO_HEIGHT,
+            static_cast<int32_t>(PixelFormat::IMPLEMENTATION_DEFINED)};
+    AvailableStream blobThreshold = {MAX_VIDEO_WIDTH, MAX_VIDEO_HEIGHT,
+            static_cast<int32_t>(PixelFormat::BLOB)};
 
     for (const auto& name : cameraDeviceNames) {
         if (getCameraDeviceVersion(name) == CAMERA_DEVICE_API_VERSION_3_2) {
@@ -500,12 +1018,224 @@ TEST_F(CameraHidlTest, configureStreams) {
                     session = newSession;
                 });
 
+            camera_metadata_t *staticMeta;
+            device3_2->getCameraCharacteristics([&] (Status s,
+                    CameraMetadata metadata) {
+                ASSERT_EQ(Status::OK, s);
+                staticMeta =
+                        reinterpret_cast<camera_metadata_t*>(metadata.data());
+            });
+            outputBlobStreams.clear();
+            ASSERT_EQ(Status::OK, getAvailableOutputStreams(staticMeta,
+                    outputBlobStreams, &blobThreshold));
+            ASSERT_NE(0u, outputBlobStreams.size());
+
+            outputVideoStreams.clear();
+            ASSERT_EQ(Status::OK, getAvailableOutputStreams(staticMeta,
+                    outputVideoStreams, &videoThreshold));
+            ASSERT_NE(0u, outputVideoStreams.size());
+
+            int32_t streamId = 0;
+            for (auto &blobIter : outputBlobStreams) {
+                for (auto &videoIter : outputVideoStreams) {
+                    Stream videoStream = {streamId++, StreamType::OUTPUT,
+                            static_cast<uint32_t> (videoIter.width),
+                            static_cast<uint32_t> (videoIter.height),
+                            static_cast<PixelFormat> (videoIter.format), 0, 0,
+                            StreamRotation::ROTATION_0};
+                    Stream blobStream = {streamId++, StreamType::OUTPUT,
+                            static_cast<uint32_t> (blobIter.width),
+                            static_cast<uint32_t> (blobIter.height),
+                            static_cast<PixelFormat> (blobIter.format),
+                            GRALLOC_USAGE_HW_VIDEO_ENCODER, 0,
+                            StreamRotation::ROTATION_0};
+                    ::android::hardware::hidl_vec<Stream> streams = {
+                            videoStream, blobStream};
+                    StreamConfiguration config = {streams,
+                            StreamConfigurationMode::NORMAL_MODE};
+                    session->configureStreams(config, [streamId] (Status s,
+                            HalStreamConfiguration halConfig) {
+                        ASSERT_EQ(Status::OK, s);
+                        ASSERT_EQ(2u, halConfig.streams.size());
+                    });
+                }
+            }
 
             session->close();
         }
     }
 }
 
+// Retrieve all valid output stream resolutions from the camera
+// static characteristics.
+Status CameraHidlTest::getAvailableOutputStreams(camera_metadata_t *staticMeta,
+        std::vector<AvailableStream> &outputStreams,
+        AvailableStream *threshold) {
+    if (nullptr == staticMeta) {
+        return Status::ILLEGAL_ARGUMENT;
+    }
+
+    camera_metadata_ro_entry entry;
+    int rc = find_camera_metadata_ro_entry(staticMeta,
+            ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS, &entry);
+    if ((0 != rc) || (0 != (entry.count % 4))) {
+        return Status::ILLEGAL_ARGUMENT;
+    }
+
+    for (size_t i = 0; i < entry.count; i+=4) {
+        if (ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT ==
+                entry.data.i32[i + 3]) {
+            if(nullptr == threshold) {
+                AvailableStream s = {entry.data.i32[i+1],
+                        entry.data.i32[i+2], entry.data.i32[i]};
+                outputStreams.push_back(s);
+            } else {
+                if ((threshold->format == entry.data.i32[i]) &&
+                        (threshold->width >= entry.data.i32[i+1]) &&
+                        (threshold->height >= entry.data.i32[i+2])) {
+                    AvailableStream s = {entry.data.i32[i+1],
+                            entry.data.i32[i+2], threshold->format};
+                    outputStreams.push_back(s);
+                }
+            }
+        }
+
+    }
+
+    return Status::OK;
+}
+
+// Check if constrained mode is supported by using the static
+// camera characteristics.
+Status CameraHidlTest::isConstrainedModeAvailable(camera_metadata_t *staticMeta) {
+    Status ret = Status::METHOD_NOT_SUPPORTED;
+    if (nullptr == staticMeta) {
+        return Status::ILLEGAL_ARGUMENT;
+    }
+
+    camera_metadata_ro_entry entry;
+    int rc = find_camera_metadata_ro_entry(staticMeta,
+            ANDROID_REQUEST_AVAILABLE_CAPABILITIES, &entry);
+    if (0 != rc) {
+        return Status::ILLEGAL_ARGUMENT;
+    }
+
+    for (size_t i = 0; i < entry.count; i++) {
+        if (ANDROID_REQUEST_AVAILABLE_CAPABILITIES_CONSTRAINED_HIGH_SPEED_VIDEO ==
+                entry.data.u8[i]) {
+            ret = Status::OK;
+            break;
+        }
+    }
+
+    return ret;
+}
+
+// Pick the largest supported HFR mode from the static camera
+// characteristics.
+Status CameraHidlTest::pickConstrainedModeSize(camera_metadata_t *staticMeta,
+        AvailableStream &hfrStream) {
+    if (nullptr == staticMeta) {
+        return Status::ILLEGAL_ARGUMENT;
+    }
+
+    camera_metadata_ro_entry entry;
+    int rc = find_camera_metadata_ro_entry(staticMeta,
+            ANDROID_CONTROL_AVAILABLE_HIGH_SPEED_VIDEO_CONFIGURATIONS, &entry);
+    if (0 != rc) {
+        return Status::METHOD_NOT_SUPPORTED;
+    } else if (0 != (entry.count % 5)) {
+        return Status::ILLEGAL_ARGUMENT;
+    }
+
+    hfrStream = {0, 0,
+            static_cast<uint32_t>(PixelFormat::IMPLEMENTATION_DEFINED)};
+    for (size_t i = 0; i < entry.count; i+=5) {
+        int32_t w = entry.data.i32[i];
+        int32_t h = entry.data.i32[i+1];
+        if ((hfrStream.width * hfrStream.height) < (w *h)) {
+            hfrStream.width = w;
+            hfrStream.height = h;
+        }
+    }
+
+    return Status::OK;
+}
+
+// Check whether ZSL is available using the static camera
+// characteristics.
+Status CameraHidlTest::isZSLModeAvailable(camera_metadata_t *staticMeta) {
+    Status ret = Status::METHOD_NOT_SUPPORTED;
+    if (nullptr == staticMeta) {
+        return Status::ILLEGAL_ARGUMENT;
+    }
+
+    camera_metadata_ro_entry entry;
+    int rc = find_camera_metadata_ro_entry(staticMeta,
+            ANDROID_REQUEST_AVAILABLE_CAPABILITIES, &entry);
+    if (0 != rc) {
+        return Status::ILLEGAL_ARGUMENT;
+    }
+
+    for (size_t i = 0; i < entry.count; i++) {
+        if ((ANDROID_REQUEST_AVAILABLE_CAPABILITIES_PRIVATE_REPROCESSING ==
+                entry.data.u8[i]) ||
+                (ANDROID_REQUEST_AVAILABLE_CAPABILITIES_YUV_REPROCESSING ==
+                        entry.data.u8[i]) ){
+            ret = Status::OK;
+            break;
+        }
+    }
+
+    return ret;
+}
+
+// Retrieve the reprocess input-output format map from the static
+// camera characteristics.
+Status CameraHidlTest::getZSLInputOutputMap(camera_metadata_t *staticMeta,
+        std::vector<AvailableZSLInputOutput> &inputOutputMap) {
+    if (nullptr == staticMeta) {
+        return Status::ILLEGAL_ARGUMENT;
+    }
+
+    camera_metadata_ro_entry entry;
+    int rc = find_camera_metadata_ro_entry(staticMeta,
+            ANDROID_SCALER_AVAILABLE_INPUT_OUTPUT_FORMATS_MAP, &entry);
+    if ((0 != rc) || (0 >= entry.count)) {
+        return Status::ILLEGAL_ARGUMENT;
+    }
+
+    const int32_t* contents = &entry.data.i32[0];
+    for (size_t i = 0; i < entry.count; ) {
+        int32_t inputFormat = contents[i++];
+        int32_t length = contents[i++];
+        for (int32_t j = 0; j < length; j++) {
+            int32_t outputFormat = contents[i+j];
+            AvailableZSLInputOutput zslEntry = {inputFormat, outputFormat};
+            inputOutputMap.push_back(zslEntry);
+        }
+        i += length;
+    }
+
+    return Status::OK;
+}
+
+// Search for the largest stream size for a given format.
+Status CameraHidlTest::findLargestSize(
+        const std::vector<AvailableStream> &streamSizes, int32_t format,
+        AvailableStream &result) {
+    result = {0, 0, 0};
+    for (auto &iter : streamSizes) {
+        if (format == iter.format) {
+            if ((result.width * result.height) < (iter.width * iter.height)) {
+                result = iter;
+            }
+        }
+    }
+
+    return (result.format == format) ? Status::OK : Status::ILLEGAL_ARGUMENT;
+}
+
 int main(int argc, char **argv) {
   ::testing::AddGlobalTestEnvironment(CameraHidlEnvironment::Instance());
   ::testing::InitGoogleTest(&argc, argv);