C++程序  |  6336行  |  272.33 KB

/*
 * Copyright (C) 2016-2018 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#define LOG_TAG "camera_hidl_hal_test"

#include <algorithm>
#include <chrono>
#include <mutex>
#include <regex>
#include <unordered_map>
#include <unordered_set>
#include <condition_variable>

#include <inttypes.h>

#include <android/hardware/camera/device/1.0/ICameraDevice.h>
#include <android/hardware/camera/device/3.2/ICameraDevice.h>
#include <android/hardware/camera/device/3.5/ICameraDevice.h>
#include <android/hardware/camera/device/3.3/ICameraDeviceSession.h>
#include <android/hardware/camera/device/3.4/ICameraDeviceSession.h>
#include <android/hardware/camera/device/3.5/ICameraDeviceSession.h>
#include <android/hardware/camera/device/3.4/ICameraDeviceCallback.h>
#include <android/hardware/camera/device/3.5/ICameraDeviceCallback.h>
#include <android/hardware/camera/provider/2.4/ICameraProvider.h>
#include <android/hardware/camera/provider/2.5/ICameraProvider.h>
#include <android/hardware/camera/metadata/3.4/types.h>
#include <android/hidl/manager/1.0/IServiceManager.h>
#include <binder/MemoryHeapBase.h>
#include <CameraMetadata.h>
#include <CameraParameters.h>
#include <cutils/properties.h>
#include <fmq/MessageQueue.h>
#include <grallocusage/GrallocUsageConversion.h>
#include <gui/BufferItemConsumer.h>
#include <gui/BufferQueue.h>
#include <gui/Surface.h>
#include <hardware/gralloc.h>
#include <hardware/gralloc1.h>
#include <system/camera.h>
#include <system/camera_metadata.h>
#include <ui/GraphicBuffer.h>

#include <android/hardware/graphics/allocator/2.0/IAllocator.h>
#include <android/hardware/graphics/allocator/3.0/IAllocator.h>
#include <android/hardware/graphics/mapper/2.0/IMapper.h>
#include <android/hardware/graphics/mapper/2.0/types.h>
#include <android/hardware/graphics/mapper/3.0/IMapper.h>
#include <android/hidl/allocator/1.0/IAllocator.h>
#include <android/hidl/memory/1.0/IMapper.h>
#include <android/hidl/memory/1.0/IMemory.h>

#include <VtsHalHidlTargetTestBase.h>
#include <VtsHalHidlTargetTestEnvBase.h>

using namespace ::android::hardware::camera::device;
using ::android::hardware::Return;
using ::android::hardware::Void;
using ::android::hardware::hidl_bitfield;
using ::android::hardware::hidl_handle;
using ::android::hardware::hidl_string;
using ::android::hardware::hidl_vec;
using ::android::sp;
using ::android::wp;
using ::android::GraphicBuffer;
using ::android::IGraphicBufferProducer;
using ::android::IGraphicBufferConsumer;
using ::android::BufferQueue;
using ::android::BufferItemConsumer;
using ::android::Surface;
using ::android::hardware::graphics::common::V1_0::BufferUsage;
using ::android::hardware::graphics::common::V1_0::Dataspace;
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;
using ::android::hardware::camera::common::V1_0::TorchModeStatus;
using ::android::hardware::camera::common::V1_0::helper::CameraParameters;
using ::android::hardware::camera::common::V1_0::helper::Size;
using ::android::hardware::camera::provider::V2_4::ICameraProvider;
using ::android::hardware::camera::provider::V2_4::ICameraProviderCallback;
using ::android::hardware::camera::device::V3_2::ICameraDevice;
using ::android::hardware::camera::device::V3_2::BufferCache;
using ::android::hardware::camera::device::V3_2::CaptureRequest;
using ::android::hardware::camera::device::V3_2::CaptureResult;
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::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;
using ::android::hardware::camera::device::V3_2::BufferStatus;
using ::android::hardware::camera::device::V3_2::StreamBuffer;
using ::android::hardware::camera::device::V3_2::MsgType;
using ::android::hardware::camera::device::V3_2::ErrorMsg;
using ::android::hardware::camera::device::V3_2::ErrorCode;
using ::android::hardware::camera::device::V1_0::CameraFacing;
using ::android::hardware::camera::device::V1_0::NotifyCallbackMsg;
using ::android::hardware::camera::device::V1_0::CommandType;
using ::android::hardware::camera::device::V1_0::DataCallbackMsg;
using ::android::hardware::camera::device::V1_0::CameraFrameMetadata;
using ::android::hardware::camera::device::V1_0::ICameraDevicePreviewCallback;
using ::android::hardware::camera::device::V1_0::FrameCallbackFlag;
using ::android::hardware::camera::device::V1_0::HandleTimestampMessage;
using ::android::hardware::camera::metadata::V3_4::CameraMetadataEnumAndroidSensorInfoColorFilterArrangement;
using ::android::hardware::camera::metadata::V3_4::CameraMetadataTag;
using ::android::hardware::camera::device::V3_4::PhysicalCameraMetadata;
using ::android::hardware::MessageQueue;
using ::android::hardware::kSynchronizedReadWrite;
using ::android::hidl::allocator::V1_0::IAllocator;
using ::android::hidl::memory::V1_0::IMemory;
using ::android::hidl::memory::V1_0::IMapper;
using ResultMetadataQueue = MessageQueue<uint8_t, kSynchronizedReadWrite>;
using ::android::hidl::manager::V1_0::IServiceManager;

using namespace ::android::hardware::camera;

const uint32_t kMaxPreviewWidth = 1920;
const uint32_t kMaxPreviewHeight = 1080;
const uint32_t kMaxVideoWidth = 4096;
const uint32_t kMaxVideoHeight = 2160;
const int64_t kStreamBufferTimeoutSec = 3;
const int64_t kAutoFocusTimeoutSec = 5;
const int64_t kTorchTimeoutSec = 1;
const int64_t kEmptyFlushTimeoutMSec = 200;
const char kDumpOutput[] = "/dev/null";
const uint32_t kBurstFrameCount = 10;
const int64_t kBufferReturnTimeoutSec = 1;

struct AvailableStream {
    int32_t width;
    int32_t height;
    int32_t format;
};

struct AvailableZSLInputOutput {
    int32_t inputFormat;
    int32_t outputFormat;
};

enum ReprocessType {
    PRIV_REPROCESS,
    YUV_REPROCESS,
};

namespace {
    // "device@<version>/legacy/<id>"
    const char *kDeviceNameRE = "device@([0-9]+\\.[0-9]+)/%s/(.+)";
    const int CAMERA_DEVICE_API_VERSION_3_5 = 0x305;
    const int CAMERA_DEVICE_API_VERSION_3_4 = 0x304;
    const int CAMERA_DEVICE_API_VERSION_3_3 = 0x303;
    const int CAMERA_DEVICE_API_VERSION_3_2 = 0x302;
    const int CAMERA_DEVICE_API_VERSION_1_0 = 0x100;
    const char *kHAL3_5 = "3.5";
    const char *kHAL3_4 = "3.4";
    const char *kHAL3_3 = "3.3";
    const char *kHAL3_2 = "3.2";
    const char *kHAL1_0 = "1.0";

    bool matchDeviceName(const hidl_string& deviceName,
            const hidl_string &providerType,
            std::string* deviceVersion,
            std::string* cameraId) {
        ::android::String8 pattern;
        pattern.appendFormat(kDeviceNameRE, providerType.c_str());
        std::regex e(pattern.string());
        std::string deviceNameStd(deviceName.c_str());
        std::smatch sm;
        if (std::regex_match(deviceNameStd, sm, e)) {
            if (deviceVersion != nullptr) {
                *deviceVersion = sm[1];
            }
            if (cameraId != nullptr) {
                *cameraId = sm[2];
            }
            return true;
        }
        return false;
    }

    int getCameraDeviceVersion(const hidl_string& deviceName,
            const hidl_string &providerType) {
        std::string version;
        bool match = matchDeviceName(deviceName, providerType, &version, nullptr);
        if (!match) {
            return -1;
        }

        if (version.compare(kHAL3_5) == 0) {
            return CAMERA_DEVICE_API_VERSION_3_5;
        } else if (version.compare(kHAL3_4) == 0) {
            return CAMERA_DEVICE_API_VERSION_3_4;
        } else if (version.compare(kHAL3_3) == 0) {
            return CAMERA_DEVICE_API_VERSION_3_3;
        } else if (version.compare(kHAL3_2) == 0) {
            return CAMERA_DEVICE_API_VERSION_3_2;
        } else if (version.compare(kHAL1_0) == 0) {
            return CAMERA_DEVICE_API_VERSION_1_0;
        }
        return 0;
    }

    bool parseProviderName(const std::string& name, std::string *type /*out*/,
            uint32_t *id /*out*/) {
        if (!type || !id) {
            ADD_FAILURE();
            return false;
        }

        std::string::size_type slashIdx = name.find('/');
        if (slashIdx == std::string::npos || slashIdx == name.size() - 1) {
            ADD_FAILURE() << "Provider name does not have / separator between type"
                    "and id";
            return false;
        }

        std::string typeVal = name.substr(0, slashIdx);

        char *endPtr;
        errno = 0;
        long idVal = strtol(name.c_str() + slashIdx + 1, &endPtr, 10);
        if (errno != 0) {
            ADD_FAILURE() << "cannot parse provider id as an integer:" <<
                    name.c_str() << strerror(errno) << errno;
            return false;
        }
        if (endPtr != name.c_str() + name.size()) {
            ADD_FAILURE() << "provider id has unexpected length " << name.c_str();
            return false;
        }
        if (idVal < 0) {
            ADD_FAILURE() << "id is negative: " << name.c_str() << idVal;
            return false;
        }

        *type = typeVal;
        *id = static_cast<uint32_t>(idVal);

        return true;
    }

    Status mapToStatus(::android::status_t s)  {
        switch(s) {
            case ::android::OK:
                return Status::OK ;
            case ::android::BAD_VALUE:
                return Status::ILLEGAL_ARGUMENT ;
            case -EBUSY:
                return Status::CAMERA_IN_USE;
            case -EUSERS:
                return Status::MAX_CAMERAS_IN_USE;
            case ::android::UNKNOWN_TRANSACTION:
                return Status::METHOD_NOT_SUPPORTED;
            case ::android::INVALID_OPERATION:
                return Status::OPERATION_NOT_SUPPORTED;
            case ::android::DEAD_OBJECT:
                return Status::CAMERA_DISCONNECTED;
        }
        ALOGW("Unexpected HAL status code %d", s);
        return Status::OPERATION_NOT_SUPPORTED;
    }

    void getFirstApiLevel(/*out*/int32_t* outApiLevel) {
        int32_t firstApiLevel = property_get_int32("ro.product.first_api_level", /*default*/-1);
        if (firstApiLevel < 0) {
            firstApiLevel = property_get_int32("ro.build.version.sdk", /*default*/-1);
        }
        ASSERT_GT(firstApiLevel, 0); // first_api_level must exist
        *outApiLevel = firstApiLevel;
        return;
    }
}

// Test environment for camera
class CameraHidlEnvironment : public ::testing::VtsHalHidlTargetTestEnvBase {
   public:
    // get the test environment singleton
    static CameraHidlEnvironment* Instance() {
        static CameraHidlEnvironment* instance = new CameraHidlEnvironment;
        return instance;
    }

    virtual void HidlSetUp() override { ALOGI("SetUp CameraHidlEnvironment"); }

    virtual void HidlTearDown() override { ALOGI("TearDown CameraHidlEnvironment"); }

    virtual void registerTestServices() override { registerTestService<ICameraProvider>(); }

   private:
    CameraHidlEnvironment() {}

    GTEST_DISALLOW_COPY_AND_ASSIGN_(CameraHidlEnvironment);
};

struct BufferItemHander: public BufferItemConsumer::FrameAvailableListener {
    BufferItemHander(wp<BufferItemConsumer> consumer) : mConsumer(consumer) {}

    void onFrameAvailable(const android::BufferItem&) override {
        sp<BufferItemConsumer> consumer = mConsumer.promote();
        ASSERT_NE(nullptr, consumer.get());

        android::BufferItem buffer;
        ASSERT_EQ(android::OK, consumer->acquireBuffer(&buffer, 0));
        ASSERT_EQ(android::OK, consumer->releaseBuffer(buffer));
    }

 private:
    wp<BufferItemConsumer> mConsumer;
};

struct PreviewWindowCb : public ICameraDevicePreviewCallback {
    PreviewWindowCb(sp<ANativeWindow> anw) : mPreviewWidth(0),
            mPreviewHeight(0), mFormat(0), mPreviewUsage(0),
            mPreviewSwapInterval(-1), mCrop{-1, -1, -1, -1}, mAnw(anw) {}

    using dequeueBuffer_cb =
            std::function<void(Status status, uint64_t bufferId,
                    const hidl_handle& buffer, uint32_t stride)>;
    Return<void> dequeueBuffer(dequeueBuffer_cb _hidl_cb) override;

    Return<Status> enqueueBuffer(uint64_t bufferId) override;

    Return<Status> cancelBuffer(uint64_t bufferId) override;

    Return<Status> setBufferCount(uint32_t count) override;

    Return<Status> setBuffersGeometry(uint32_t w,
            uint32_t h, PixelFormat format) override;

    Return<Status> setCrop(int32_t left, int32_t top,
            int32_t right, int32_t bottom) override;

    Return<Status> setUsage(BufferUsage usage) override;

    Return<Status> setSwapInterval(int32_t interval) override;

    using getMinUndequeuedBufferCount_cb =
            std::function<void(Status status, uint32_t count)>;
    Return<void> getMinUndequeuedBufferCount(
            getMinUndequeuedBufferCount_cb _hidl_cb) override;

    Return<Status> setTimestamp(int64_t timestamp) override;

 private:
    struct BufferHasher {
        size_t operator()(const buffer_handle_t& buf) const {
            if (buf == nullptr)
                return 0;

            size_t result = 1;
            result = 31 * result + buf->numFds;
            for (int i = 0; i < buf->numFds; i++) {
                result = 31 * result + buf->data[i];
            }
            return result;
        }
    };

    struct BufferComparator {
        bool operator()(const buffer_handle_t& buf1,
                const buffer_handle_t& buf2) const {
            if (buf1->numFds == buf2->numFds) {
                for (int i = 0; i < buf1->numFds; i++) {
                    if (buf1->data[i] != buf2->data[i]) {
                        return false;
                    }
                }
                return true;
            }
            return false;
        }
    };

    std::pair<bool, uint64_t> getBufferId(ANativeWindowBuffer* anb);
    void cleanupCirculatingBuffers();

    std::mutex mBufferIdMapLock; // protecting mBufferIdMap and mNextBufferId
    typedef std::unordered_map<const buffer_handle_t, uint64_t,
            BufferHasher, BufferComparator> BufferIdMap;

    BufferIdMap mBufferIdMap; // stream ID -> per stream buffer ID map
    std::unordered_map<uint64_t, ANativeWindowBuffer*> mReversedBufMap;
    uint64_t mNextBufferId = 1;

    uint32_t mPreviewWidth, mPreviewHeight;
    int mFormat, mPreviewUsage;
    int32_t mPreviewSwapInterval;
    android_native_rect_t mCrop;
    sp<ANativeWindow> mAnw;     //Native window reference
};

std::pair<bool, uint64_t> PreviewWindowCb::getBufferId(
        ANativeWindowBuffer* anb) {
    std::lock_guard<std::mutex> lock(mBufferIdMapLock);

    buffer_handle_t& buf = anb->handle;
    auto it = mBufferIdMap.find(buf);
    if (it == mBufferIdMap.end()) {
        uint64_t bufId = mNextBufferId++;
        mBufferIdMap[buf] = bufId;
        mReversedBufMap[bufId] = anb;
        return std::make_pair(true, bufId);
    } else {
        return std::make_pair(false, it->second);
    }
}

void PreviewWindowCb::cleanupCirculatingBuffers() {
    std::lock_guard<std::mutex> lock(mBufferIdMapLock);
    mBufferIdMap.clear();
    mReversedBufMap.clear();
}

Return<void> PreviewWindowCb::dequeueBuffer(dequeueBuffer_cb _hidl_cb) {
    ANativeWindowBuffer* anb;
    auto rc = native_window_dequeue_buffer_and_wait(mAnw.get(), &anb);
    uint64_t bufferId = 0;
    uint32_t stride = 0;
    hidl_handle buf = nullptr;
    if (rc == ::android::OK) {
        auto pair = getBufferId(anb);
        buf = (pair.first) ? anb->handle : nullptr;
        bufferId = pair.second;
        stride = anb->stride;
    }

    _hidl_cb(mapToStatus(rc), bufferId, buf, stride);
    return Void();
}

Return<Status> PreviewWindowCb::enqueueBuffer(uint64_t bufferId) {
    if (mReversedBufMap.count(bufferId) == 0) {
        ALOGE("%s: bufferId %" PRIu64 " not found", __FUNCTION__, bufferId);
        return Status::ILLEGAL_ARGUMENT;
    }
    return mapToStatus(mAnw->queueBuffer(mAnw.get(),
            mReversedBufMap.at(bufferId), -1));
}

Return<Status> PreviewWindowCb::cancelBuffer(uint64_t bufferId) {
    if (mReversedBufMap.count(bufferId) == 0) {
        ALOGE("%s: bufferId %" PRIu64 " not found", __FUNCTION__, bufferId);
        return Status::ILLEGAL_ARGUMENT;
    }
    return mapToStatus(mAnw->cancelBuffer(mAnw.get(),
            mReversedBufMap.at(bufferId), -1));
}

Return<Status> PreviewWindowCb::setBufferCount(uint32_t count) {
    if (mAnw.get() != nullptr) {
        // WAR for b/27039775
        native_window_api_disconnect(mAnw.get(), NATIVE_WINDOW_API_CAMERA);
        native_window_api_connect(mAnw.get(), NATIVE_WINDOW_API_CAMERA);
        if (mPreviewWidth != 0) {
            native_window_set_buffers_dimensions(mAnw.get(),
                    mPreviewWidth, mPreviewHeight);
            native_window_set_buffers_format(mAnw.get(), mFormat);
        }
        if (mPreviewUsage != 0) {
            native_window_set_usage(mAnw.get(), mPreviewUsage);
        }
        if (mPreviewSwapInterval >= 0) {
            mAnw->setSwapInterval(mAnw.get(), mPreviewSwapInterval);
        }
        if (mCrop.left >= 0) {
            native_window_set_crop(mAnw.get(), &(mCrop));
        }
    }

    auto rc = native_window_set_buffer_count(mAnw.get(), count);
    if (rc == ::android::OK) {
        cleanupCirculatingBuffers();
    }

    return mapToStatus(rc);
}

Return<Status> PreviewWindowCb::setBuffersGeometry(uint32_t w, uint32_t h,
        PixelFormat format) {
    auto rc = native_window_set_buffers_dimensions(mAnw.get(), w, h);
    if (rc == ::android::OK) {
        mPreviewWidth = w;
        mPreviewHeight = h;
        rc = native_window_set_buffers_format(mAnw.get(),
                static_cast<int>(format));
        if (rc == ::android::OK) {
            mFormat = static_cast<int>(format);
        }
    }

    return mapToStatus(rc);
}

Return<Status> PreviewWindowCb::setCrop(int32_t left, int32_t top,
        int32_t right, int32_t bottom) {
    android_native_rect_t crop = { left, top, right, bottom };
    auto rc = native_window_set_crop(mAnw.get(), &crop);
    if (rc == ::android::OK) {
        mCrop = crop;
    }
    return mapToStatus(rc);
}

Return<Status> PreviewWindowCb::setUsage(BufferUsage usage) {
    auto rc = native_window_set_usage(mAnw.get(), static_cast<int>(usage));
    if (rc == ::android::OK) {
        mPreviewUsage =  static_cast<int>(usage);
    }
    return mapToStatus(rc);
}

Return<Status> PreviewWindowCb::setSwapInterval(int32_t interval) {
    auto rc = mAnw->setSwapInterval(mAnw.get(), interval);
    if (rc == ::android::OK) {
        mPreviewSwapInterval = interval;
    }
    return mapToStatus(rc);
}

Return<void> PreviewWindowCb::getMinUndequeuedBufferCount(
        getMinUndequeuedBufferCount_cb _hidl_cb) {
    int count = 0;
    auto rc = mAnw->query(mAnw.get(),
            NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, &count);
    _hidl_cb(mapToStatus(rc), count);
    return Void();
}

Return<Status> PreviewWindowCb::setTimestamp(int64_t timestamp) {
    return mapToStatus(native_window_set_buffers_timestamp(mAnw.get(),
            timestamp));
}

// The main test class for camera HIDL HAL.
class CameraHidlTest : public ::testing::VtsHalHidlTargetTestBase {
public:
 virtual void SetUp() override {
     string service_name = CameraHidlEnvironment::Instance()->getServiceName<ICameraProvider>();
     ALOGI("get service with name: %s", service_name.c_str());
     mProvider = ::testing::VtsHalHidlTargetTestBase::getService<ICameraProvider>(service_name);
     ASSERT_NE(mProvider, nullptr);

     uint32_t id;
     ASSERT_TRUE(parseProviderName(service_name, &mProviderType, &id));

     castProvider(mProvider, &mProvider2_5);
     notifyDeviceState(provider::V2_5::DeviceState::NORMAL);
 }
 virtual void TearDown() override {}

 hidl_vec<hidl_string> getCameraDeviceNames(sp<ICameraProvider> provider);

    struct EmptyDeviceCb : public V3_5::ICameraDeviceCallback {
     virtual Return<void> processCaptureResult(
         const hidl_vec<CaptureResult>& /*results*/) override {
         ALOGI("processCaptureResult callback");
         ADD_FAILURE();  // Empty callback should not reach here
         return Void();
     }

     virtual Return<void> processCaptureResult_3_4(
         const hidl_vec<V3_4::CaptureResult>& /*results*/) override {
         ALOGI("processCaptureResult_3_4 callback");
         ADD_FAILURE();  // Empty callback should not reach here
         return Void();
     }

     virtual Return<void> notify(const hidl_vec<NotifyMsg>& /*msgs*/) override {
         ALOGI("notify callback");
         ADD_FAILURE();  // Empty callback should not reach here
         return Void();
     }

     virtual Return<void> requestStreamBuffers(
             const hidl_vec<V3_5::BufferRequest>&,
             requestStreamBuffers_cb _hidl_cb) override {
         ALOGI("requestStreamBuffers callback");
         // HAL might want to request buffer after configureStreams, but tests with EmptyDeviceCb
         // doesn't actually need to send capture requests, so just return an error.
         hidl_vec<V3_5::StreamBufferRet> emptyBufRets;
         _hidl_cb(V3_5::BufferRequestStatus::FAILED_UNKNOWN, emptyBufRets);
         return Void();
     }

     virtual Return<void> returnStreamBuffers(const hidl_vec<StreamBuffer>&) override {
         ALOGI("returnStreamBuffers");
         ADD_FAILURE();  // Empty callback should not reach here
         return Void();
     }

    };

    struct DeviceCb : public V3_5::ICameraDeviceCallback {
        DeviceCb(CameraHidlTest *parent, int deviceVersion, const camera_metadata_t *staticMeta) :
                mParent(parent), mDeviceVersion(deviceVersion) {
            mStaticMetadata = staticMeta;
        }

        Return<void> processCaptureResult_3_4(
                const hidl_vec<V3_4::CaptureResult>& results) override;
        Return<void> processCaptureResult(const hidl_vec<CaptureResult>& results) override;
        Return<void> notify(const hidl_vec<NotifyMsg>& msgs) override;

        Return<void> requestStreamBuffers(
                const hidl_vec<V3_5::BufferRequest>& bufReqs,
                requestStreamBuffers_cb _hidl_cb) override;

        Return<void> returnStreamBuffers(const hidl_vec<StreamBuffer>& buffers) override;

        void setCurrentStreamConfig(const hidl_vec<V3_2::Stream>& streams,
                const hidl_vec<V3_2::HalStream>& halStreams);

        void waitForBuffersReturned();

     private:
        bool processCaptureResultLocked(const CaptureResult& results,
                hidl_vec<PhysicalCameraMetadata> physicalCameraMetadata);

        CameraHidlTest *mParent; // Parent object
        int mDeviceVersion;
        android::hardware::camera::common::V1_0::helper::CameraMetadata mStaticMetadata;
        bool hasOutstandingBuffersLocked();

        /* members for requestStreamBuffers() and returnStreamBuffers()*/
        std::mutex mLock; // protecting members below
        bool                      mUseHalBufManager = false;
        hidl_vec<V3_2::Stream>    mStreams;
        hidl_vec<V3_2::HalStream> mHalStreams;
        uint64_t mNextBufferId = 1;
        using OutstandingBuffers = std::unordered_map<uint64_t, hidl_handle>;
        // size == mStreams.size(). Tracking each streams outstanding buffers
        std::vector<OutstandingBuffers> mOutstandingBufferIds;
        std::condition_variable mFlushedCondition;
    };

    struct TorchProviderCb : public ICameraProviderCallback {
        TorchProviderCb(CameraHidlTest *parent) : mParent(parent) {}
        virtual Return<void> cameraDeviceStatusChange(
                const hidl_string&, CameraDeviceStatus) override {
            return Void();
        }

        virtual Return<void> torchModeStatusChange(
                const hidl_string&, TorchModeStatus newStatus) override {
            std::lock_guard<std::mutex> l(mParent->mTorchLock);
            mParent->mTorchStatus = newStatus;
            mParent->mTorchCond.notify_one();
            return Void();
        }

     private:
        CameraHidlTest *mParent;               // Parent object
    };

    struct Camera1DeviceCb :
            public ::android::hardware::camera::device::V1_0::ICameraDeviceCallback {
        Camera1DeviceCb(CameraHidlTest *parent) : mParent(parent) {}

        Return<void> notifyCallback(NotifyCallbackMsg msgType,
                int32_t ext1, int32_t ext2) override;

        Return<uint32_t> registerMemory(const hidl_handle& descriptor,
                uint32_t bufferSize, uint32_t bufferCount) override;

        Return<void> unregisterMemory(uint32_t memId) override;

        Return<void> dataCallback(DataCallbackMsg msgType,
                uint32_t data, uint32_t bufferIndex,
                const CameraFrameMetadata& metadata) override;

        Return<void> dataCallbackTimestamp(DataCallbackMsg msgType,
                uint32_t data, uint32_t bufferIndex,
                int64_t timestamp) override;

        Return<void> handleCallbackTimestamp(DataCallbackMsg msgType,
                const hidl_handle& frameData,uint32_t data,
                uint32_t bufferIndex, int64_t timestamp) override;

        Return<void> handleCallbackTimestampBatch(DataCallbackMsg msgType,
                const ::android::hardware::hidl_vec<HandleTimestampMessage>& batch) override;


     private:
        CameraHidlTest *mParent;               // Parent object
    };

    void notifyDeviceState(::android::hardware::camera::provider::V2_5::DeviceState newState);

    void openCameraDevice(const std::string &name, sp<ICameraProvider> provider,
            sp<::android::hardware::camera::device::V1_0::ICameraDevice> *device /*out*/);
    void setupPreviewWindow(
            const sp<::android::hardware::camera::device::V1_0::ICameraDevice> &device,
            sp<BufferItemConsumer> *bufferItemConsumer /*out*/,
            sp<BufferItemHander> *bufferHandler /*out*/);
    void stopPreviewAndClose(
            const sp<::android::hardware::camera::device::V1_0::ICameraDevice> &device);
    void startPreview(
            const sp<::android::hardware::camera::device::V1_0::ICameraDevice> &device);
    void enableMsgType(unsigned int msgType,
            const sp<::android::hardware::camera::device::V1_0::ICameraDevice> &device);
    void disableMsgType(unsigned int msgType,
            const sp<::android::hardware::camera::device::V1_0::ICameraDevice> &device);
    void getParameters(
            const sp<::android::hardware::camera::device::V1_0::ICameraDevice> &device,
            CameraParameters *cameraParams /*out*/);
    void setParameters(
            const sp<::android::hardware::camera::device::V1_0::ICameraDevice> &device,
            const CameraParameters &cameraParams);
    void allocateGraphicBuffer(uint32_t width, uint32_t height, uint64_t usage,
            PixelFormat format, hidl_handle *buffer_handle /*out*/);
    void waitForFrameLocked(DataCallbackMsg msgFrame,
            std::unique_lock<std::mutex> &l);
    void openEmptyDeviceSession(const std::string &name,
            sp<ICameraProvider> provider,
            sp<ICameraDeviceSession> *session /*out*/,
            camera_metadata_t **staticMeta /*out*/,
            ::android::sp<ICameraDevice> *device = nullptr/*out*/);
    void castProvider(const sp<provider::V2_4::ICameraProvider> &provider,
            sp<provider::V2_5::ICameraProvider> *provider2_5 /*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*/);
    void castDevice(const sp<device::V3_2::ICameraDevice> &device, int32_t deviceVersion,
            sp<device::V3_5::ICameraDevice> *device3_5/*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,
            uint32_t jpegBufferSize = 0);

    void configurePreviewStreams3_4(const std::string &name, int32_t deviceVersion,
            sp<ICameraProvider> provider,
            const AvailableStream *previewThreshold,
            const std::unordered_set<std::string>& physicalIds,
            sp<device::V3_4::ICameraDeviceSession> *session3_4 /*out*/,
            sp<device::V3_5::ICameraDeviceSession> *session3_5 /*out*/,
            V3_2::Stream* previewStream /*out*/,
            device::V3_4::HalStreamConfiguration *halStreamConfig /*out*/,
            bool *supportsPartialResults /*out*/,
            uint32_t *partialResultCount /*out*/,
            bool *useHalBufManager /*out*/,
            sp<DeviceCb> *cb /*out*/,
            uint32_t streamConfigCounter = 0,
            bool allowUnsupport = false);
    void configurePreviewStream(const std::string &name, int32_t deviceVersion,
            sp<ICameraProvider> provider,
            const AvailableStream *previewThreshold,
            sp<ICameraDeviceSession> *session /*out*/,
            V3_2::Stream *previewStream /*out*/,
            HalStreamConfiguration *halStreamConfig /*out*/,
            bool *supportsPartialResults /*out*/,
            uint32_t *partialResultCount /*out*/,
            bool *useHalBufManager /*out*/,
            sp<DeviceCb> *cb /*out*/,
            uint32_t streamConfigCounter = 0);

    void verifyLogicalCameraMetadata(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);
    void verifyCameraCharacteristics(Status status, const CameraMetadata& chars);
    void verifyRecommendedConfigs(const CameraMetadata& metadata);
    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,
            bool expectedStatus, bool expectStreamCombQuery);
    void verifyLogicalCameraResult(const camera_metadata_t* staticMetadata,
            const ::android::hardware::camera::common::V1_0::helper::CameraMetadata& resultMetadata);

    void verifyBuffersReturned(sp<device::V3_2::ICameraDeviceSession> session,
            int deviceVerison, int32_t streamId, sp<DeviceCb> cb,
            uint32_t streamConfigCounter = 0);

    void verifyBuffersReturned(sp<device::V3_4::ICameraDeviceSession> session,
            hidl_vec<int32_t> streamIds, sp<DeviceCb> cb,
            uint32_t streamConfigCounter = 0);

    void verifySessionReconfigurationQuery(sp<device::V3_5::ICameraDeviceSession> session3_5,
            camera_metadata* oldSessionParams, camera_metadata* newSessionParams);

    bool isDepthOnly(camera_metadata_t* staticMeta);

    static Status getAvailableOutputStreams(camera_metadata_t *staticMeta,
            std::vector<AvailableStream> &outputStreams,
            const AvailableStream *threshold = nullptr);
    static Status getJpegBufferSize(camera_metadata_t *staticMeta,
            uint32_t* outBufSize);
    static Status isConstrainedModeAvailable(camera_metadata_t *staticMeta);
    static Status isLogicalMultiCamera(const camera_metadata_t *staticMeta);
    static Status getPhysicalCameraIds(const camera_metadata_t *staticMeta,
            std::unordered_set<std::string> *physicalIds/*out*/);
    static Status getSupportedKeys(camera_metadata_t *staticMeta,
            uint32_t tagId, std::unordered_set<int32_t> *requestIDs/*out*/);
    static void fillOutputStreams(camera_metadata_ro_entry_t* entry,
            std::vector<AvailableStream>& outputStreams,
            const AvailableStream *threshold = nullptr,
            const int32_t availableConfigOutputTag = 0u);
    static void constructFilteredSettings(const sp<ICameraDeviceSession>& session,
            const std::unordered_set<int32_t>& availableKeys, RequestTemplate reqTemplate,
            android::hardware::camera::common::V1_0::helper::CameraMetadata* defaultSettings/*out*/,
            android::hardware::camera::common::V1_0::helper::CameraMetadata* filteredSettings
            /*out*/);
    static Status pickConstrainedModeSize(camera_metadata_t *staticMeta,
            AvailableStream &hfrStream);
    static Status isZSLModeAvailable(const camera_metadata_t *staticMeta);
    static Status isZSLModeAvailable(const camera_metadata_t *staticMeta, ReprocessType reprocType);
    static Status getZSLInputOutputMap(camera_metadata_t *staticMeta,
            std::vector<AvailableZSLInputOutput> &inputOutputMap);
    static Status findLargestSize(
            const std::vector<AvailableStream> &streamSizes,
            int32_t format, AvailableStream &result);
    static Status isAutoFocusModeAvailable(
            CameraParameters &cameraParams, const char *mode) ;
    static Status isMonochromeCamera(const camera_metadata_t *staticMeta);

protected:

    // In-flight queue for tracking completion of capture requests.
    struct InFlightRequest {
        // Set by notify() SHUTTER call.
        nsecs_t shutterTimestamp;

        bool errorCodeValid;
        ErrorCode errorCode;

        //Is partial result supported
        bool usePartialResult;

        //Partial result count expected
        uint32_t numPartialResults;

        // Message queue
        std::shared_ptr<ResultMetadataQueue> resultQueue;

        // Set by process_capture_result call with valid metadata
        bool haveResultMetadata;

        // Decremented by calls to process_capture_result with valid output
        // and input buffers
        ssize_t numBuffersLeft;

         // A 64bit integer to index the frame number associated with this result.
        int64_t frameNumber;

         // The partial result count (index) for this capture result.
        int32_t partialResultCount;

        // For buffer drop errors, the stream ID for the stream that lost a buffer.
        // Otherwise -1.
        int32_t errorStreamId;

        // If this request has any input buffer
        bool hasInputBuffer;

        // Result metadata
        ::android::hardware::camera::common::V1_0::helper::CameraMetadata collectedResult;

        // Buffers are added by process_capture_result when output buffers
        // return from HAL but framework.
        ::android::Vector<StreamBuffer> resultOutputBuffers;

        InFlightRequest() :
                shutterTimestamp(0),
                errorCodeValid(false),
                errorCode(ErrorCode::ERROR_BUFFER),
                usePartialResult(false),
                numPartialResults(0),
                resultQueue(nullptr),
                haveResultMetadata(false),
                numBuffersLeft(0),
                frameNumber(0),
                partialResultCount(0),
                errorStreamId(-1),
                hasInputBuffer(false) {}

        InFlightRequest(ssize_t numBuffers, bool hasInput,
                bool partialResults, uint32_t partialCount,
                std::shared_ptr<ResultMetadataQueue> queue = nullptr) :
                shutterTimestamp(0),
                errorCodeValid(false),
                errorCode(ErrorCode::ERROR_BUFFER),
                usePartialResult(partialResults),
                numPartialResults(partialCount),
                resultQueue(queue),
                haveResultMetadata(false),
                numBuffersLeft(numBuffers),
                frameNumber(0),
                partialResultCount(0),
                errorStreamId(-1),
                hasInputBuffer(hasInput) {}
    };

    // Map from frame number to the in-flight request state
    typedef ::android::KeyedVector<uint32_t, InFlightRequest*> InFlightMap;

    std::mutex mLock;                          // Synchronize access to member variables
    std::condition_variable mResultCondition;  // Condition variable for incoming results
    InFlightMap mInflightMap;                  // Map of all inflight requests

    DataCallbackMsg mDataMessageTypeReceived;  // Most recent message type received through data callbacks
    uint32_t mVideoBufferIndex;                // Buffer index of the most recent video buffer
    uint32_t mVideoData;                       // Buffer data of the most recent video buffer
    hidl_handle mVideoNativeHandle;            // Most recent video buffer native handle
    NotifyCallbackMsg mNotifyMessage;          // Current notification message

    std::mutex mTorchLock;                     // Synchronize access to torch status
    std::condition_variable mTorchCond;        // Condition variable for torch status
    TorchModeStatus mTorchStatus;              // Current torch status

    // Holds camera registered buffers
    std::unordered_map<uint32_t, sp<::android::MemoryHeapBase> > mMemoryPool;

    // Camera provider service
    sp<ICameraProvider> mProvider;
    sp<::android::hardware::camera::provider::V2_5::ICameraProvider> mProvider2_5;

    // Camera provider type.
    std::string mProviderType;
};

Return<void> CameraHidlTest::Camera1DeviceCb::notifyCallback(
        NotifyCallbackMsg msgType, int32_t ext1 __unused,
        int32_t ext2 __unused) {
    std::unique_lock<std::mutex> l(mParent->mLock);
    mParent->mNotifyMessage = msgType;
    mParent->mResultCondition.notify_one();

    return Void();
}

Return<uint32_t> CameraHidlTest::Camera1DeviceCb::registerMemory(
        const hidl_handle& descriptor, uint32_t bufferSize,
        uint32_t bufferCount) {
    if (descriptor->numFds != 1) {
        ADD_FAILURE() << "camera memory descriptor has"
                " numFds " <<  descriptor->numFds << " (expect 1)" ;
        return 0;
    }
    if (descriptor->data[0] < 0) {
        ADD_FAILURE() << "camera memory descriptor has"
                " FD " << descriptor->data[0] << " (expect >= 0)";
        return 0;
    }

    sp<::android::MemoryHeapBase> pool = new ::android::MemoryHeapBase(
            descriptor->data[0], bufferSize*bufferCount, 0, 0);
    mParent->mMemoryPool.emplace(pool->getHeapID(), pool);

    return pool->getHeapID();
}

Return<void> CameraHidlTest::Camera1DeviceCb::unregisterMemory(uint32_t memId) {
    if (mParent->mMemoryPool.count(memId) == 0) {
        ALOGE("%s: memory pool ID %d not found", __FUNCTION__, memId);
        ADD_FAILURE();
        return Void();
    }

    mParent->mMemoryPool.erase(memId);
    return Void();
}

Return<void> CameraHidlTest::Camera1DeviceCb::dataCallback(
        DataCallbackMsg msgType __unused, uint32_t data __unused,
        uint32_t bufferIndex __unused,
        const CameraFrameMetadata& metadata __unused) {
    std::unique_lock<std::mutex> l(mParent->mLock);
    mParent->mDataMessageTypeReceived = msgType;
    mParent->mResultCondition.notify_one();

    return Void();
}

Return<void> CameraHidlTest::Camera1DeviceCb::dataCallbackTimestamp(
        DataCallbackMsg msgType, uint32_t data,
        uint32_t bufferIndex, int64_t timestamp __unused) {
    std::unique_lock<std::mutex> l(mParent->mLock);
    mParent->mDataMessageTypeReceived = msgType;
    mParent->mVideoBufferIndex = bufferIndex;
    if (mParent->mMemoryPool.count(data) == 0) {
        ADD_FAILURE() << "memory pool ID " << data << "not found";
    }
    mParent->mVideoData = data;
    mParent->mResultCondition.notify_one();

    return Void();
}

Return<void> CameraHidlTest::Camera1DeviceCb::handleCallbackTimestamp(
        DataCallbackMsg msgType, const hidl_handle& frameData,
        uint32_t data __unused, uint32_t bufferIndex,
        int64_t timestamp __unused) {
    std::unique_lock<std::mutex> l(mParent->mLock);
    mParent->mDataMessageTypeReceived = msgType;
    mParent->mVideoBufferIndex = bufferIndex;
    if (mParent->mMemoryPool.count(data) == 0) {
        ADD_FAILURE() << "memory pool ID " << data << " not found";
    }
    mParent->mVideoData = data;
    mParent->mVideoNativeHandle = frameData;
    mParent->mResultCondition.notify_one();

    return Void();
}

Return<void> CameraHidlTest::Camera1DeviceCb::handleCallbackTimestampBatch(
        DataCallbackMsg msgType,
        const hidl_vec<HandleTimestampMessage>& batch) {
    std::unique_lock<std::mutex> l(mParent->mLock);
    for (auto& msg : batch) {
        mParent->mDataMessageTypeReceived = msgType;
        mParent->mVideoBufferIndex = msg.bufferIndex;
        if (mParent->mMemoryPool.count(msg.data) == 0) {
            ADD_FAILURE() << "memory pool ID " << msg.data << " not found";
        }
        mParent->mVideoData = msg.data;
        mParent->mVideoNativeHandle = msg.frameData;
        mParent->mResultCondition.notify_one();
    }
    return Void();
}

Return<void> CameraHidlTest::DeviceCb::processCaptureResult_3_4(
        const hidl_vec<V3_4::CaptureResult>& results) {

    if (nullptr == mParent) {
        return Void();
    }

    bool notify = false;
    std::unique_lock<std::mutex> l(mParent->mLock);
    for (size_t i = 0 ; i < results.size(); i++) {
        notify = processCaptureResultLocked(results[i].v3_2, results[i].physicalCameraMetadata);
    }

    l.unlock();
    if (notify) {
        mParent->mResultCondition.notify_one();
    }

    return Void();
}

Return<void> CameraHidlTest::DeviceCb::processCaptureResult(
        const hidl_vec<CaptureResult>& results) {
    if (nullptr == mParent) {
        return Void();
    }

    bool notify = false;
    std::unique_lock<std::mutex> l(mParent->mLock);
    ::android::hardware::hidl_vec<PhysicalCameraMetadata> noPhysMetadata;
    for (size_t i = 0 ; i < results.size(); i++) {
        notify = processCaptureResultLocked(results[i], noPhysMetadata);
    }

    l.unlock();
    if (notify) {
        mParent->mResultCondition.notify_one();
    }

    return Void();
}

bool CameraHidlTest::DeviceCb::processCaptureResultLocked(const CaptureResult& results,
        hidl_vec<PhysicalCameraMetadata> physicalCameraMetadata) {
    bool notify = false;
    uint32_t frameNumber = results.frameNumber;

    if ((results.result.size() == 0) &&
            (results.outputBuffers.size() == 0) &&
            (results.inputBuffer.buffer == nullptr) &&
            (results.fmqResultSize == 0)) {
        ALOGE("%s: No result data provided by HAL for frame %d result count: %d",
                __func__, frameNumber, (int) results.fmqResultSize);
        ADD_FAILURE();
        return notify;
    }

    ssize_t idx = mParent->mInflightMap.indexOfKey(frameNumber);
    if (::android::NAME_NOT_FOUND == idx) {
        ALOGE("%s: Unexpected frame number! received: %u",
                __func__, frameNumber);
        ADD_FAILURE();
        return notify;
    }

    bool isPartialResult = false;
    bool hasInputBufferInRequest = false;
    InFlightRequest *request = mParent->mInflightMap.editValueAt(idx);
    ::android::hardware::camera::device::V3_2::CameraMetadata resultMetadata;
    size_t resultSize = 0;
    if (results.fmqResultSize > 0) {
        resultMetadata.resize(results.fmqResultSize);
        if (request->resultQueue == nullptr) {
            ADD_FAILURE();
            return notify;
        }
        if (!request->resultQueue->read(resultMetadata.data(),
                    results.fmqResultSize)) {
            ALOGE("%s: Frame %d: Cannot read camera metadata from fmq,"
                    "size = %" PRIu64, __func__, frameNumber,
                    results.fmqResultSize);
            ADD_FAILURE();
            return notify;
        }

        std::vector<::android::hardware::camera::device::V3_2::CameraMetadata> physResultMetadata;
        physResultMetadata.resize(physicalCameraMetadata.size());
        for (size_t i = 0; i < physicalCameraMetadata.size(); i++) {
            physResultMetadata[i].resize(physicalCameraMetadata[i].fmqMetadataSize);
            if (!request->resultQueue->read(physResultMetadata[i].data(),
                    physicalCameraMetadata[i].fmqMetadataSize)) {
                ALOGE("%s: Frame %d: Cannot read physical camera metadata from fmq,"
                        "size = %" PRIu64, __func__, frameNumber,
                        physicalCameraMetadata[i].fmqMetadataSize);
                ADD_FAILURE();
                return notify;
            }
        }
        resultSize = resultMetadata.size();
    } else if (results.result.size() > 0) {
        resultMetadata.setToExternal(const_cast<uint8_t *>(
                    results.result.data()), results.result.size());
        resultSize = resultMetadata.size();
    }

    if (!request->usePartialResult && (resultSize > 0) &&
            (results.partialResult != 1)) {
        ALOGE("%s: Result is malformed for frame %d: partial_result %u "
                "must be 1  if partial result is not supported", __func__,
                frameNumber, results.partialResult);
        ADD_FAILURE();
        return notify;
    }

    if (results.partialResult != 0) {
        request->partialResultCount = results.partialResult;
    }

    // Check if this result carries only partial metadata
    if (request->usePartialResult && (resultSize > 0)) {
        if ((results.partialResult > request->numPartialResults) ||
                (results.partialResult < 1)) {
            ALOGE("%s: Result is malformed for frame %d: partial_result %u"
                    " must be  in the range of [1, %d] when metadata is "
                    "included in the result", __func__, frameNumber,
                    results.partialResult, request->numPartialResults);
            ADD_FAILURE();
            return notify;
        }
        request->collectedResult.append(
                reinterpret_cast<const camera_metadata_t*>(
                    resultMetadata.data()));

        isPartialResult =
            (results.partialResult < request->numPartialResults);
    } else if (resultSize > 0) {
        request->collectedResult.append(reinterpret_cast<const camera_metadata_t*>(
                    resultMetadata.data()));
        isPartialResult = false;
    }

    hasInputBufferInRequest = request->hasInputBuffer;

    // Did we get the (final) result metadata for this capture?
    if ((resultSize > 0) && !isPartialResult) {
        if (request->haveResultMetadata) {
            ALOGE("%s: Called multiple times with metadata for frame %d",
                    __func__, frameNumber);
            ADD_FAILURE();
            return notify;
        }
        request->haveResultMetadata = true;
        request->collectedResult.sort();

        // Verify final result metadata
        bool isAtLeast_3_5 = mDeviceVersion >= CAMERA_DEVICE_API_VERSION_3_5;
        if (isAtLeast_3_5) {
            auto staticMetadataBuffer = mStaticMetadata.getAndLock();
            bool isMonochrome = Status::OK ==
                    CameraHidlTest::isMonochromeCamera(staticMetadataBuffer);
            if (isMonochrome) {
                mParent->verifyMonochromeCameraResult(request->collectedResult);
            }

            // Verify logical camera result metadata
            bool isLogicalCamera =
                    Status::OK == CameraHidlTest::isLogicalMultiCamera(staticMetadataBuffer);
            if (isLogicalCamera) {
                mParent->verifyLogicalCameraResult(staticMetadataBuffer, request->collectedResult);
            }
            mStaticMetadata.unlock(staticMetadataBuffer);
        }
    }

    uint32_t numBuffersReturned = results.outputBuffers.size();
    if (results.inputBuffer.buffer != nullptr) {
        if (hasInputBufferInRequest) {
            numBuffersReturned += 1;
        } else {
            ALOGW("%s: Input buffer should be NULL if there is no input"
                    " buffer sent in the request", __func__);
        }
    }
    request->numBuffersLeft -= numBuffersReturned;
    if (request->numBuffersLeft < 0) {
        ALOGE("%s: Too many buffers returned for frame %d", __func__,
                frameNumber);
        ADD_FAILURE();
        return notify;
    }

    request->resultOutputBuffers.appendArray(results.outputBuffers.data(),
            results.outputBuffers.size());
    // If shutter event is received notify the pending threads.
    if (request->shutterTimestamp != 0) {
        notify = true;
    }

    if (mUseHalBufManager) {
        // Don't return buffers of bufId 0 (empty buffer)
        std::vector<StreamBuffer> buffers;
        for (const auto& sb : results.outputBuffers) {
            if (sb.bufferId != 0) {
                buffers.push_back(sb);
            }
        }
        returnStreamBuffers(buffers);
    }
    return notify;
}

void CameraHidlTest::DeviceCb::setCurrentStreamConfig(
        const hidl_vec<V3_2::Stream>& streams, const hidl_vec<V3_2::HalStream>& halStreams) {
    ASSERT_EQ(streams.size(), halStreams.size());
    ASSERT_NE(streams.size(), 0);
    for (size_t i = 0; i < streams.size(); i++) {
        ASSERT_EQ(streams[i].id, halStreams[i].id);
    }
    std::lock_guard<std::mutex> l(mLock);
    mUseHalBufManager = true;
    mStreams = streams;
    mHalStreams = halStreams;
    mOutstandingBufferIds.clear();
    for (size_t i = 0; i < streams.size(); i++) {
        mOutstandingBufferIds.emplace_back();
    }
}

bool CameraHidlTest::DeviceCb::hasOutstandingBuffersLocked() {
    if (!mUseHalBufManager) {
        return false;
    }
    for (const auto& outstandingBuffers : mOutstandingBufferIds) {
        if (!outstandingBuffers.empty()) {
            return true;
        }
    }
    return false;
}

void CameraHidlTest::DeviceCb::waitForBuffersReturned() {
    std::unique_lock<std::mutex> lk(mLock);
    if (hasOutstandingBuffersLocked()) {
        auto timeout = std::chrono::seconds(kBufferReturnTimeoutSec);
        auto st = mFlushedCondition.wait_for(lk, timeout);
        ASSERT_NE(std::cv_status::timeout, st);
    }
}

Return<void> CameraHidlTest::DeviceCb::notify(
        const hidl_vec<NotifyMsg>& messages) {
    std::lock_guard<std::mutex> l(mParent->mLock);

    for (size_t i = 0; i < messages.size(); i++) {
        ssize_t idx = mParent->mInflightMap.indexOfKey(
                messages[i].msg.shutter.frameNumber);
        if (::android::NAME_NOT_FOUND == idx) {
            ALOGE("%s: Unexpected frame number! received: %u",
                  __func__, messages[i].msg.shutter.frameNumber);
            ADD_FAILURE();
            break;
        }
        InFlightRequest *r = mParent->mInflightMap.editValueAt(idx);

        switch(messages[i].type) {
            case MsgType::ERROR:
                if (ErrorCode::ERROR_DEVICE == messages[i].msg.error.errorCode) {
                    ALOGE("%s: Camera reported serious device error",
                          __func__);
                    ADD_FAILURE();
                } else {
                    r->errorCodeValid = true;
                    r->errorCode = messages[i].msg.error.errorCode;
                    r->errorStreamId = messages[i].msg.error.errorStreamId;
                }
                break;
            case MsgType::SHUTTER:
                r->shutterTimestamp = messages[i].msg.shutter.timestamp;
                break;
            default:
                ALOGE("%s: Unsupported notify message %d", __func__,
                      messages[i].type);
                ADD_FAILURE();
                break;
        }
    }

    mParent->mResultCondition.notify_one();
    return Void();
}

Return<void> CameraHidlTest::DeviceCb::requestStreamBuffers(
        const hidl_vec<V3_5::BufferRequest>& bufReqs,
        requestStreamBuffers_cb _hidl_cb) {
    using V3_5::BufferRequestStatus;
    using V3_5::StreamBufferRet;
    using V3_5::StreamBufferRequestError;
    hidl_vec<StreamBufferRet> bufRets;
    std::unique_lock<std::mutex> l(mLock);

    if (!mUseHalBufManager) {
        ALOGE("%s: Camera does not support HAL buffer management", __FUNCTION__);
        ADD_FAILURE();
        _hidl_cb(BufferRequestStatus::FAILED_ILLEGAL_ARGUMENTS, bufRets);
        return Void();
    }

    if (bufReqs.size() > mStreams.size()) {
        ALOGE("%s: illegal buffer request: too many requests!", __FUNCTION__);
        ADD_FAILURE();
        _hidl_cb(BufferRequestStatus::FAILED_ILLEGAL_ARGUMENTS, bufRets);
        return Void();
    }

    std::vector<int32_t> indexes(bufReqs.size());
    for (size_t i = 0; i < bufReqs.size(); i++) {
        bool found = false;
        for (size_t idx = 0; idx < mStreams.size(); idx++) {
            if (bufReqs[i].streamId == mStreams[idx].id) {
                found = true;
                indexes[i] = idx;
                break;
            }
        }
        if (!found) {
            ALOGE("%s: illegal buffer request: unknown streamId %d!",
                    __FUNCTION__, bufReqs[i].streamId);
            ADD_FAILURE();
            _hidl_cb(BufferRequestStatus::FAILED_ILLEGAL_ARGUMENTS, bufRets);
            return Void();
        }
    }

    bool allStreamOk = true;
    bool atLeastOneStreamOk = false;
    bufRets.resize(bufReqs.size());
    for (size_t i = 0; i < bufReqs.size(); i++) {
        int32_t idx = indexes[i];
        const auto& stream = mStreams[idx];
        const auto& halStream = mHalStreams[idx];
        const V3_5::BufferRequest& bufReq = bufReqs[i];
        if (mOutstandingBufferIds[idx].size() + bufReq.numBuffersRequested > halStream.maxBuffers) {
            bufRets[i].streamId = stream.id;
            bufRets[i].val.error(StreamBufferRequestError::MAX_BUFFER_EXCEEDED);
            allStreamOk = false;
            continue;
        }

        hidl_vec<StreamBuffer> tmpRetBuffers(bufReq.numBuffersRequested);
        for (size_t j = 0; j < bufReq.numBuffersRequested; j++) {
            hidl_handle buffer_handle;
            mParent->allocateGraphicBuffer(stream.width, stream.height,
                    android_convertGralloc1To0Usage(
                            halStream.producerUsage, halStream.consumerUsage),
                    halStream.overrideFormat, &buffer_handle);

            tmpRetBuffers[j] = {stream.id, mNextBufferId, buffer_handle, BufferStatus::OK,
                                nullptr, nullptr};
            mOutstandingBufferIds[idx].insert(std::make_pair(mNextBufferId++, buffer_handle));
        }
        atLeastOneStreamOk = true;
        bufRets[i].streamId = stream.id;
        bufRets[i].val.buffers(std::move(tmpRetBuffers));
    }

    if (allStreamOk) {
        _hidl_cb(BufferRequestStatus::OK, bufRets);
    } else if (atLeastOneStreamOk) {
        _hidl_cb(BufferRequestStatus::FAILED_PARTIAL, bufRets);
    } else {
        _hidl_cb(BufferRequestStatus::FAILED_UNKNOWN, bufRets);
    }

    if (!hasOutstandingBuffersLocked()) {
        l.unlock();
        mFlushedCondition.notify_one();
    }
    return Void();
}

Return<void> CameraHidlTest::DeviceCb::returnStreamBuffers(
        const hidl_vec<StreamBuffer>& buffers) {
    if (!mUseHalBufManager) {
        ALOGE("%s: Camera does not support HAL buffer management", __FUNCTION__);
        ADD_FAILURE();
    }

    std::lock_guard<std::mutex> l(mLock);
    for (const auto& buf : buffers) {
        bool found = false;
        for (size_t idx = 0; idx < mOutstandingBufferIds.size(); idx++) {
            if (mStreams[idx].id == buf.streamId &&
                    mOutstandingBufferIds[idx].count(buf.bufferId) == 1) {
                mOutstandingBufferIds[idx].erase(buf.bufferId);
                // TODO: check do we need to close/delete native handle or assume we have enough
                // memory to run till the test finish? since we do not capture much requests (and
                // most of time one buffer is sufficient)
                found = true;
                break;
            }
        }
        if (found) {
            continue;
        }
        ALOGE("%s: unknown buffer ID %" PRIu64, __FUNCTION__, buf.bufferId);
        ADD_FAILURE();
    }
    return Void();
}

hidl_vec<hidl_string> CameraHidlTest::getCameraDeviceNames(sp<ICameraProvider> provider) {
    std::vector<std::string> cameraDeviceNames;
    Return<void> ret;
    ret = provider->getCameraIdList(
        [&](auto status, const auto& idList) {
            ALOGI("getCameraIdList returns status:%d", (int)status);
            for (size_t i = 0; i < idList.size(); i++) {
                ALOGI("Camera Id[%zu] is %s", i, idList[i].c_str());
            }
            ASSERT_EQ(Status::OK, status);
            for (const auto& id : idList) {
                cameraDeviceNames.push_back(id);
            }
        });
    if (!ret.isOk()) {
        ADD_FAILURE();
    }

    // External camera devices are reported through cameraDeviceStatusChange
    struct ProviderCb : public ICameraProviderCallback {
        virtual Return<void> cameraDeviceStatusChange(
                const hidl_string& devName,
                CameraDeviceStatus newStatus) override {
            ALOGI("camera device status callback name %s, status %d",
                    devName.c_str(), (int) newStatus);
            if (newStatus == CameraDeviceStatus::PRESENT) {
                externalCameraDeviceNames.push_back(devName);

            }
            return Void();
        }

        virtual Return<void> torchModeStatusChange(
                const hidl_string&, TorchModeStatus) override {
            return Void();
        }

        std::vector<std::string> externalCameraDeviceNames;
    };
    sp<ProviderCb> cb = new ProviderCb;
    auto status = mProvider->setCallback(cb);

    for (const auto& devName : cb->externalCameraDeviceNames) {
        if (cameraDeviceNames.end() == std::find(
                cameraDeviceNames.begin(), cameraDeviceNames.end(), devName)) {
            cameraDeviceNames.push_back(devName);
        }
    }

    hidl_vec<hidl_string> retList(cameraDeviceNames.size());
    for (size_t i = 0; i < cameraDeviceNames.size(); i++) {
        retList[i] = cameraDeviceNames[i];
    }
    return retList;
}

// Test devices with first_api_level >= P does not advertise device@1.0
TEST_F(CameraHidlTest, noHal1AfterP) {
    constexpr int32_t HAL1_PHASE_OUT_API_LEVEL = 28;
    int32_t firstApiLevel = 0;
    getFirstApiLevel(&firstApiLevel);

    // all devices with first API level == 28 and <= 1GB of RAM must set low_ram
    // and thus be allowed to continue using HAL1
    if ((firstApiLevel == HAL1_PHASE_OUT_API_LEVEL) &&
        (property_get_bool("ro.config.low_ram", /*default*/ false))) {
        ALOGI("Hal1 allowed for low ram device");
        return;
    }

    if (firstApiLevel >= HAL1_PHASE_OUT_API_LEVEL) {
        hidl_vec<hidl_string> cameraDeviceNames = getCameraDeviceNames(mProvider);
        for (const auto& name : cameraDeviceNames) {
            int deviceVersion = getCameraDeviceVersion(name, mProviderType);
            ASSERT_NE(deviceVersion, 0); // Must be a valid device version
            ASSERT_NE(deviceVersion, CAMERA_DEVICE_API_VERSION_1_0); // Must not be device@1.0
        }
    }
}

// Test if ICameraProvider::isTorchModeSupported returns Status::OK
// Also if first_api_level >= Q torch API must be supported.
TEST_F(CameraHidlTest, isTorchModeSupported) {
    constexpr int32_t API_LEVEL_Q = 29;
    int32_t firstApiLevel = 0;
    getFirstApiLevel(&firstApiLevel);

    Return<void> ret;
    ret = mProvider->isSetTorchModeSupported([&](auto status, bool support) {
        ALOGI("isSetTorchModeSupported returns status:%d supported:%d", (int)status, support);
        ASSERT_EQ(Status::OK, status);
        if (firstApiLevel >= API_LEVEL_Q) {
            ASSERT_EQ(true, support);
        }
    });
    ASSERT_TRUE(ret.isOk());
}

// TODO: consider removing this test if getCameraDeviceNames() has the same coverage
TEST_F(CameraHidlTest, getCameraIdList) {
    Return<void> ret;
    ret = mProvider->getCameraIdList([&](auto status, const auto& idList) {
        ALOGI("getCameraIdList returns status:%d", (int)status);
        for (size_t i = 0; i < idList.size(); i++) {
            ALOGI("Camera Id[%zu] is %s", i, idList[i].c_str());
        }
        ASSERT_EQ(Status::OK, status);
    });
    ASSERT_TRUE(ret.isOk());
}

// Test if ICameraProvider::getVendorTags returns Status::OK
TEST_F(CameraHidlTest, getVendorTags) {
    Return<void> ret;
    ret = mProvider->getVendorTags([&](auto status, const auto& vendorTagSecs) {
        ALOGI("getVendorTags returns status:%d numSections %zu", (int)status, vendorTagSecs.size());
        for (size_t i = 0; i < vendorTagSecs.size(); i++) {
            ALOGI("Vendor tag section %zu name %s", i, vendorTagSecs[i].sectionName.c_str());
            for (size_t j = 0; j < vendorTagSecs[i].tags.size(); j++) {
                const auto& tag = vendorTagSecs[i].tags[j];
                ALOGI("Vendor tag id %u name %s type %d", tag.tagId, tag.tagName.c_str(),
                      (int)tag.tagType);
            }
        }
        ASSERT_EQ(Status::OK, status);
    });
    ASSERT_TRUE(ret.isOk());
}

// Test if ICameraProvider::setCallback returns Status::OK
TEST_F(CameraHidlTest, setCallback) {
    struct ProviderCb : public ICameraProviderCallback {
        virtual Return<void> cameraDeviceStatusChange(
                const hidl_string& cameraDeviceName,
                CameraDeviceStatus newStatus) override {
            ALOGI("camera device status callback name %s, status %d",
                    cameraDeviceName.c_str(), (int) newStatus);
            return Void();
        }

        virtual Return<void> torchModeStatusChange(
                const hidl_string& cameraDeviceName,
                TorchModeStatus newStatus) override {
            ALOGI("Torch mode status callback name %s, status %d",
                    cameraDeviceName.c_str(), (int) newStatus);
            return Void();
        }
    };
    sp<ProviderCb> cb = new ProviderCb;
    auto status = mProvider->setCallback(cb);
    ASSERT_TRUE(status.isOk());
    ASSERT_EQ(Status::OK, status);
    status = mProvider->setCallback(nullptr);
    ASSERT_TRUE(status.isOk());
    ASSERT_EQ(Status::OK, status);
}

// Test if ICameraProvider::getCameraDeviceInterface returns Status::OK and non-null device
TEST_F(CameraHidlTest, getCameraDeviceInterface) {
    hidl_vec<hidl_string> cameraDeviceNames = getCameraDeviceNames(mProvider);

    for (const auto& name : cameraDeviceNames) {
        int deviceVersion = getCameraDeviceVersion(name, mProviderType);
        switch (deviceVersion) {
            case CAMERA_DEVICE_API_VERSION_3_5:
            case CAMERA_DEVICE_API_VERSION_3_4:
            case CAMERA_DEVICE_API_VERSION_3_3:
            case CAMERA_DEVICE_API_VERSION_3_2: {
                Return<void> ret;
                ret = mProvider->getCameraDeviceInterface_V3_x(
                    name, [&](auto status, const auto& device3_x) {
                        ALOGI("getCameraDeviceInterface_V3_x returns status:%d", (int)status);
                        ASSERT_EQ(Status::OK, status);
                        ASSERT_NE(device3_x, nullptr);
                    });
                ASSERT_TRUE(ret.isOk());
            }
            break;
            case CAMERA_DEVICE_API_VERSION_1_0: {
                Return<void> ret;
                ret = mProvider->getCameraDeviceInterface_V1_x(
                    name, [&](auto status, const auto& device1) {
                        ALOGI("getCameraDeviceInterface_V1_x returns status:%d", (int)status);
                        ASSERT_EQ(Status::OK, status);
                        ASSERT_NE(device1, nullptr);
                    });
                ASSERT_TRUE(ret.isOk());
            }
            break;
            default: {
                ALOGE("%s: Unsupported device version %d", __func__, deviceVersion);
                ADD_FAILURE();
            }
            break;
        }
    }
}

// Verify that the device resource cost can be retrieved and the values are
// sane.
TEST_F(CameraHidlTest, getResourceCost) {
    hidl_vec<hidl_string> cameraDeviceNames = getCameraDeviceNames(mProvider);

    for (const auto& name : cameraDeviceNames) {
        int deviceVersion = getCameraDeviceVersion(name, mProviderType);
        switch (deviceVersion) {
            case CAMERA_DEVICE_API_VERSION_3_5:
            case CAMERA_DEVICE_API_VERSION_3_4:
            case CAMERA_DEVICE_API_VERSION_3_3:
            case CAMERA_DEVICE_API_VERSION_3_2: {
                ::android::sp<::android::hardware::camera::device::V3_2::ICameraDevice> device3_x;
                ALOGI("getResourceCost: Testing camera device %s", name.c_str());
                Return<void> ret;
                ret = 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_x = device;
                    });
                ASSERT_TRUE(ret.isOk());

                ret = device3_x->getResourceCost([&](auto status, const auto& resourceCost) {
                    ALOGI("getResourceCost returns status:%d", (int)status);
                    ASSERT_EQ(Status::OK, status);
                    ALOGI("    Resource cost is %d", resourceCost.resourceCost);
                    ASSERT_LE(resourceCost.resourceCost, 100u);
                    for (const auto& name : resourceCost.conflictingDevices) {
                        ALOGI("    Conflicting device: %s", name.c_str());
                    }
                });
                ASSERT_TRUE(ret.isOk());
            }
            break;
            case CAMERA_DEVICE_API_VERSION_1_0: {
                ::android::sp<::android::hardware::camera::device::V1_0::ICameraDevice> device1;
                ALOGI("getResourceCost: Testing camera device %s", name.c_str());
                Return<void> ret;
                ret = mProvider->getCameraDeviceInterface_V1_x(
                    name, [&](auto status, const auto& device) {
                        ALOGI("getCameraDeviceInterface_V1_x returns status:%d", (int)status);
                        ASSERT_EQ(Status::OK, status);
                        ASSERT_NE(device, nullptr);
                        device1 = device;
                    });
                ASSERT_TRUE(ret.isOk());

                ret = device1->getResourceCost([&](auto status, const auto& resourceCost) {
                    ALOGI("getResourceCost returns status:%d", (int)status);
                    ASSERT_EQ(Status::OK, status);
                    ALOGI("    Resource cost is %d", resourceCost.resourceCost);
                    ASSERT_LE(resourceCost.resourceCost, 100u);
                    for (const auto& name : resourceCost.conflictingDevices) {
                        ALOGI("    Conflicting device: %s", name.c_str());
                    }
                });
                ASSERT_TRUE(ret.isOk());
            }
            break;
            default: {
                ALOGE("%s: Unsupported device version %d", __func__, deviceVersion);
                ADD_FAILURE();
            }
            break;
        }
    }
}

// Verify that the static camera info can be retrieved
// successfully.
TEST_F(CameraHidlTest, getCameraInfo) {
    hidl_vec<hidl_string> cameraDeviceNames = getCameraDeviceNames(mProvider);

    for (const auto& name : cameraDeviceNames) {
        if (getCameraDeviceVersion(name, mProviderType) == CAMERA_DEVICE_API_VERSION_1_0) {
            ::android::sp<::android::hardware::camera::device::V1_0::ICameraDevice> device1;
            ALOGI("getCameraCharacteristics: Testing camera device %s", name.c_str());
            Return<void> ret;
            ret = mProvider->getCameraDeviceInterface_V1_x(
                name, [&](auto status, const auto& device) {
                    ALOGI("getCameraDeviceInterface_V1_x returns status:%d", (int)status);
                    ASSERT_EQ(Status::OK, status);
                    ASSERT_NE(device, nullptr);
                    device1 = device;
                });
            ASSERT_TRUE(ret.isOk());

            ret = device1->getCameraInfo([&](auto status, const auto& info) {
                ALOGI("getCameraInfo returns status:%d", (int)status);
                ASSERT_EQ(Status::OK, status);
                switch (info.orientation) {
                    case 0:
                    case 90:
                    case 180:
                    case 270:
                        // Expected cases
                        ALOGI("camera orientation: %d", info.orientation);
                        break;
                    default:
                        FAIL() << "Unexpected camera orientation:" << info.orientation;
                }
                switch (info.facing) {
                    case CameraFacing::BACK:
                    case CameraFacing::FRONT:
                    case CameraFacing::EXTERNAL:
                        // Expected cases
                        ALOGI("camera facing: %d", info.facing);
                        break;
                    default:
                        FAIL() << "Unexpected camera facing:" << static_cast<uint32_t>(info.facing);
                }
            });
            ASSERT_TRUE(ret.isOk());
        }
    }
}

// Check whether preview window can be configured
TEST_F(CameraHidlTest, setPreviewWindow) {
    hidl_vec<hidl_string> cameraDeviceNames = getCameraDeviceNames(mProvider);

    for (const auto& name : cameraDeviceNames) {
        if (getCameraDeviceVersion(name, mProviderType) == CAMERA_DEVICE_API_VERSION_1_0) {
            sp<::android::hardware::camera::device::V1_0::ICameraDevice> device1;
            openCameraDevice(name, mProvider, &device1 /*out*/);
            ASSERT_NE(nullptr, device1.get());
            sp<BufferItemConsumer> bufferItemConsumer;
            sp<BufferItemHander> bufferHandler;
            setupPreviewWindow(device1, &bufferItemConsumer /*out*/, &bufferHandler /*out*/);

            Return<void> ret;
            ret = device1->close();
            ASSERT_TRUE(ret.isOk());
        }
    }
}

// Verify that setting preview window fails in case device is not open
TEST_F(CameraHidlTest, setPreviewWindowInvalid) {
    hidl_vec<hidl_string> cameraDeviceNames = getCameraDeviceNames(mProvider);

    for (const auto& name : cameraDeviceNames) {
        if (getCameraDeviceVersion(name, mProviderType) == CAMERA_DEVICE_API_VERSION_1_0) {
            ::android::sp<::android::hardware::camera::device::V1_0::ICameraDevice> device1;
            ALOGI("getCameraCharacteristics: Testing camera device %s", name.c_str());
            Return<void> ret;
            ret = mProvider->getCameraDeviceInterface_V1_x(
                name, [&](auto status, const auto& device) {
                    ALOGI("getCameraDeviceInterface_V1_x returns status:%d", (int)status);
                    ASSERT_EQ(Status::OK, status);
                    ASSERT_NE(device, nullptr);
                    device1 = device;
                });
            ASSERT_TRUE(ret.isOk());

            Return<Status> returnStatus = device1->setPreviewWindow(nullptr);
            ASSERT_TRUE(returnStatus.isOk());
            ASSERT_EQ(Status::OPERATION_NOT_SUPPORTED, returnStatus);
        }
    }
}

// Start and stop preview checking whether it gets enabled in between.
TEST_F(CameraHidlTest, startStopPreview) {
    hidl_vec<hidl_string> cameraDeviceNames = getCameraDeviceNames(mProvider);

    for (const auto& name : cameraDeviceNames) {
        if (getCameraDeviceVersion(name, mProviderType) == CAMERA_DEVICE_API_VERSION_1_0) {
            sp<::android::hardware::camera::device::V1_0::ICameraDevice> device1;
            openCameraDevice(name, mProvider, &device1 /*out*/);
            ASSERT_NE(nullptr, device1.get());
            sp<BufferItemConsumer> bufferItemConsumer;
            sp<BufferItemHander> bufferHandler;
            setupPreviewWindow(device1, &bufferItemConsumer /*out*/, &bufferHandler /*out*/);

            startPreview(device1);

            Return<bool> returnBoolStatus = device1->previewEnabled();
            ASSERT_TRUE(returnBoolStatus.isOk());
            ASSERT_TRUE(returnBoolStatus);

            stopPreviewAndClose(device1);
        }
    }
}

// Start preview without active preview window. Preview should start as soon
// as a valid active window gets configured.
TEST_F(CameraHidlTest, startStopPreviewDelayed) {
    hidl_vec<hidl_string> cameraDeviceNames = getCameraDeviceNames(mProvider);

    for (const auto& name : cameraDeviceNames) {
        if (getCameraDeviceVersion(name, mProviderType) == CAMERA_DEVICE_API_VERSION_1_0) {
            sp<::android::hardware::camera::device::V1_0::ICameraDevice> device1;
            openCameraDevice(name, mProvider, &device1 /*out*/);
            ASSERT_NE(nullptr, device1.get());

            Return<Status> returnStatus = device1->setPreviewWindow(nullptr);
            ASSERT_TRUE(returnStatus.isOk());
            ASSERT_EQ(Status::OK, returnStatus);

            startPreview(device1);

            sp<BufferItemConsumer> bufferItemConsumer;
            sp<BufferItemHander> bufferHandler;
            setupPreviewWindow(device1, &bufferItemConsumer /*out*/, &bufferHandler /*out*/);

            // Preview should get enabled now
            Return<bool> returnBoolStatus = device1->previewEnabled();
            ASSERT_TRUE(returnBoolStatus.isOk());
            ASSERT_TRUE(returnBoolStatus);

            stopPreviewAndClose(device1);
        }
    }
}

// Verify that image capture behaves as expected along with preview callbacks.
TEST_F(CameraHidlTest, takePicture) {
    hidl_vec<hidl_string> cameraDeviceNames = getCameraDeviceNames(mProvider);

    for (const auto& name : cameraDeviceNames) {
        if (getCameraDeviceVersion(name, mProviderType) == CAMERA_DEVICE_API_VERSION_1_0) {
            sp<::android::hardware::camera::device::V1_0::ICameraDevice> device1;
            openCameraDevice(name, mProvider, &device1 /*out*/);
            ASSERT_NE(nullptr, device1.get());
            sp<BufferItemConsumer> bufferItemConsumer;
            sp<BufferItemHander> bufferHandler;
            setupPreviewWindow(device1, &bufferItemConsumer /*out*/, &bufferHandler /*out*/);

            {
                std::unique_lock<std::mutex> l(mLock);
                mDataMessageTypeReceived = DataCallbackMsg::RAW_IMAGE_NOTIFY;
            }

            enableMsgType((unsigned int)DataCallbackMsg::PREVIEW_FRAME, device1);
            startPreview(device1);

            {
                std::unique_lock<std::mutex> l(mLock);
                waitForFrameLocked(DataCallbackMsg::PREVIEW_FRAME, l);
            }

            disableMsgType((unsigned int)DataCallbackMsg::PREVIEW_FRAME, device1);
            enableMsgType((unsigned int)DataCallbackMsg::COMPRESSED_IMAGE, device1);

            {
                std::unique_lock<std::mutex> l(mLock);
                mDataMessageTypeReceived = DataCallbackMsg::RAW_IMAGE_NOTIFY;
            }

            Return<Status> returnStatus = device1->takePicture();
            ASSERT_TRUE(returnStatus.isOk());
            ASSERT_EQ(Status::OK, returnStatus);

            {
                std::unique_lock<std::mutex> l(mLock);
                waitForFrameLocked(DataCallbackMsg::COMPRESSED_IMAGE, l);
            }

            disableMsgType((unsigned int)DataCallbackMsg::COMPRESSED_IMAGE, device1);
            stopPreviewAndClose(device1);
        }
    }
}

// Image capture should fail in case preview didn't get enabled first.
TEST_F(CameraHidlTest, takePictureFail) {
    hidl_vec<hidl_string> cameraDeviceNames = getCameraDeviceNames(mProvider);

    for (const auto& name : cameraDeviceNames) {
        if (getCameraDeviceVersion(name, mProviderType) == CAMERA_DEVICE_API_VERSION_1_0) {
            sp<::android::hardware::camera::device::V1_0::ICameraDevice> device1;
            openCameraDevice(name, mProvider, &device1 /*out*/);
            ASSERT_NE(nullptr, device1.get());

            Return<Status> returnStatus = device1->takePicture();
            ASSERT_TRUE(returnStatus.isOk());
            ASSERT_NE(Status::OK, returnStatus);

            Return<void> ret = device1->close();
            ASSERT_TRUE(ret.isOk());
        }
    }
}

// Verify that image capture can be cancelled.
TEST_F(CameraHidlTest, cancelPicture) {
    hidl_vec<hidl_string> cameraDeviceNames = getCameraDeviceNames(mProvider);

    for (const auto& name : cameraDeviceNames) {
        if (getCameraDeviceVersion(name, mProviderType) == CAMERA_DEVICE_API_VERSION_1_0) {
            sp<::android::hardware::camera::device::V1_0::ICameraDevice> device1;
            openCameraDevice(name, mProvider, &device1 /*out*/);
            ASSERT_NE(nullptr, device1.get());
            sp<BufferItemConsumer> bufferItemConsumer;
            sp<BufferItemHander> bufferHandler;
            setupPreviewWindow(device1, &bufferItemConsumer /*out*/, &bufferHandler /*out*/);
            startPreview(device1);

            Return<Status> returnStatus = device1->takePicture();
            ASSERT_TRUE(returnStatus.isOk());
            ASSERT_EQ(Status::OK, returnStatus);

            returnStatus = device1->cancelPicture();
            ASSERT_TRUE(returnStatus.isOk());
            ASSERT_EQ(Status::OK, returnStatus);

            stopPreviewAndClose(device1);
        }
    }
}

// Image capture cancel is a no-op when image capture is not running.
TEST_F(CameraHidlTest, cancelPictureNOP) {
    hidl_vec<hidl_string> cameraDeviceNames = getCameraDeviceNames(mProvider);

    for (const auto& name : cameraDeviceNames) {
        if (getCameraDeviceVersion(name, mProviderType) == CAMERA_DEVICE_API_VERSION_1_0) {
            sp<::android::hardware::camera::device::V1_0::ICameraDevice> device1;
            openCameraDevice(name, mProvider, &device1 /*out*/);
            ASSERT_NE(nullptr, device1.get());
            sp<BufferItemConsumer> bufferItemConsumer;
            sp<BufferItemHander> bufferHandler;
            setupPreviewWindow(device1, &bufferItemConsumer /*out*/, &bufferHandler /*out*/);
            startPreview(device1);

            Return<Status> returnStatus = device1->cancelPicture();
            ASSERT_TRUE(returnStatus.isOk());
            ASSERT_EQ(Status::OK, returnStatus);

            stopPreviewAndClose(device1);
        }
    }
}

// Test basic video recording.
TEST_F(CameraHidlTest, startStopRecording) {
    hidl_vec<hidl_string> cameraDeviceNames = getCameraDeviceNames(mProvider);

    for (const auto& name : cameraDeviceNames) {
        if (getCameraDeviceVersion(name, mProviderType) == CAMERA_DEVICE_API_VERSION_1_0) {
            sp<::android::hardware::camera::device::V1_0::ICameraDevice> device1;
            openCameraDevice(name, mProvider, &device1 /*out*/);
            ASSERT_NE(nullptr, device1.get());
            sp<BufferItemConsumer> bufferItemConsumer;
            sp<BufferItemHander> bufferHandler;
            setupPreviewWindow(device1, &bufferItemConsumer /*out*/, &bufferHandler /*out*/);

            {
                std::unique_lock<std::mutex> l(mLock);
                mDataMessageTypeReceived = DataCallbackMsg::RAW_IMAGE_NOTIFY;
            }

            enableMsgType((unsigned int)DataCallbackMsg::PREVIEW_FRAME, device1);
            startPreview(device1);

            {
                std::unique_lock<std::mutex> l(mLock);
                waitForFrameLocked(DataCallbackMsg::PREVIEW_FRAME, l);
                mDataMessageTypeReceived = DataCallbackMsg::RAW_IMAGE_NOTIFY;
                mVideoBufferIndex = UINT32_MAX;
            }

            disableMsgType((unsigned int)DataCallbackMsg::PREVIEW_FRAME, device1);

            bool videoMetaEnabled = false;
            Return<Status> returnStatus = device1->storeMetaDataInBuffers(true);
            ASSERT_TRUE(returnStatus.isOk());
            // It is allowed for devices to not support this feature
            ASSERT_TRUE((Status::OK == returnStatus) ||
                        (Status::OPERATION_NOT_SUPPORTED == returnStatus));
            if (Status::OK == returnStatus) {
                videoMetaEnabled = true;
            }

            enableMsgType((unsigned int)DataCallbackMsg::VIDEO_FRAME, device1);
            Return<bool> returnBoolStatus = device1->recordingEnabled();
            ASSERT_TRUE(returnBoolStatus.isOk());
            ASSERT_FALSE(returnBoolStatus);

            returnStatus = device1->startRecording();
            ASSERT_TRUE(returnStatus.isOk());
            ASSERT_EQ(Status::OK, returnStatus);

            {
                std::unique_lock<std::mutex> l(mLock);
                waitForFrameLocked(DataCallbackMsg::VIDEO_FRAME, l);
                ASSERT_NE(UINT32_MAX, mVideoBufferIndex);
                disableMsgType((unsigned int)DataCallbackMsg::VIDEO_FRAME, device1);
            }

            returnBoolStatus = device1->recordingEnabled();
            ASSERT_TRUE(returnBoolStatus.isOk());
            ASSERT_TRUE(returnBoolStatus);

            Return<void> ret;
            if (videoMetaEnabled) {
                ret = device1->releaseRecordingFrameHandle(mVideoData, mVideoBufferIndex,
                                                           mVideoNativeHandle);
                ASSERT_TRUE(ret.isOk());
            } else {
                ret = device1->releaseRecordingFrame(mVideoData, mVideoBufferIndex);
                ASSERT_TRUE(ret.isOk());
            }

            ret = device1->stopRecording();
            ASSERT_TRUE(ret.isOk());

            stopPreviewAndClose(device1);
        }
    }
}

// It shouldn't be possible to start recording without enabling preview first.
TEST_F(CameraHidlTest, startRecordingFail) {
    hidl_vec<hidl_string> cameraDeviceNames = getCameraDeviceNames(mProvider);

    for (const auto& name : cameraDeviceNames) {
        if (getCameraDeviceVersion(name, mProviderType) == CAMERA_DEVICE_API_VERSION_1_0) {
            sp<::android::hardware::camera::device::V1_0::ICameraDevice> device1;
            openCameraDevice(name, mProvider, &device1 /*out*/);
            ASSERT_NE(nullptr, device1.get());

            Return<bool> returnBoolStatus = device1->recordingEnabled();
            ASSERT_TRUE(returnBoolStatus.isOk());
            ASSERT_FALSE(returnBoolStatus);

            Return<Status> returnStatus = device1->startRecording();
            ASSERT_TRUE(returnStatus.isOk());
            ASSERT_NE(Status::OK, returnStatus);

            Return<void> ret = device1->close();
            ASSERT_TRUE(ret.isOk());
        }
    }
}

// Check autofocus support if available.
TEST_F(CameraHidlTest, autoFocus) {
    hidl_vec<hidl_string> cameraDeviceNames = getCameraDeviceNames(mProvider);
    std::vector<const char*> focusModes = {CameraParameters::FOCUS_MODE_AUTO,
                                           CameraParameters::FOCUS_MODE_CONTINUOUS_PICTURE,
                                           CameraParameters::FOCUS_MODE_CONTINUOUS_VIDEO};

    for (const auto& name : cameraDeviceNames) {
        if (getCameraDeviceVersion(name, mProviderType) == CAMERA_DEVICE_API_VERSION_1_0) {
            sp<::android::hardware::camera::device::V1_0::ICameraDevice> device1;
            openCameraDevice(name, mProvider, &device1 /*out*/);
            ASSERT_NE(nullptr, device1.get());

            CameraParameters cameraParams;
            getParameters(device1, &cameraParams /*out*/);

            if (Status::OK !=
                isAutoFocusModeAvailable(cameraParams, CameraParameters::FOCUS_MODE_AUTO)) {
                Return<void> ret = device1->close();
                ASSERT_TRUE(ret.isOk());
                continue;
            }

            sp<BufferItemConsumer> bufferItemConsumer;
            sp<BufferItemHander> bufferHandler;
            setupPreviewWindow(device1, &bufferItemConsumer /*out*/, &bufferHandler /*out*/);
            startPreview(device1);
            enableMsgType((unsigned int)NotifyCallbackMsg::FOCUS, device1);

            for (auto& iter : focusModes) {
                if (Status::OK != isAutoFocusModeAvailable(cameraParams, iter)) {
                    continue;
                }

                cameraParams.set(CameraParameters::KEY_FOCUS_MODE, iter);
                setParameters(device1, cameraParams);
                {
                    std::unique_lock<std::mutex> l(mLock);
                    mNotifyMessage = NotifyCallbackMsg::ERROR;
                }

                Return<Status> returnStatus = device1->autoFocus();
                ASSERT_TRUE(returnStatus.isOk());
                ASSERT_EQ(Status::OK, returnStatus);

                {
                    std::unique_lock<std::mutex> l(mLock);
                    while (NotifyCallbackMsg::FOCUS != mNotifyMessage) {
                        auto timeout = std::chrono::system_clock::now() +
                                       std::chrono::seconds(kAutoFocusTimeoutSec);
                        ASSERT_NE(std::cv_status::timeout, mResultCondition.wait_until(l, timeout));
                    }
                }
            }

            disableMsgType((unsigned int)NotifyCallbackMsg::FOCUS, device1);
            stopPreviewAndClose(device1);
        }
    }
}

// In case autofocus is supported verify that it can be cancelled.
TEST_F(CameraHidlTest, cancelAutoFocus) {
    hidl_vec<hidl_string> cameraDeviceNames = getCameraDeviceNames(mProvider);

    for (const auto& name : cameraDeviceNames) {
        if (getCameraDeviceVersion(name, mProviderType) == CAMERA_DEVICE_API_VERSION_1_0) {
            sp<::android::hardware::camera::device::V1_0::ICameraDevice> device1;
            openCameraDevice(name, mProvider, &device1 /*out*/);
            ASSERT_NE(nullptr, device1.get());

            CameraParameters cameraParams;
            getParameters(device1, &cameraParams /*out*/);

            if (Status::OK !=
                isAutoFocusModeAvailable(cameraParams, CameraParameters::FOCUS_MODE_AUTO)) {
                Return<void> ret = device1->close();
                ASSERT_TRUE(ret.isOk());
                continue;
            }

            // It should be fine to call before preview starts.
            ASSERT_EQ(Status::OK, device1->cancelAutoFocus());

            sp<BufferItemConsumer> bufferItemConsumer;
            sp<BufferItemHander> bufferHandler;
            setupPreviewWindow(device1, &bufferItemConsumer /*out*/, &bufferHandler /*out*/);
            startPreview(device1);

            // It should be fine to call after preview starts too.
            Return<Status> returnStatus = device1->cancelAutoFocus();
            ASSERT_TRUE(returnStatus.isOk());
            ASSERT_EQ(Status::OK, returnStatus);

            returnStatus = device1->autoFocus();
            ASSERT_TRUE(returnStatus.isOk());
            ASSERT_EQ(Status::OK, returnStatus);

            returnStatus = device1->cancelAutoFocus();
            ASSERT_TRUE(returnStatus.isOk());
            ASSERT_EQ(Status::OK, returnStatus);

            stopPreviewAndClose(device1);
        }
    }
}

// Check whether face detection is available and try to enable&disable.
TEST_F(CameraHidlTest, sendCommandFaceDetection) {
    hidl_vec<hidl_string> cameraDeviceNames = getCameraDeviceNames(mProvider);

    for (const auto& name : cameraDeviceNames) {
        if (getCameraDeviceVersion(name, mProviderType) == CAMERA_DEVICE_API_VERSION_1_0) {
            sp<::android::hardware::camera::device::V1_0::ICameraDevice> device1;
            openCameraDevice(name, mProvider, &device1 /*out*/);
            ASSERT_NE(nullptr, device1.get());

            CameraParameters cameraParams;
            getParameters(device1, &cameraParams /*out*/);

            int32_t hwFaces = cameraParams.getInt(CameraParameters::KEY_MAX_NUM_DETECTED_FACES_HW);
            int32_t swFaces = cameraParams.getInt(CameraParameters::KEY_MAX_NUM_DETECTED_FACES_SW);
            if ((0 >= hwFaces) && (0 >= swFaces)) {
                Return<void> ret = device1->close();
                ASSERT_TRUE(ret.isOk());
                continue;
            }

            sp<BufferItemConsumer> bufferItemConsumer;
            sp<BufferItemHander> bufferHandler;
            setupPreviewWindow(device1, &bufferItemConsumer /*out*/, &bufferHandler /*out*/);
            startPreview(device1);

            if (0 < hwFaces) {
                Return<Status> returnStatus = device1->sendCommand(
                    CommandType::START_FACE_DETECTION, CAMERA_FACE_DETECTION_HW, 0);
                ASSERT_TRUE(returnStatus.isOk());
                ASSERT_EQ(Status::OK, returnStatus);
                // TODO(epeev) : Enable and check for face notifications
                returnStatus = device1->sendCommand(CommandType::STOP_FACE_DETECTION,
                                                    CAMERA_FACE_DETECTION_HW, 0);
                ASSERT_TRUE(returnStatus.isOk());
                ASSERT_EQ(Status::OK, returnStatus);
            }

            if (0 < swFaces) {
                Return<Status> returnStatus = device1->sendCommand(
                    CommandType::START_FACE_DETECTION, CAMERA_FACE_DETECTION_SW, 0);
                ASSERT_TRUE(returnStatus.isOk());
                ASSERT_EQ(Status::OK, returnStatus);
                // TODO(epeev) : Enable and check for face notifications
                returnStatus = device1->sendCommand(CommandType::STOP_FACE_DETECTION,
                                                    CAMERA_FACE_DETECTION_SW, 0);
                ASSERT_TRUE(returnStatus.isOk());
                ASSERT_EQ(Status::OK, returnStatus);
            }

            stopPreviewAndClose(device1);
        }
    }
}

// Check whether smooth zoom is available and try to enable&disable.
TEST_F(CameraHidlTest, sendCommandSmoothZoom) {
    hidl_vec<hidl_string> cameraDeviceNames = getCameraDeviceNames(mProvider);

    for (const auto& name : cameraDeviceNames) {
        if (getCameraDeviceVersion(name, mProviderType) == CAMERA_DEVICE_API_VERSION_1_0) {
            sp<::android::hardware::camera::device::V1_0::ICameraDevice> device1;
            openCameraDevice(name, mProvider, &device1 /*out*/);
            ASSERT_NE(nullptr, device1.get());

            CameraParameters cameraParams;
            getParameters(device1, &cameraParams /*out*/);

            const char* smoothZoomStr =
                cameraParams.get(CameraParameters::KEY_SMOOTH_ZOOM_SUPPORTED);
            bool smoothZoomSupported =
                ((nullptr != smoothZoomStr) && (strcmp(smoothZoomStr, CameraParameters::TRUE) == 0))
                    ? true
                    : false;
            if (!smoothZoomSupported) {
                Return<void> ret = device1->close();
                ASSERT_TRUE(ret.isOk());
                continue;
            }

            int32_t maxZoom = cameraParams.getInt(CameraParameters::KEY_MAX_ZOOM);
            ASSERT_TRUE(0 < maxZoom);

            sp<BufferItemConsumer> bufferItemConsumer;
            sp<BufferItemHander> bufferHandler;
            setupPreviewWindow(device1, &bufferItemConsumer /*out*/, &bufferHandler /*out*/);
            startPreview(device1);
            setParameters(device1, cameraParams);

            Return<Status> returnStatus =
                device1->sendCommand(CommandType::START_SMOOTH_ZOOM, maxZoom, 0);
            ASSERT_TRUE(returnStatus.isOk());
            ASSERT_EQ(Status::OK, returnStatus);
            // TODO(epeev) : Enable and check for face notifications
            returnStatus = device1->sendCommand(CommandType::STOP_SMOOTH_ZOOM, 0, 0);
            ASSERT_TRUE(returnStatus.isOk());
            ASSERT_EQ(Status::OK, returnStatus);

            stopPreviewAndClose(device1);
        }
    }
}

// Basic sanity tests related to camera parameters.
TEST_F(CameraHidlTest, getSetParameters) {
    hidl_vec<hidl_string> cameraDeviceNames = getCameraDeviceNames(mProvider);

    for (const auto& name : cameraDeviceNames) {
        if (getCameraDeviceVersion(name, mProviderType) == CAMERA_DEVICE_API_VERSION_1_0) {
            sp<::android::hardware::camera::device::V1_0::ICameraDevice> device1;
            openCameraDevice(name, mProvider, &device1 /*out*/);
            ASSERT_NE(nullptr, device1.get());

            CameraParameters cameraParams;
            getParameters(device1, &cameraParams /*out*/);

            int32_t width, height;
            cameraParams.getPictureSize(&width, &height);
            ASSERT_TRUE((0 < width) && (0 < height));
            cameraParams.getPreviewSize(&width, &height);
            ASSERT_TRUE((0 < width) && (0 < height));
            int32_t minFps, maxFps;
            cameraParams.getPreviewFpsRange(&minFps, &maxFps);
            ASSERT_TRUE((0 < minFps) && (0 < maxFps));
            ASSERT_NE(nullptr, cameraParams.getPreviewFormat());
            ASSERT_NE(nullptr, cameraParams.getPictureFormat());
            ASSERT_TRUE(
                strcmp(CameraParameters::PIXEL_FORMAT_JPEG, cameraParams.getPictureFormat()) == 0);

            const char* flashMode = cameraParams.get(CameraParameters::KEY_FLASH_MODE);
            ASSERT_TRUE((nullptr == flashMode) ||
                        (strcmp(CameraParameters::FLASH_MODE_OFF, flashMode) == 0));

            const char* wbMode = cameraParams.get(CameraParameters::KEY_WHITE_BALANCE);
            ASSERT_TRUE((nullptr == wbMode) ||
                        (strcmp(CameraParameters::WHITE_BALANCE_AUTO, wbMode) == 0));

            const char* effect = cameraParams.get(CameraParameters::KEY_EFFECT);
            ASSERT_TRUE((nullptr == effect) ||
                        (strcmp(CameraParameters::EFFECT_NONE, effect) == 0));

            ::android::Vector<Size> previewSizes;
            cameraParams.getSupportedPreviewSizes(previewSizes);
            ASSERT_FALSE(previewSizes.empty());
            ::android::Vector<Size> pictureSizes;
            cameraParams.getSupportedPictureSizes(pictureSizes);
            ASSERT_FALSE(pictureSizes.empty());
            const char* previewFormats =
                cameraParams.get(CameraParameters::KEY_SUPPORTED_PREVIEW_FORMATS);
            ASSERT_NE(nullptr, previewFormats);
            ::android::String8 previewFormatsString(previewFormats);
            ASSERT_TRUE(previewFormatsString.contains(CameraParameters::PIXEL_FORMAT_YUV420SP));
            ASSERT_NE(nullptr, cameraParams.get(CameraParameters::KEY_SUPPORTED_PICTURE_FORMATS));
            ASSERT_NE(nullptr,
                      cameraParams.get(CameraParameters::KEY_SUPPORTED_PREVIEW_FRAME_RATES));
            const char* focusModes = cameraParams.get(CameraParameters::KEY_SUPPORTED_FOCUS_MODES);
            ASSERT_NE(nullptr, focusModes);
            ::android::String8 focusModesString(focusModes);
            const char* focusMode = cameraParams.get(CameraParameters::KEY_FOCUS_MODE);
            ASSERT_NE(nullptr, focusMode);
            // Auto focus mode should be default
            if (focusModesString.contains(CameraParameters::FOCUS_MODE_AUTO)) {
                ASSERT_TRUE(strcmp(CameraParameters::FOCUS_MODE_AUTO, focusMode) == 0);
            }
            ASSERT_TRUE(0 < cameraParams.getInt(CameraParameters::KEY_FOCAL_LENGTH));
            int32_t horizontalViewAngle =
                cameraParams.getInt(CameraParameters::KEY_HORIZONTAL_VIEW_ANGLE);
            ASSERT_TRUE((0 < horizontalViewAngle) && (360 >= horizontalViewAngle));
            int32_t verticalViewAngle =
                cameraParams.getInt(CameraParameters::KEY_VERTICAL_VIEW_ANGLE);
            ASSERT_TRUE((0 < verticalViewAngle) && (360 >= verticalViewAngle));
            int32_t jpegQuality = cameraParams.getInt(CameraParameters::KEY_JPEG_QUALITY);
            ASSERT_TRUE((1 <= jpegQuality) && (100 >= jpegQuality));
            int32_t jpegThumbQuality =
                cameraParams.getInt(CameraParameters::KEY_JPEG_THUMBNAIL_QUALITY);
            ASSERT_TRUE((1 <= jpegThumbQuality) && (100 >= jpegThumbQuality));

            cameraParams.setPictureSize(pictureSizes[0].width, pictureSizes[0].height);
            cameraParams.setPreviewSize(previewSizes[0].width, previewSizes[0].height);

            setParameters(device1, cameraParams);
            getParameters(device1, &cameraParams /*out*/);

            cameraParams.getPictureSize(&width, &height);
            ASSERT_TRUE((pictureSizes[0].width == width) && (pictureSizes[0].height == height));
            cameraParams.getPreviewSize(&width, &height);
            ASSERT_TRUE((previewSizes[0].width == width) && (previewSizes[0].height == height));

            Return<void> ret = device1->close();
            ASSERT_TRUE(ret.isOk());
        }
    }
}

// Verify that the static camera characteristics can be retrieved
// successfully.
TEST_F(CameraHidlTest, getCameraCharacteristics) {
    hidl_vec<hidl_string> cameraDeviceNames = getCameraDeviceNames(mProvider);

    for (const auto& name : cameraDeviceNames) {
        int deviceVersion = getCameraDeviceVersion(name, mProviderType);
        switch (deviceVersion) {
            case CAMERA_DEVICE_API_VERSION_3_5:
            case CAMERA_DEVICE_API_VERSION_3_4:
            case CAMERA_DEVICE_API_VERSION_3_3:
            case CAMERA_DEVICE_API_VERSION_3_2: {
                ::android::sp<::android::hardware::camera::device::V3_2::ICameraDevice> device3_x;
                ALOGI("getCameraCharacteristics: Testing camera device %s", name.c_str());
                Return<void> ret;
                ret = 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_x = device;
                    });
                ASSERT_TRUE(ret.isOk());

                ret = device3_x->getCameraCharacteristics([&](auto status, const auto& chars) {
                    verifyCameraCharacteristics(status, chars);
                    verifyMonochromeCharacteristics(chars, deviceVersion);
                    verifyRecommendedConfigs(chars);
                    verifyLogicalCameraMetadata(name, device3_x, chars, deviceVersion,
                            cameraDeviceNames);
                });
                ASSERT_TRUE(ret.isOk());

                //getPhysicalCameraCharacteristics will fail for publicly
                //advertised camera IDs.
                if (deviceVersion >= CAMERA_DEVICE_API_VERSION_3_5) {
                    auto castResult = device::V3_5::ICameraDevice::castFrom(device3_x);
                    ASSERT_TRUE(castResult.isOk());
                    ::android::sp<::android::hardware::camera::device::V3_5::ICameraDevice>
                            device3_5 = castResult;
                    ASSERT_NE(device3_5, nullptr);

                    std::string version, cameraId;
                    ASSERT_TRUE(::matchDeviceName(name, mProviderType, &version, &cameraId));
                    Return<void> ret = device3_5->getPhysicalCameraCharacteristics(cameraId,
                            [&](auto status, const auto& chars) {
                        ASSERT_TRUE(Status::ILLEGAL_ARGUMENT == status);
                        ASSERT_EQ(0, chars.size());
                    });
                    ASSERT_TRUE(ret.isOk());
                }
            }
            break;
            case CAMERA_DEVICE_API_VERSION_1_0: {
                //Not applicable
            }
            break;
            default: {
                ALOGE("%s: Unsupported device version %d", __func__, deviceVersion);
                ADD_FAILURE();
            }
            break;
        }
    }
}

//In case it is supported verify that torch can be enabled.
//Check for corresponding toch callbacks as well.
TEST_F(CameraHidlTest, setTorchMode) {
    hidl_vec<hidl_string> cameraDeviceNames = getCameraDeviceNames(mProvider);
    bool torchControlSupported = false;
    Return<void> ret;

    ret = mProvider->isSetTorchModeSupported([&](auto status, bool support) {
        ALOGI("isSetTorchModeSupported returns status:%d supported:%d", (int)status, support);
        ASSERT_EQ(Status::OK, status);
        torchControlSupported = support;
    });

    sp<TorchProviderCb> cb = new TorchProviderCb(this);
    Return<Status> returnStatus = mProvider->setCallback(cb);
    ASSERT_TRUE(returnStatus.isOk());
    ASSERT_EQ(Status::OK, returnStatus);

    for (const auto& name : cameraDeviceNames) {
        int deviceVersion = getCameraDeviceVersion(name, mProviderType);
        switch (deviceVersion) {
            case CAMERA_DEVICE_API_VERSION_3_5:
            case CAMERA_DEVICE_API_VERSION_3_4:
            case CAMERA_DEVICE_API_VERSION_3_3:
            case CAMERA_DEVICE_API_VERSION_3_2: {
                ::android::sp<::android::hardware::camera::device::V3_2::ICameraDevice> device3_x;
                ALOGI("setTorchMode: Testing camera device %s", name.c_str());
                ret = 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_x = device;
                    });
                ASSERT_TRUE(ret.isOk());

                mTorchStatus = TorchModeStatus::NOT_AVAILABLE;
                returnStatus = device3_x->setTorchMode(TorchMode::ON);
                ASSERT_TRUE(returnStatus.isOk());
                if (!torchControlSupported) {
                    ASSERT_EQ(Status::METHOD_NOT_SUPPORTED, returnStatus);
                } else {
                    ASSERT_TRUE(returnStatus == Status::OK ||
                                returnStatus == Status::OPERATION_NOT_SUPPORTED);
                    if (returnStatus == Status::OK) {
                        {
                            std::unique_lock<std::mutex> l(mTorchLock);
                            while (TorchModeStatus::NOT_AVAILABLE == mTorchStatus) {
                                auto timeout = std::chrono::system_clock::now() +
                                               std::chrono::seconds(kTorchTimeoutSec);
                                ASSERT_NE(std::cv_status::timeout, mTorchCond.wait_until(l, timeout));
                            }
                            ASSERT_EQ(TorchModeStatus::AVAILABLE_ON, mTorchStatus);
                            mTorchStatus = TorchModeStatus::NOT_AVAILABLE;
                        }

                        returnStatus = device3_x->setTorchMode(TorchMode::OFF);
                        ASSERT_TRUE(returnStatus.isOk());
                        ASSERT_EQ(Status::OK, returnStatus);

                        {
                            std::unique_lock<std::mutex> l(mTorchLock);
                            while (TorchModeStatus::NOT_AVAILABLE == mTorchStatus) {
                                auto timeout = std::chrono::system_clock::now() +
                                               std::chrono::seconds(kTorchTimeoutSec);
                                ASSERT_NE(std::cv_status::timeout, mTorchCond.wait_until(l, timeout));
                            }
                            ASSERT_EQ(TorchModeStatus::AVAILABLE_OFF, mTorchStatus);
                        }
                    }
                }
            }
            break;
            case CAMERA_DEVICE_API_VERSION_1_0: {
                ::android::sp<::android::hardware::camera::device::V1_0::ICameraDevice> device1;
                ALOGI("dumpState: Testing camera device %s", name.c_str());
                ret = mProvider->getCameraDeviceInterface_V1_x(
                    name, [&](auto status, const auto& device) {
                        ALOGI("getCameraDeviceInterface_V1_x returns status:%d", (int)status);
                        ASSERT_EQ(Status::OK, status);
                        ASSERT_NE(device, nullptr);
                        device1 = device;
                    });
                ASSERT_TRUE(ret.isOk());

                mTorchStatus = TorchModeStatus::NOT_AVAILABLE;
                returnStatus = device1->setTorchMode(TorchMode::ON);
                ASSERT_TRUE(returnStatus.isOk());
                if (!torchControlSupported) {
                    ASSERT_EQ(Status::METHOD_NOT_SUPPORTED, returnStatus);
                } else {
                    ASSERT_TRUE(returnStatus == Status::OK ||
                                returnStatus == Status::OPERATION_NOT_SUPPORTED);
                    if (returnStatus == Status::OK) {
                        {
                            std::unique_lock<std::mutex> l(mTorchLock);
                            while (TorchModeStatus::NOT_AVAILABLE == mTorchStatus) {
                                auto timeout = std::chrono::system_clock::now() +
                                               std::chrono::seconds(kTorchTimeoutSec);
                                ASSERT_NE(std::cv_status::timeout, mTorchCond.wait_until(l,
                                        timeout));
                            }
                            ASSERT_EQ(TorchModeStatus::AVAILABLE_ON, mTorchStatus);
                            mTorchStatus = TorchModeStatus::NOT_AVAILABLE;
                        }

                        returnStatus = device1->setTorchMode(TorchMode::OFF);
                        ASSERT_TRUE(returnStatus.isOk());
                        ASSERT_EQ(Status::OK, returnStatus);

                        {
                            std::unique_lock<std::mutex> l(mTorchLock);
                            while (TorchModeStatus::NOT_AVAILABLE == mTorchStatus) {
                                auto timeout = std::chrono::system_clock::now() +
                                               std::chrono::seconds(kTorchTimeoutSec);
                                ASSERT_NE(std::cv_status::timeout, mTorchCond.wait_until(l,
                                        timeout));
                            }
                            ASSERT_EQ(TorchModeStatus::AVAILABLE_OFF, mTorchStatus);
                        }
                    }
                }
                ret = device1->close();
                ASSERT_TRUE(ret.isOk());
            }
            break;
            default: {
                ALOGE("%s: Unsupported device version %d", __func__, deviceVersion);
                ADD_FAILURE();
            }
            break;
        }
    }

    returnStatus = mProvider->setCallback(nullptr);
    ASSERT_TRUE(returnStatus.isOk());
    ASSERT_EQ(Status::OK, returnStatus);
}

// Check dump functionality.
TEST_F(CameraHidlTest, dumpState) {
    hidl_vec<hidl_string> cameraDeviceNames = getCameraDeviceNames(mProvider);
    Return<void> ret;

    for (const auto& name : cameraDeviceNames) {
        int deviceVersion = getCameraDeviceVersion(name, mProviderType);
        switch (deviceVersion) {
            case CAMERA_DEVICE_API_VERSION_3_5:
            case CAMERA_DEVICE_API_VERSION_3_4:
            case CAMERA_DEVICE_API_VERSION_3_3:
            case CAMERA_DEVICE_API_VERSION_3_2: {
                ::android::sp<ICameraDevice> device3_x;
                ALOGI("dumpState: Testing camera device %s", name.c_str());
                ret = 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_x = device;
                    });
                ASSERT_TRUE(ret.isOk());

                native_handle_t* raw_handle = native_handle_create(1, 0);
                raw_handle->data[0] = open(kDumpOutput, O_RDWR);
                ASSERT_GE(raw_handle->data[0], 0);
                hidl_handle handle = raw_handle;
                ret = device3_x->dumpState(handle);
                ASSERT_TRUE(ret.isOk());
                close(raw_handle->data[0]);
                native_handle_delete(raw_handle);
            }
            break;
            case CAMERA_DEVICE_API_VERSION_1_0: {
                ::android::sp<::android::hardware::camera::device::V1_0::ICameraDevice> device1;
                ALOGI("dumpState: Testing camera device %s", name.c_str());
                ret = mProvider->getCameraDeviceInterface_V1_x(
                    name, [&](auto status, const auto& device) {
                        ALOGI("getCameraDeviceInterface_V1_x returns status:%d", (int)status);
                        ASSERT_EQ(Status::OK, status);
                        ASSERT_NE(device, nullptr);
                        device1 = device;
                    });
                ASSERT_TRUE(ret.isOk());

                native_handle_t* raw_handle = native_handle_create(1, 0);
                raw_handle->data[0] = open(kDumpOutput, O_RDWR);
                ASSERT_GE(raw_handle->data[0], 0);
                hidl_handle handle = raw_handle;
                Return<Status> returnStatus = device1->dumpState(handle);
                ASSERT_TRUE(returnStatus.isOk());
                ASSERT_EQ(Status::OK, returnStatus);
                close(raw_handle->data[0]);
                native_handle_delete(raw_handle);
            }
            break;
            default: {
                ALOGE("%s: Unsupported device version %d", __func__, deviceVersion);
                ADD_FAILURE();
            }
            break;
        }
    }
}

// Open, dumpStates, then close
TEST_F(CameraHidlTest, openClose) {
    hidl_vec<hidl_string> cameraDeviceNames = getCameraDeviceNames(mProvider);
    Return<void> ret;

    for (const auto& name : cameraDeviceNames) {
        int deviceVersion = getCameraDeviceVersion(name, mProviderType);
        switch (deviceVersion) {
            case CAMERA_DEVICE_API_VERSION_3_5:
            case CAMERA_DEVICE_API_VERSION_3_4:
            case CAMERA_DEVICE_API_VERSION_3_3:
            case CAMERA_DEVICE_API_VERSION_3_2: {
                ::android::sp<::android::hardware::camera::device::V3_2::ICameraDevice> device3_x;
                ALOGI("openClose: Testing camera device %s", name.c_str());
                ret = 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_x = device;
                    });
                ASSERT_TRUE(ret.isOk());

                sp<EmptyDeviceCb> cb = new EmptyDeviceCb;
                sp<ICameraDeviceSession> session;
                ret = device3_x->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;
                });
                ASSERT_TRUE(ret.isOk());
                // Ensure that a device labeling itself as 3.3/3.4 can have its session interface
                // cast the 3.3/3.4 interface, and that lower versions can't be cast to it.
                sp<device::V3_3::ICameraDeviceSession> sessionV3_3;
                sp<device::V3_4::ICameraDeviceSession> sessionV3_4;
                sp<device::V3_5::ICameraDeviceSession> sessionV3_5;
                castSession(session, deviceVersion, &sessionV3_3, &sessionV3_4, &sessionV3_5);
                if (deviceVersion == CAMERA_DEVICE_API_VERSION_3_5) {
                    ASSERT_TRUE(sessionV3_5.get() != nullptr);
                } else if (deviceVersion == CAMERA_DEVICE_API_VERSION_3_4) {
                    ASSERT_TRUE(sessionV3_4.get() != nullptr);
                } else if (deviceVersion == CAMERA_DEVICE_API_VERSION_3_3) {
                    ASSERT_TRUE(sessionV3_3.get() != nullptr);
                } else { //V3_2
                    ASSERT_TRUE(sessionV3_3.get() == nullptr);
                    ASSERT_TRUE(sessionV3_4.get() == nullptr);
                    ASSERT_TRUE(sessionV3_5.get() == nullptr);
                }
                native_handle_t* raw_handle = native_handle_create(1, 0);
                raw_handle->data[0] = open(kDumpOutput, O_RDWR);
                ASSERT_GE(raw_handle->data[0], 0);
                hidl_handle handle = raw_handle;
                ret = device3_x->dumpState(handle);
                ASSERT_TRUE(ret.isOk());
                close(raw_handle->data[0]);
                native_handle_delete(raw_handle);

                ret = session->close();
                ASSERT_TRUE(ret.isOk());
                // TODO: test all session API calls return INTERNAL_ERROR after close
                // TODO: keep a wp copy here and verify session cannot be promoted out of this scope
            }
            break;
            case CAMERA_DEVICE_API_VERSION_1_0: {
                sp<::android::hardware::camera::device::V1_0::ICameraDevice> device1;
                openCameraDevice(name, mProvider, &device1 /*out*/);
                ASSERT_NE(nullptr, device1.get());

                native_handle_t* raw_handle = native_handle_create(1, 0);
                raw_handle->data[0] = open(kDumpOutput, O_RDWR);
                ASSERT_GE(raw_handle->data[0], 0);
                hidl_handle handle = raw_handle;
                Return<Status> returnStatus = device1->dumpState(handle);
                ASSERT_TRUE(returnStatus.isOk());
                ASSERT_EQ(Status::OK, returnStatus);
                close(raw_handle->data[0]);
                native_handle_delete(raw_handle);

                ret = device1->close();
                ASSERT_TRUE(ret.isOk());
            }
            break;
            default: {
                ALOGE("%s: Unsupported device version %d", __func__, deviceVersion);
                ADD_FAILURE();
            }
            break;
        }
    }
}

// Check whether all common default request settings can be sucessfully
// constructed.
TEST_F(CameraHidlTest, constructDefaultRequestSettings) {
    hidl_vec<hidl_string> cameraDeviceNames = getCameraDeviceNames(mProvider);

    for (const auto& name : cameraDeviceNames) {
        int deviceVersion = getCameraDeviceVersion(name, mProviderType);
        switch (deviceVersion) {
            case CAMERA_DEVICE_API_VERSION_3_5:
            case CAMERA_DEVICE_API_VERSION_3_4:
            case CAMERA_DEVICE_API_VERSION_3_3:
            case CAMERA_DEVICE_API_VERSION_3_2: {
                ::android::sp<::android::hardware::camera::device::V3_2::ICameraDevice> device3_x;
                Return<void> ret;
                ALOGI("constructDefaultRequestSettings: Testing camera device %s", name.c_str());
                ret = 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_x = device;
                    });
                ASSERT_TRUE(ret.isOk());

                sp<EmptyDeviceCb> cb = new EmptyDeviceCb;
                sp<ICameraDeviceSession> session;
                ret = device3_x->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;
                });
                ASSERT_TRUE(ret.isOk());

                for (uint32_t t = (uint32_t)RequestTemplate::PREVIEW;
                     t <= (uint32_t)RequestTemplate::MANUAL; t++) {
                    RequestTemplate reqTemplate = (RequestTemplate)t;
                    ret =
                        session->constructDefaultRequestSettings(
                            reqTemplate, [&](auto status, const auto& req) {
                                ALOGI("constructDefaultRequestSettings returns status:%d",
                                      (int)status);
                                if (reqTemplate == RequestTemplate::ZERO_SHUTTER_LAG ||
                                        reqTemplate == RequestTemplate::MANUAL) {
                                    // optional templates
                                    ASSERT_TRUE((status == Status::OK) ||
                                            (status == Status::ILLEGAL_ARGUMENT));
                                } else {
                                    ASSERT_EQ(Status::OK, status);
                                }

                                if (status == Status::OK) {
                                    const camera_metadata_t* metadata =
                                        (camera_metadata_t*) req.data();
                                    size_t expectedSize = req.size();
                                    int result = validate_camera_metadata_structure(
                                            metadata, &expectedSize);
                                    ASSERT_TRUE((result == 0) ||
                                            (result == CAMERA_METADATA_VALIDATION_SHIFTED));
                                    size_t entryCount =
                                            get_camera_metadata_entry_count(metadata);
                                    // TODO: we can do better than 0 here. Need to check how many required
                                    // request keys we've defined for each template
                                    ASSERT_GT(entryCount, 0u);
                                    ALOGI("template %u metadata entry count is %zu",
                                          t, entryCount);
                                } else {
                                    ASSERT_EQ(0u, req.size());
                                }
                            });
                    ASSERT_TRUE(ret.isOk());
                }
                ret = session->close();
                ASSERT_TRUE(ret.isOk());
            }
            break;
            case CAMERA_DEVICE_API_VERSION_1_0: {
                //Not applicable
            }
            break;
            default: {
                ALOGE("%s: Unsupported device version %d", __func__, deviceVersion);
                ADD_FAILURE();
            }
            break;
        }
    }
}


// Verify that all supported stream formats and sizes can be configured
// successfully.
TEST_F(CameraHidlTest, configureStreamsAvailableOutputs) {
    hidl_vec<hidl_string> cameraDeviceNames = getCameraDeviceNames(mProvider);
    std::vector<AvailableStream> outputStreams;

    for (const auto& name : cameraDeviceNames) {
        int deviceVersion = getCameraDeviceVersion(name, mProviderType);
        if (deviceVersion == CAMERA_DEVICE_API_VERSION_1_0) {
            continue;
        } else if (deviceVersion <= 0) {
            ALOGE("%s: Unsupported device version %d", __func__, deviceVersion);
            ADD_FAILURE();
            return;
        }

        camera_metadata_t* staticMeta;
        Return<void> ret;
        sp<ICameraDeviceSession> session;
        sp<device::V3_3::ICameraDeviceSession> session3_3;
        sp<device::V3_4::ICameraDeviceSession> session3_4;
        sp<device::V3_5::ICameraDeviceSession> session3_5;
        sp<device::V3_2::ICameraDevice> cameraDevice;
        sp<device::V3_5::ICameraDevice> cameraDevice3_5;
        openEmptyDeviceSession(name, mProvider,
                &session /*out*/, &staticMeta /*out*/, &cameraDevice /*out*/);
        castSession(session, deviceVersion, &session3_3, &session3_4, &session3_5);
        castDevice(cameraDevice, deviceVersion, &cameraDevice3_5);

        outputStreams.clear();
        ASSERT_EQ(Status::OK, getAvailableOutputStreams(staticMeta, outputStreams));
        ASSERT_NE(0u, outputStreams.size());

        uint32_t jpegBufferSize = 0;
        ASSERT_EQ(Status::OK, getJpegBufferSize(staticMeta, &jpegBufferSize));
        ASSERT_NE(0u, jpegBufferSize);

        int32_t streamId = 0;
        uint32_t streamConfigCounter = 0;
        for (auto& it : outputStreams) {
            V3_2::Stream stream3_2;
            V3_2::DataspaceFlags dataspaceFlag = 0;
            switch (static_cast<PixelFormat>(it.format)) {
                case PixelFormat::BLOB:
                    dataspaceFlag = static_cast<V3_2::DataspaceFlags>(Dataspace::V0_JFIF);
                    break;
                case PixelFormat::Y16:
                    dataspaceFlag = static_cast<V3_2::DataspaceFlags>(Dataspace::DEPTH);
                    break;
                default:
                    dataspaceFlag = static_cast<V3_2::DataspaceFlags>(Dataspace::UNKNOWN);
            }
            stream3_2 = {streamId,
                             StreamType::OUTPUT,
                             static_cast<uint32_t>(it.width),
                             static_cast<uint32_t>(it.height),
                             static_cast<PixelFormat>(it.format),
                             GRALLOC1_CONSUMER_USAGE_HWCOMPOSER,
                             dataspaceFlag,
                             StreamRotation::ROTATION_0};
            ::android::hardware::hidl_vec<V3_2::Stream> streams3_2 = {stream3_2};
            ::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);
            if (session3_5 != nullptr) {
                bool expectStreamCombQuery = (isLogicalMultiCamera(staticMeta) == Status::OK);
                verifyStreamCombination(cameraDevice3_5, config3_4,
                        /*expectedStatus*/ true, expectStreamCombQuery);
                config3_5.streamConfigCounter = streamConfigCounter++;
                ret = session3_5->configureStreams_3_5(config3_5,
                        [streamId](Status s, device::V3_4::HalStreamConfiguration halConfig) {
                            ASSERT_EQ(Status::OK, s);
                            ASSERT_EQ(1u, halConfig.streams.size());
                            ASSERT_EQ(halConfig.streams[0].v3_3.v3_2.id, streamId);
                        });
            } else if (session3_4 != nullptr) {
                ret = session3_4->configureStreams_3_4(config3_4,
                        [streamId](Status s, device::V3_4::HalStreamConfiguration halConfig) {
                            ASSERT_EQ(Status::OK, s);
                            ASSERT_EQ(1u, halConfig.streams.size());
                            ASSERT_EQ(halConfig.streams[0].v3_3.v3_2.id, streamId);
                        });
            } else if (session3_3 != nullptr) {
                ret = session3_3->configureStreams_3_3(config3_2,
                        [streamId](Status s, device::V3_3::HalStreamConfiguration halConfig) {
                            ASSERT_EQ(Status::OK, s);
                            ASSERT_EQ(1u, halConfig.streams.size());
                            ASSERT_EQ(halConfig.streams[0].v3_2.id, streamId);
                        });
            } else {
                ret = session->configureStreams(config3_2,
                        [streamId](Status s, HalStreamConfiguration halConfig) {
                            ASSERT_EQ(Status::OK, s);
                            ASSERT_EQ(1u, halConfig.streams.size());
                            ASSERT_EQ(halConfig.streams[0].id, streamId);
                        });
            }
            ASSERT_TRUE(ret.isOk());
            streamId++;
        }

        free_camera_metadata(staticMeta);
        ret = session->close();
        ASSERT_TRUE(ret.isOk());
    }
}

// Check for correct handling of invalid/incorrect configuration parameters.
TEST_F(CameraHidlTest, configureStreamsInvalidOutputs) {
    hidl_vec<hidl_string> cameraDeviceNames = getCameraDeviceNames(mProvider);
    std::vector<AvailableStream> outputStreams;

    for (const auto& name : cameraDeviceNames) {
        int deviceVersion = getCameraDeviceVersion(name, mProviderType);
        if (deviceVersion == CAMERA_DEVICE_API_VERSION_1_0) {
            continue;
        } else if (deviceVersion <= 0) {
            ALOGE("%s: Unsupported device version %d", __func__, deviceVersion);
            ADD_FAILURE();
            return;
        }

        camera_metadata_t* staticMeta;
        Return<void> ret;
        sp<ICameraDeviceSession> session;
        sp<device::V3_3::ICameraDeviceSession> session3_3;
        sp<device::V3_4::ICameraDeviceSession> session3_4;
        sp<device::V3_5::ICameraDeviceSession> session3_5;
        sp<device::V3_2::ICameraDevice> cameraDevice;
        sp<device::V3_5::ICameraDevice> cameraDevice3_5;
        openEmptyDeviceSession(name, mProvider, &session /*out*/, &staticMeta /*out*/,
                &cameraDevice /*out*/);
        castSession(session, deviceVersion, &session3_3, &session3_4, &session3_5);
        castDevice(cameraDevice, deviceVersion, &cameraDevice3_5);

        outputStreams.clear();
        ASSERT_EQ(Status::OK, getAvailableOutputStreams(staticMeta, outputStreams));
        ASSERT_NE(0u, outputStreams.size());

        uint32_t jpegBufferSize = 0;
        ASSERT_EQ(Status::OK, getJpegBufferSize(staticMeta, &jpegBufferSize));
        ASSERT_NE(0u, jpegBufferSize);

        int32_t streamId = 0;
        V3_2::Stream stream3_2 = {streamId++,
                         StreamType::OUTPUT,
                         static_cast<uint32_t>(0),
                         static_cast<uint32_t>(0),
                         static_cast<PixelFormat>(outputStreams[0].format),
                         GRALLOC1_CONSUMER_USAGE_HWCOMPOSER,
                         0,
                         StreamRotation::ROTATION_0};
        uint32_t streamConfigCounter = 0;
        ::android::hardware::hidl_vec<V3_2::Stream> streams = {stream3_2};
        ::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);
        if (session3_5 != nullptr) {
            verifyStreamCombination(cameraDevice3_5, config3_4, /*expectedStatus*/ false,
                    /*expectStreamCombQuery*/false);
            config3_5.streamConfigCounter = streamConfigCounter++;
            ret = session3_5->configureStreams_3_5(config3_5,
                    [](Status s, device::V3_4::HalStreamConfiguration) {
                        ASSERT_TRUE((Status::ILLEGAL_ARGUMENT == s) ||
                            (Status::INTERNAL_ERROR == s));
                    });
        } else if (session3_4 != nullptr) {
            ret = session3_4->configureStreams_3_4(config3_4,
                    [](Status s, device::V3_4::HalStreamConfiguration) {
                        ASSERT_TRUE((Status::ILLEGAL_ARGUMENT == s) ||
                                (Status::INTERNAL_ERROR == s));
                    });
        } 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) ||
                                (Status::INTERNAL_ERROR == s));
                    });
        } else {
            ret = session->configureStreams(config3_2,
                    [](Status s, HalStreamConfiguration) {
                        ASSERT_TRUE((Status::ILLEGAL_ARGUMENT == s) ||
                                (Status::INTERNAL_ERROR == s));
                    });
        }
        ASSERT_TRUE(ret.isOk());

        stream3_2 = {streamId++,
                  StreamType::OUTPUT,
                  static_cast<uint32_t>(UINT32_MAX),
                  static_cast<uint32_t>(UINT32_MAX),
                  static_cast<PixelFormat>(outputStreams[0].format),
                  GRALLOC1_CONSUMER_USAGE_HWCOMPOSER,
                  0,
                  StreamRotation::ROTATION_0};
        streams[0] = stream3_2;
        createStreamConfiguration(streams, StreamConfigurationMode::NORMAL_MODE,
                &config3_2, &config3_4, &config3_5, jpegBufferSize);
        if (session3_5 != nullptr) {
            config3_5.streamConfigCounter = streamConfigCounter++;
            ret = session3_5->configureStreams_3_5(config3_5, [](Status s,
                        device::V3_4::HalStreamConfiguration) {
                    ASSERT_EQ(Status::ILLEGAL_ARGUMENT, s);
                });
        } else if(session3_4 != nullptr) {
            ret = session3_4->configureStreams_3_4(config3_4, [](Status s,
                        device::V3_4::HalStreamConfiguration) {
                    ASSERT_EQ(Status::ILLEGAL_ARGUMENT, s);
                });
        } else if(session3_3 != nullptr) {
            ret = session3_3->configureStreams_3_3(config3_2, [](Status s,
                        device::V3_3::HalStreamConfiguration) {
                    ASSERT_EQ(Status::ILLEGAL_ARGUMENT, s);
                });
        } else {
            ret = session->configureStreams(config3_2, [](Status s,
                        HalStreamConfiguration) {
                    ASSERT_EQ(Status::ILLEGAL_ARGUMENT, s);
                });
        }
        ASSERT_TRUE(ret.isOk());

        for (auto& it : outputStreams) {
            stream3_2 = {streamId++,
                      StreamType::OUTPUT,
                      static_cast<uint32_t>(it.width),
                      static_cast<uint32_t>(it.height),
                      static_cast<PixelFormat>(UINT32_MAX),
                      GRALLOC1_CONSUMER_USAGE_HWCOMPOSER,
                      0,
                      StreamRotation::ROTATION_0};
            streams[0] = stream3_2;
            createStreamConfiguration(streams, StreamConfigurationMode::NORMAL_MODE,
                    &config3_2, &config3_4, &config3_5, jpegBufferSize);
            if (session3_5 != nullptr) {
                config3_5.streamConfigCounter = streamConfigCounter++;
                ret = session3_5->configureStreams_3_5(config3_5,
                        [](Status s, device::V3_4::HalStreamConfiguration) {
                            ASSERT_EQ(Status::ILLEGAL_ARGUMENT, s);
                        });
            } else if(session3_4 != nullptr) {
                ret = session3_4->configureStreams_3_4(config3_4,
                        [](Status s, device::V3_4::HalStreamConfiguration) {
                            ASSERT_EQ(Status::ILLEGAL_ARGUMENT, s);
                        });
            } else if(session3_3 != nullptr) {
                ret = session3_3->configureStreams_3_3(config3_2,
                        [](Status s, device::V3_3::HalStreamConfiguration) {
                            ASSERT_EQ(Status::ILLEGAL_ARGUMENT, s);
                        });
            } else {
                ret = session->configureStreams(config3_2,
                        [](Status s, HalStreamConfiguration) {
                            ASSERT_EQ(Status::ILLEGAL_ARGUMENT, s);
                        });
            }
            ASSERT_TRUE(ret.isOk());

            stream3_2 = {streamId++,
                      StreamType::OUTPUT,
                      static_cast<uint32_t>(it.width),
                      static_cast<uint32_t>(it.height),
                      static_cast<PixelFormat>(it.format),
                      GRALLOC1_CONSUMER_USAGE_HWCOMPOSER,
                      0,
                      static_cast<StreamRotation>(UINT32_MAX)};
            streams[0] = stream3_2;
            createStreamConfiguration(streams, StreamConfigurationMode::NORMAL_MODE,
                    &config3_2, &config3_4, &config3_5, jpegBufferSize);
            if (session3_5 != nullptr) {
                config3_5.streamConfigCounter = streamConfigCounter++;
                ret = session3_5->configureStreams_3_5(config3_5,
                        [](Status s, device::V3_4::HalStreamConfiguration) {
                            ASSERT_EQ(Status::ILLEGAL_ARGUMENT, s);
                        });
            } else if(session3_4 != nullptr) {
                ret = session3_4->configureStreams_3_4(config3_4,
                        [](Status s, device::V3_4::HalStreamConfiguration) {
                            ASSERT_EQ(Status::ILLEGAL_ARGUMENT, s);
                        });
            } else if(session3_3 != nullptr) {
                ret = session3_3->configureStreams_3_3(config3_2,
                        [](Status s, device::V3_3::HalStreamConfiguration) {
                            ASSERT_EQ(Status::ILLEGAL_ARGUMENT, s);
                        });
            } else {
                ret = session->configureStreams(config3_2,
                        [](Status s, HalStreamConfiguration) {
                            ASSERT_EQ(Status::ILLEGAL_ARGUMENT, s);
                        });
            }
            ASSERT_TRUE(ret.isOk());
        }

        free_camera_metadata(staticMeta);
        ret = session->close();
        ASSERT_TRUE(ret.isOk());
    }
}

// Check whether all supported ZSL output stream combinations can be
// configured successfully.
TEST_F(CameraHidlTest, configureStreamsZSLInputOutputs) {
    hidl_vec<hidl_string> cameraDeviceNames = getCameraDeviceNames(mProvider);
    std::vector<AvailableStream> inputStreams;
    std::vector<AvailableZSLInputOutput> inputOutputMap;

    for (const auto& name : cameraDeviceNames) {
        int deviceVersion = getCameraDeviceVersion(name, mProviderType);
        if (deviceVersion == CAMERA_DEVICE_API_VERSION_1_0) {
            continue;
        } else if (deviceVersion <= 0) {
            ALOGE("%s: Unsupported device version %d", __func__, deviceVersion);
            ADD_FAILURE();
            return;
        }

        camera_metadata_t* staticMeta;
        Return<void> ret;
        sp<ICameraDeviceSession> session;
        sp<device::V3_3::ICameraDeviceSession> session3_3;
        sp<device::V3_4::ICameraDeviceSession> session3_4;
        sp<device::V3_5::ICameraDeviceSession> session3_5;
        sp<device::V3_2::ICameraDevice> cameraDevice;
        sp<device::V3_5::ICameraDevice> cameraDevice3_5;
        openEmptyDeviceSession(name, mProvider, &session /*out*/, &staticMeta /*out*/,
                &cameraDevice /*out*/);
        castSession(session, deviceVersion, &session3_3, &session3_4, &session3_5);
        castDevice(cameraDevice, deviceVersion, &cameraDevice3_5);

        Status rc = isZSLModeAvailable(staticMeta);
        if (Status::METHOD_NOT_SUPPORTED == rc) {
            ret = session->close();
            ASSERT_TRUE(ret.isOk());
            continue;
        }
        ASSERT_EQ(Status::OK, rc);

        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());

        bool supportMonoY8 = false;
        if (Status::OK == isMonochromeCamera(staticMeta)) {
            for (auto& it : inputStreams) {
                if (it.format == static_cast<uint32_t>(PixelFormat::Y8)) {
                    supportMonoY8 = true;
                    break;
                }
            }
        }

        uint32_t jpegBufferSize = 0;
        ASSERT_EQ(Status::OK, getJpegBufferSize(staticMeta, &jpegBufferSize));
        ASSERT_NE(0u, jpegBufferSize);

        int32_t streamId = 0;
        bool hasPrivToY8 = false, hasY8ToY8 = false, hasY8ToBlob = false;
        uint32_t streamConfigCounter = 0;
        for (auto& inputIter : inputOutputMap) {
            AvailableStream input;
            ASSERT_EQ(Status::OK, findLargestSize(inputStreams, inputIter.inputFormat,
                    input));
            ASSERT_NE(0u, inputStreams.size());

            if (inputIter.inputFormat == static_cast<uint32_t>(PixelFormat::IMPLEMENTATION_DEFINED)
                    && inputIter.outputFormat == static_cast<uint32_t>(PixelFormat::Y8)) {
                hasPrivToY8 = true;
            } else if (inputIter.inputFormat == static_cast<uint32_t>(PixelFormat::Y8)) {
                if (inputIter.outputFormat == static_cast<uint32_t>(PixelFormat::BLOB)) {
                    hasY8ToBlob = true;
                } else if (inputIter.outputFormat == static_cast<uint32_t>(PixelFormat::Y8)) {
                    hasY8ToY8 = true;
                }
            }
            AvailableStream outputThreshold = {INT32_MAX, INT32_MAX,
                                               inputIter.outputFormat};
            std::vector<AvailableStream> outputStreams;
            ASSERT_EQ(Status::OK,
                      getAvailableOutputStreams(staticMeta, outputStreams,
                              &outputThreshold));
            for (auto& outputIter : outputStreams) {
                V3_2::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};
                V3_2::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};
                V3_2::Stream outputStream = {streamId++,
                                       StreamType::OUTPUT,
                                       static_cast<uint32_t>(outputIter.width),
                                       static_cast<uint32_t>(outputIter.height),
                                       static_cast<PixelFormat>(outputIter.format),
                                       GRALLOC1_CONSUMER_USAGE_HWCOMPOSER,
                                       0,
                                       StreamRotation::ROTATION_0};

                ::android::hardware::hidl_vec<V3_2::Stream> streams = {inputStream, zslStream,
                                                                 outputStream};
                ::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);
                if (session3_5 != nullptr) {
                    verifyStreamCombination(cameraDevice3_5, config3_4,
                            /*expectedStatus*/ true, /*expectStreamCombQuery*/ false);
                    config3_5.streamConfigCounter = streamConfigCounter++;
                    ret = session3_5->configureStreams_3_5(config3_5,
                            [](Status s, device::V3_4::HalStreamConfiguration halConfig) {
                                ASSERT_EQ(Status::OK, s);
                                ASSERT_EQ(3u, halConfig.streams.size());
                            });
                } else if (session3_4 != nullptr) {
                    ret = session3_4->configureStreams_3_4(config3_4,
                            [](Status s, device::V3_4::HalStreamConfiguration halConfig) {
                                ASSERT_EQ(Status::OK, s);
                                ASSERT_EQ(3u, halConfig.streams.size());
                            });
                } else if (session3_3 != nullptr) {
                    ret = session3_3->configureStreams_3_3(config3_2,
                            [](Status s, device::V3_3::HalStreamConfiguration halConfig) {
                                ASSERT_EQ(Status::OK, s);
                                ASSERT_EQ(3u, halConfig.streams.size());
                            });
                } else {
                    ret = session->configureStreams(config3_2,
                            [](Status s, HalStreamConfiguration halConfig) {
                                ASSERT_EQ(Status::OK, s);
                                ASSERT_EQ(3u, halConfig.streams.size());
                            });
                }
                ASSERT_TRUE(ret.isOk());
            }
        }

        if (supportMonoY8) {
            if (Status::OK == isZSLModeAvailable(staticMeta, PRIV_REPROCESS)) {
                ASSERT_TRUE(hasPrivToY8);
            }
            if (Status::OK == isZSLModeAvailable(staticMeta, YUV_REPROCESS)) {
                ASSERT_TRUE(hasY8ToY8);
                ASSERT_TRUE(hasY8ToBlob);
            }
        }

        free_camera_metadata(staticMeta);
        ret = session->close();
        ASSERT_TRUE(ret.isOk());
    }
}

// Check whether session parameters are supported. If Hal support for them
// exist, then try to configure a preview stream using them.
TEST_F(CameraHidlTest, configureStreamsWithSessionParameters) {
    hidl_vec<hidl_string> cameraDeviceNames = getCameraDeviceNames(mProvider);
    std::vector<AvailableStream> outputPreviewStreams;
    AvailableStream previewThreshold = {kMaxPreviewWidth, kMaxPreviewHeight,
                                        static_cast<int32_t>(PixelFormat::IMPLEMENTATION_DEFINED)};

    for (const auto& name : cameraDeviceNames) {
        int deviceVersion = getCameraDeviceVersion(name, mProviderType);
        if (deviceVersion <= 0) {
            ALOGE("%s: Unsupported device version %d", __func__, deviceVersion);
            ADD_FAILURE();
            return;
        } else if (deviceVersion < CAMERA_DEVICE_API_VERSION_3_4) {
            continue;
        }

        camera_metadata_t* staticMetaBuffer;
        Return<void> ret;
        sp<ICameraDeviceSession> session;
        sp<device::V3_3::ICameraDeviceSession> session3_3;
        sp<device::V3_4::ICameraDeviceSession> session3_4;
        sp<device::V3_5::ICameraDeviceSession> session3_5;
        openEmptyDeviceSession(name, mProvider, &session /*out*/, &staticMetaBuffer /*out*/);
        castSession(session, deviceVersion, &session3_3, &session3_4, &session3_5);
        if (deviceVersion == CAMERA_DEVICE_API_VERSION_3_4) {
            ASSERT_NE(session3_4, nullptr);
        } else {
            ASSERT_NE(session3_5, nullptr);
        }

        std::unordered_set<int32_t> availableSessionKeys;
        auto rc = getSupportedKeys(staticMetaBuffer, ANDROID_REQUEST_AVAILABLE_SESSION_KEYS,
                &availableSessionKeys);
        ASSERT_TRUE(Status::OK == rc);
        if (availableSessionKeys.empty()) {
            free_camera_metadata(staticMetaBuffer);
            ret = session->close();
            ASSERT_TRUE(ret.isOk());
            continue;
        }

        android::hardware::camera::common::V1_0::helper::CameraMetadata previewRequestSettings;
        android::hardware::camera::common::V1_0::helper::CameraMetadata sessionParams,
                modifiedSessionParams;
        constructFilteredSettings(session, availableSessionKeys, RequestTemplate::PREVIEW,
                &previewRequestSettings, &sessionParams);
        if (sessionParams.isEmpty()) {
            free_camera_metadata(staticMetaBuffer);
            ret = session->close();
            ASSERT_TRUE(ret.isOk());
            continue;
        }

        outputPreviewStreams.clear();

        ASSERT_EQ(Status::OK, getAvailableOutputStreams(staticMetaBuffer, outputPreviewStreams,
                &previewThreshold));
        ASSERT_NE(0u, outputPreviewStreams.size());

        V3_4::Stream previewStream;
        previewStream.v3_2 = {0,
                                StreamType::OUTPUT,
                                static_cast<uint32_t>(outputPreviewStreams[0].width),
                                static_cast<uint32_t>(outputPreviewStreams[0].height),
                                static_cast<PixelFormat>(outputPreviewStreams[0].format),
                                GRALLOC1_CONSUMER_USAGE_HWCOMPOSER,
                                0,
                                StreamRotation::ROTATION_0};
        previewStream.bufferSize = 0;
        ::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;
        config.streams = streams;
        config.operationMode = StreamConfigurationMode::NORMAL_MODE;
        modifiedSessionParams = sessionParams;
        auto sessionParamsBuffer = sessionParams.release();
        config.sessionParams.setToExternal(reinterpret_cast<uint8_t *> (sessionParamsBuffer),
                get_camera_metadata_size(sessionParamsBuffer));
        config3_5.v3_4 = config;
        config3_5.streamConfigCounter = 0;
        if (session3_5 != nullptr) {
            bool newSessionParamsAvailable = false;
            for (const auto& it : availableSessionKeys) {
                if (modifiedSessionParams.exists(it)) {
                    modifiedSessionParams.erase(it);
                    newSessionParamsAvailable = true;
                    break;
                }
            }
            if (newSessionParamsAvailable) {
                auto modifiedSessionParamsBuffer = modifiedSessionParams.release();
                verifySessionReconfigurationQuery(session3_5, sessionParamsBuffer,
                        modifiedSessionParamsBuffer);
                modifiedSessionParams.acquire(modifiedSessionParamsBuffer);
            }

            ret = session3_5->configureStreams_3_5(config3_5,
                    [](Status s, device::V3_4::HalStreamConfiguration halConfig) {
                        ASSERT_EQ(Status::OK, s);
                        ASSERT_EQ(1u, halConfig.streams.size());
                    });
        } else {
            ret = session3_4->configureStreams_3_4(config,
                    [](Status s, device::V3_4::HalStreamConfiguration halConfig) {
                        ASSERT_EQ(Status::OK, s);
                        ASSERT_EQ(1u, halConfig.streams.size());
                    });
        }
        sessionParams.acquire(sessionParamsBuffer);
        ASSERT_TRUE(ret.isOk());

        free_camera_metadata(staticMetaBuffer);
        ret = session->close();
        ASSERT_TRUE(ret.isOk());
    }
}

// Verify that all supported preview + still capture stream combinations
// can be configured successfully.
TEST_F(CameraHidlTest, configureStreamsPreviewStillOutputs) {
    hidl_vec<hidl_string> cameraDeviceNames = getCameraDeviceNames(mProvider);
    std::vector<AvailableStream> outputBlobStreams;
    std::vector<AvailableStream> outputPreviewStreams;
    AvailableStream previewThreshold = {kMaxPreviewWidth, kMaxPreviewHeight,
                                        static_cast<int32_t>(PixelFormat::IMPLEMENTATION_DEFINED)};
    AvailableStream blobThreshold = {INT32_MAX, INT32_MAX,
                                     static_cast<int32_t>(PixelFormat::BLOB)};

    for (const auto& name : cameraDeviceNames) {
        int deviceVersion = getCameraDeviceVersion(name, mProviderType);
        if (deviceVersion == CAMERA_DEVICE_API_VERSION_1_0) {
            continue;
        } else if (deviceVersion <= 0) {
            ALOGE("%s: Unsupported device version %d", __func__, deviceVersion);
            ADD_FAILURE();
            return;
        }

        camera_metadata_t* staticMeta;
        Return<void> ret;
        sp<ICameraDeviceSession> session;
        sp<device::V3_3::ICameraDeviceSession> session3_3;
        sp<device::V3_4::ICameraDeviceSession> session3_4;
        sp<device::V3_5::ICameraDeviceSession> session3_5;
        sp<device::V3_2::ICameraDevice> cameraDevice;
        sp<device::V3_5::ICameraDevice> cameraDevice3_5;
        openEmptyDeviceSession(name, mProvider, &session /*out*/, &staticMeta /*out*/,
                &cameraDevice /*out*/);
        castSession(session, deviceVersion, &session3_3, &session3_4, &session3_5);
        castDevice(cameraDevice, deviceVersion, &cameraDevice3_5);

        // Check if camera support depth only
        if (isDepthOnly(staticMeta)) {
            free_camera_metadata(staticMeta);
            ret = session->close();
            ASSERT_TRUE(ret.isOk());
            continue;
        }

        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());

        uint32_t jpegBufferSize = 0;
        ASSERT_EQ(Status::OK, getJpegBufferSize(staticMeta, &jpegBufferSize));
        ASSERT_NE(0u, jpegBufferSize);

        int32_t streamId = 0;
        uint32_t streamConfigCounter = 0;
        for (auto& blobIter : outputBlobStreams) {
            for (auto& previewIter : outputPreviewStreams) {
                V3_2::Stream previewStream = {streamId++,
                                        StreamType::OUTPUT,
                                        static_cast<uint32_t>(previewIter.width),
                                        static_cast<uint32_t>(previewIter.height),
                                        static_cast<PixelFormat>(previewIter.format),
                                        GRALLOC1_CONSUMER_USAGE_HWCOMPOSER,
                                        0,
                                        StreamRotation::ROTATION_0};
                V3_2::Stream blobStream = {streamId++,
                                     StreamType::OUTPUT,
                                     static_cast<uint32_t>(blobIter.width),
                                     static_cast<uint32_t>(blobIter.height),
                                     static_cast<PixelFormat>(blobIter.format),
                                     GRALLOC1_CONSUMER_USAGE_CPU_READ,
                                     static_cast<V3_2::DataspaceFlags>(Dataspace::V0_JFIF),
                                     StreamRotation::ROTATION_0};
                ::android::hardware::hidl_vec<V3_2::Stream> streams = {previewStream,
                                                                 blobStream};
                ::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);
                if (session3_5 != nullptr) {
                    verifyStreamCombination(cameraDevice3_5, config3_4,
                            /*expectedStatus*/ true, /*expectStreamCombQuery*/ false);
                    config3_5.streamConfigCounter = streamConfigCounter++;
                    ret = session3_5->configureStreams_3_5(config3_5,
                            [](Status s, device::V3_4::HalStreamConfiguration halConfig) {
                                ASSERT_EQ(Status::OK, s);
                                ASSERT_EQ(2u, halConfig.streams.size());
                            });
                } else if (session3_4 != nullptr) {
                    ret = session3_4->configureStreams_3_4(config3_4,
                            [](Status s, device::V3_4::HalStreamConfiguration halConfig) {
                                ASSERT_EQ(Status::OK, s);
                                ASSERT_EQ(2u, halConfig.streams.size());
                            });
                } else if (session3_3 != nullptr) {
                    ret = session3_3->configureStreams_3_3(config3_2,
                            [](Status s, device::V3_3::HalStreamConfiguration halConfig) {
                                ASSERT_EQ(Status::OK, s);
                                ASSERT_EQ(2u, halConfig.streams.size());
                            });
                } else {
                    ret = session->configureStreams(config3_2,
                            [](Status s, HalStreamConfiguration halConfig) {
                                ASSERT_EQ(Status::OK, s);
                                ASSERT_EQ(2u, halConfig.streams.size());
                            });
                }
                ASSERT_TRUE(ret.isOk());
            }
        }

        free_camera_metadata(staticMeta);
        ret = session->close();
        ASSERT_TRUE(ret.isOk());
    }
}

// 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) {
    hidl_vec<hidl_string> cameraDeviceNames = getCameraDeviceNames(mProvider);

    for (const auto& name : cameraDeviceNames) {
        int deviceVersion = getCameraDeviceVersion(name, mProviderType);
        if (deviceVersion == CAMERA_DEVICE_API_VERSION_1_0) {
            continue;
        } else if (deviceVersion <= 0) {
            ALOGE("%s: Unsupported device version %d", __func__, deviceVersion);
            ADD_FAILURE();
            return;
        }

        camera_metadata_t* staticMeta;
        Return<void> ret;
        sp<ICameraDeviceSession> session;
        sp<device::V3_3::ICameraDeviceSession> session3_3;
        sp<device::V3_4::ICameraDeviceSession> session3_4;
        sp<device::V3_5::ICameraDeviceSession> session3_5;
        sp<device::V3_2::ICameraDevice> cameraDevice;
        sp<device::V3_5::ICameraDevice> cameraDevice3_5;
        openEmptyDeviceSession(name, mProvider, &session /*out*/, &staticMeta /*out*/,
                &cameraDevice /*out*/);
        castSession(session, deviceVersion, &session3_3, &session3_4, &session3_5);
        castDevice(cameraDevice, deviceVersion, &cameraDevice3_5);

        Status rc = isConstrainedModeAvailable(staticMeta);
        if (Status::METHOD_NOT_SUPPORTED == rc) {
            ret = session->close();
            ASSERT_TRUE(ret.isOk());
            continue;
        }
        ASSERT_EQ(Status::OK, rc);

        AvailableStream hfrStream;
        rc = pickConstrainedModeSize(staticMeta, hfrStream);
        ASSERT_EQ(Status::OK, rc);

        int32_t streamId = 0;
        uint32_t streamConfigCounter = 0;
        V3_2::Stream stream = {streamId,
                         StreamType::OUTPUT,
                         static_cast<uint32_t>(hfrStream.width),
                         static_cast<uint32_t>(hfrStream.height),
                         static_cast<PixelFormat>(hfrStream.format),
                         GRALLOC1_CONSUMER_USAGE_VIDEO_ENCODER,
                         0,
                         StreamRotation::ROTATION_0};
        ::android::hardware::hidl_vec<V3_2::Stream> streams = {stream};
        ::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);
        if (session3_5 != nullptr) {
            verifyStreamCombination(cameraDevice3_5, config3_4,
                    /*expectedStatus*/ true, /*expectStreamCombQuery*/ false);
            config3_5.streamConfigCounter = streamConfigCounter++;
            ret = session3_5->configureStreams_3_5(config3_5,
                    [streamId](Status s, device::V3_4::HalStreamConfiguration halConfig) {
                        ASSERT_EQ(Status::OK, s);
                        ASSERT_EQ(1u, halConfig.streams.size());
                        ASSERT_EQ(halConfig.streams[0].v3_3.v3_2.id, streamId);
                    });
        } else if (session3_4 != nullptr) {
            ret = session3_4->configureStreams_3_4(config3_4,
                    [streamId](Status s, device::V3_4::HalStreamConfiguration halConfig) {
                        ASSERT_EQ(Status::OK, s);
                        ASSERT_EQ(1u, halConfig.streams.size());
                        ASSERT_EQ(halConfig.streams[0].v3_3.v3_2.id, streamId);
                    });
        } else if (session3_3 != nullptr) {
            ret = session3_3->configureStreams_3_3(config3_2,
                    [streamId](Status s, device::V3_3::HalStreamConfiguration halConfig) {
                        ASSERT_EQ(Status::OK, s);
                        ASSERT_EQ(1u, halConfig.streams.size());
                        ASSERT_EQ(halConfig.streams[0].v3_2.id, streamId);
                    });
        } else {
            ret = session->configureStreams(config3_2,
                    [streamId](Status s, HalStreamConfiguration halConfig) {
                        ASSERT_EQ(Status::OK, s);
                        ASSERT_EQ(1u, halConfig.streams.size());
                        ASSERT_EQ(halConfig.streams[0].id, streamId);
                    });
        }
        ASSERT_TRUE(ret.isOk());

        stream = {streamId++,
                  StreamType::OUTPUT,
                  static_cast<uint32_t>(0),
                  static_cast<uint32_t>(0),
                  static_cast<PixelFormat>(hfrStream.format),
                  GRALLOC1_CONSUMER_USAGE_VIDEO_ENCODER,
                  0,
                  StreamRotation::ROTATION_0};
        streams[0] = stream;
        createStreamConfiguration(streams, StreamConfigurationMode::CONSTRAINED_HIGH_SPEED_MODE,
                                  &config3_2, &config3_4, &config3_5);
        if (session3_5 != nullptr) {
            config3_5.streamConfigCounter = streamConfigCounter++;
            ret = session3_5->configureStreams_3_5(config3_5,
                    [](Status s, device::V3_4::HalStreamConfiguration) {
                        ASSERT_TRUE((Status::ILLEGAL_ARGUMENT == s) ||
                                (Status::INTERNAL_ERROR == s));
                    });
        } else if (session3_4 != nullptr) {
            ret = session3_4->configureStreams_3_4(config3_4,
                    [](Status s, device::V3_4::HalStreamConfiguration) {
                        ASSERT_TRUE((Status::ILLEGAL_ARGUMENT == s) ||
                                (Status::INTERNAL_ERROR == s));
                    });
        } 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) ||
                                (Status::INTERNAL_ERROR == s));
                    });
        } else {
            ret = session->configureStreams(config3_2,
                    [](Status s, HalStreamConfiguration) {
                        ASSERT_TRUE((Status::ILLEGAL_ARGUMENT == s) ||
                                (Status::INTERNAL_ERROR == s));
                    });
        }
        ASSERT_TRUE(ret.isOk());

        stream = {streamId++,
                  StreamType::OUTPUT,
                  static_cast<uint32_t>(UINT32_MAX),
                  static_cast<uint32_t>(UINT32_MAX),
                  static_cast<PixelFormat>(hfrStream.format),
                  GRALLOC1_CONSUMER_USAGE_VIDEO_ENCODER,
                  0,
                  StreamRotation::ROTATION_0};
        streams[0] = stream;
        createStreamConfiguration(streams, StreamConfigurationMode::CONSTRAINED_HIGH_SPEED_MODE,
                                  &config3_2, &config3_4, &config3_5);
        if (session3_5 != nullptr) {
            config3_5.streamConfigCounter = streamConfigCounter++;
            ret = session3_5->configureStreams_3_5(config3_5,
                    [](Status s, device::V3_4::HalStreamConfiguration) {
                        ASSERT_EQ(Status::ILLEGAL_ARGUMENT, s);
                    });
        } else if (session3_4 != nullptr) {
            ret = session3_4->configureStreams_3_4(config3_4,
                    [](Status s, device::V3_4::HalStreamConfiguration) {
                        ASSERT_EQ(Status::ILLEGAL_ARGUMENT, s);
                    });
        } else if (session3_3 != nullptr) {
            ret = session3_3->configureStreams_3_3(config3_2,
                    [](Status s, device::V3_3::HalStreamConfiguration) {
                        ASSERT_EQ(Status::ILLEGAL_ARGUMENT, s);
                    });
        } else {
            ret = session->configureStreams(config3_2,
                    [](Status s, HalStreamConfiguration) {
                        ASSERT_EQ(Status::ILLEGAL_ARGUMENT, s);
                    });
        }
        ASSERT_TRUE(ret.isOk());

        stream = {streamId++,
                  StreamType::OUTPUT,
                  static_cast<uint32_t>(hfrStream.width),
                  static_cast<uint32_t>(hfrStream.height),
                  static_cast<PixelFormat>(UINT32_MAX),
                  GRALLOC1_CONSUMER_USAGE_VIDEO_ENCODER,
                  0,
                  StreamRotation::ROTATION_0};
        streams[0] = stream;
        createStreamConfiguration(streams, StreamConfigurationMode::CONSTRAINED_HIGH_SPEED_MODE,
                                  &config3_2, &config3_4, &config3_5);
        if (session3_5 != nullptr) {
            config3_5.streamConfigCounter = streamConfigCounter++;
            ret = session3_5->configureStreams_3_5(config3_5,
                    [](Status s, device::V3_4::HalStreamConfiguration) {
                        ASSERT_EQ(Status::ILLEGAL_ARGUMENT, s);
                    });
        } else if (session3_4 != nullptr) {
            ret = session3_4->configureStreams_3_4(config3_4,
                    [](Status s, device::V3_4::HalStreamConfiguration) {
                        ASSERT_EQ(Status::ILLEGAL_ARGUMENT, s);
                    });
        } else if (session3_3 != nullptr) {
            ret = session3_3->configureStreams_3_3(config3_2,
                    [](Status s, device::V3_3::HalStreamConfiguration) {
                        ASSERT_EQ(Status::ILLEGAL_ARGUMENT, s);
                    });
        } else {
            ret = session->configureStreams(config3_2,
                    [](Status s, HalStreamConfiguration) {
                        ASSERT_EQ(Status::ILLEGAL_ARGUMENT, s);
                    });
        }
        ASSERT_TRUE(ret.isOk());

        free_camera_metadata(staticMeta);
        ret = session->close();
        ASSERT_TRUE(ret.isOk());
    }
}

// Verify that all supported video + snapshot stream combinations can
// be configured successfully.
TEST_F(CameraHidlTest, configureStreamsVideoStillOutputs) {
    hidl_vec<hidl_string> cameraDeviceNames = getCameraDeviceNames(mProvider);
    std::vector<AvailableStream> outputBlobStreams;
    std::vector<AvailableStream> outputVideoStreams;
    AvailableStream videoThreshold = {kMaxVideoWidth, kMaxVideoHeight,
                                      static_cast<int32_t>(PixelFormat::IMPLEMENTATION_DEFINED)};
    AvailableStream blobThreshold = {kMaxVideoWidth, kMaxVideoHeight,
                                     static_cast<int32_t>(PixelFormat::BLOB)};

    for (const auto& name : cameraDeviceNames) {
        int deviceVersion = getCameraDeviceVersion(name, mProviderType);
        if (deviceVersion == CAMERA_DEVICE_API_VERSION_1_0) {
            continue;
        } else if (deviceVersion <= 0) {
            ALOGE("%s: Unsupported device version %d", __func__, deviceVersion);
            ADD_FAILURE();
            return;
        }

        camera_metadata_t* staticMeta;
        Return<void> ret;
        sp<ICameraDeviceSession> session;
        sp<device::V3_3::ICameraDeviceSession> session3_3;
        sp<device::V3_4::ICameraDeviceSession> session3_4;
        sp<device::V3_5::ICameraDeviceSession> session3_5;
        sp<device::V3_2::ICameraDevice> cameraDevice;
        sp<device::V3_5::ICameraDevice> cameraDevice3_5;
        openEmptyDeviceSession(name, mProvider, &session /*out*/, &staticMeta /*out*/,
                &cameraDevice /*out*/);
        castSession(session, deviceVersion, &session3_3, &session3_4, &session3_5);
        castDevice(cameraDevice, deviceVersion, &cameraDevice3_5);

        // Check if camera support depth only
        if (isDepthOnly(staticMeta)) {
            free_camera_metadata(staticMeta);
            ret = session->close();
            ASSERT_TRUE(ret.isOk());
            continue;
        }

        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());

        uint32_t jpegBufferSize = 0;
        ASSERT_EQ(Status::OK, getJpegBufferSize(staticMeta, &jpegBufferSize));
        ASSERT_NE(0u, jpegBufferSize);

        int32_t streamId = 0;
        uint32_t streamConfigCounter = 0;
        for (auto& blobIter : outputBlobStreams) {
            for (auto& videoIter : outputVideoStreams) {
                V3_2::Stream videoStream = {streamId++,
                                      StreamType::OUTPUT,
                                      static_cast<uint32_t>(videoIter.width),
                                      static_cast<uint32_t>(videoIter.height),
                                      static_cast<PixelFormat>(videoIter.format),
                                      GRALLOC1_CONSUMER_USAGE_VIDEO_ENCODER,
                                      0,
                                      StreamRotation::ROTATION_0};
                V3_2::Stream blobStream = {streamId++,
                                     StreamType::OUTPUT,
                                     static_cast<uint32_t>(blobIter.width),
                                     static_cast<uint32_t>(blobIter.height),
                                     static_cast<PixelFormat>(blobIter.format),
                                     GRALLOC1_CONSUMER_USAGE_CPU_READ,
                                     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_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);
                if (session3_5 != nullptr) {
                    verifyStreamCombination(cameraDevice3_5, config3_4,
                            /*expectedStatus*/ true, /*expectStreamCombQuery*/ false);
                    config3_5.streamConfigCounter = streamConfigCounter++;
                    ret = session3_5->configureStreams_3_5(config3_5,
                            [](Status s, device::V3_4::HalStreamConfiguration halConfig) {
                                ASSERT_EQ(Status::OK, s);
                                ASSERT_EQ(2u, halConfig.streams.size());
                            });
                } else if (session3_4 != nullptr) {
                    ret = session3_4->configureStreams_3_4(config3_4,
                            [](Status s, device::V3_4::HalStreamConfiguration halConfig) {
                                ASSERT_EQ(Status::OK, s);
                                ASSERT_EQ(2u, halConfig.streams.size());
                            });
                } else if (session3_3 != nullptr) {
                    ret = session3_3->configureStreams_3_3(config3_2,
                            [](Status s, device::V3_3::HalStreamConfiguration halConfig) {
                                ASSERT_EQ(Status::OK, s);
                                ASSERT_EQ(2u, halConfig.streams.size());
                            });
                } else {
                    ret = session->configureStreams(config3_2,
                            [](Status s, HalStreamConfiguration halConfig) {
                                ASSERT_EQ(Status::OK, s);
                                ASSERT_EQ(2u, halConfig.streams.size());
                            });
                }
                ASSERT_TRUE(ret.isOk());
            }
        }

        free_camera_metadata(staticMeta);
        ret = session->close();
        ASSERT_TRUE(ret.isOk());
    }
}

// Generate and verify a camera capture request
TEST_F(CameraHidlTest, processCaptureRequestPreview) {
    hidl_vec<hidl_string> cameraDeviceNames = getCameraDeviceNames(mProvider);
    AvailableStream previewThreshold = {kMaxPreviewWidth, kMaxPreviewHeight,
                                        static_cast<int32_t>(PixelFormat::IMPLEMENTATION_DEFINED)};
    uint64_t bufferId = 1;
    uint32_t frameNumber = 1;
    ::android::hardware::hidl_vec<uint8_t> settings;

    for (const auto& name : cameraDeviceNames) {
        int deviceVersion = getCameraDeviceVersion(name, mProviderType);
        if (deviceVersion == CAMERA_DEVICE_API_VERSION_1_0) {
            continue;
        } else if (deviceVersion <= 0) {
            ALOGE("%s: Unsupported device version %d", __func__, deviceVersion);
            ADD_FAILURE();
            return;
        }

        V3_2::Stream previewStream;
        HalStreamConfiguration halStreamConfig;
        sp<ICameraDeviceSession> session;
        sp<DeviceCb> cb;
        bool supportsPartialResults = false;
        bool useHalBufManager = false;
        uint32_t partialResultCount = 0;
        configurePreviewStream(name, deviceVersion, mProvider, &previewThreshold, &session /*out*/,
                &previewStream /*out*/, &halStreamConfig /*out*/,
                &supportsPartialResults /*out*/,
                &partialResultCount /*out*/, &useHalBufManager /*out*/, &cb /*out*/);

        std::shared_ptr<ResultMetadataQueue> resultQueue;
        auto resultQueueRet =
            session->getCaptureResultMetadataQueue(
                [&resultQueue](const auto& descriptor) {
                    resultQueue = std::make_shared<ResultMetadataQueue>(
                            descriptor);
                    if (!resultQueue->isValid() ||
                            resultQueue->availableToWrite() <= 0) {
                        ALOGE("%s: HAL returns empty result metadata fmq,"
                                " not use it", __func__);
                        resultQueue = nullptr;
                        // Don't use the queue onwards.
                    }
                });
        ASSERT_TRUE(resultQueueRet.isOk());

        InFlightRequest inflightReq = {1, false, supportsPartialResults,
                                       partialResultCount, resultQueue};

        RequestTemplate reqTemplate = RequestTemplate::PREVIEW;
        Return<void> ret;
        ret = session->constructDefaultRequestSettings(reqTemplate,
                                                       [&](auto status, const auto& req) {
                                                           ASSERT_EQ(Status::OK, status);
                                                           settings = req;
                                                       });
        ASSERT_TRUE(ret.isOk());

        hidl_handle buffer_handle;
        StreamBuffer outputBuffer;
        if (useHalBufManager) {
            outputBuffer = {halStreamConfig.streams[0].id,
                            /*bufferId*/ 0,
                            buffer_handle,
                            BufferStatus::OK,
                            nullptr,
                            nullptr};
        } else {
            allocateGraphicBuffer(previewStream.width, previewStream.height,
                    android_convertGralloc1To0Usage(halStreamConfig.streams[0].producerUsage,
                        halStreamConfig.streams[0].consumerUsage),
                    halStreamConfig.streams[0].overrideFormat, &buffer_handle);
            outputBuffer = {halStreamConfig.streams[0].id,
                            bufferId,
                            buffer_handle,
                            BufferStatus::OK,
                            nullptr,
                            nullptr};
        }
        ::android::hardware::hidl_vec<StreamBuffer> outputBuffers = {outputBuffer};
        StreamBuffer emptyInputBuffer = {-1, 0, nullptr, BufferStatus::ERROR, nullptr,
                                         nullptr};
        CaptureRequest request = {frameNumber, 0 /* fmqSettingsSize */, settings,
                                  emptyInputBuffer, outputBuffers};

        {
            std::unique_lock<std::mutex> l(mLock);
            mInflightMap.clear();
            mInflightMap.add(frameNumber, &inflightReq);
        }

        Status status = Status::INTERNAL_ERROR;
        uint32_t numRequestProcessed = 0;
        hidl_vec<BufferCache> cachesToRemove;
        Return<void> returnStatus = session->processCaptureRequest(
            {request}, cachesToRemove, [&status, &numRequestProcessed](auto s,
                    uint32_t n) {
                status = s;
                numRequestProcessed = n;
            });
        ASSERT_TRUE(returnStatus.isOk());
        ASSERT_EQ(Status::OK, status);
        ASSERT_EQ(numRequestProcessed, 1u);

        {
            std::unique_lock<std::mutex> l(mLock);
            while (!inflightReq.errorCodeValid &&
                   ((0 < inflightReq.numBuffersLeft) ||
                           (!inflightReq.haveResultMetadata))) {
                auto timeout = std::chrono::system_clock::now() +
                               std::chrono::seconds(kStreamBufferTimeoutSec);
                ASSERT_NE(std::cv_status::timeout,
                        mResultCondition.wait_until(l, timeout));
            }

            ASSERT_FALSE(inflightReq.errorCodeValid);
            ASSERT_NE(inflightReq.resultOutputBuffers.size(), 0u);
            ASSERT_EQ(previewStream.id, inflightReq.resultOutputBuffers[0].streamId);

            request.frameNumber++;
            // Empty settings should be supported after the first call
            // for repeating requests.
            request.settings.setToExternal(nullptr, 0, true);
            // The buffer has been registered to HAL by bufferId, so per
            // API contract we should send a null handle for this buffer
            request.outputBuffers[0].buffer = nullptr;
            mInflightMap.clear();
            inflightReq = {1, false, supportsPartialResults, partialResultCount,
                           resultQueue};
            mInflightMap.add(request.frameNumber, &inflightReq);
        }

        returnStatus = session->processCaptureRequest(
            {request}, cachesToRemove, [&status, &numRequestProcessed](auto s,
                    uint32_t n) {
                status = s;
                numRequestProcessed = n;
            });
        ASSERT_TRUE(returnStatus.isOk());
        ASSERT_EQ(Status::OK, status);
        ASSERT_EQ(numRequestProcessed, 1u);

        {
            std::unique_lock<std::mutex> l(mLock);
            while (!inflightReq.errorCodeValid &&
                   ((0 < inflightReq.numBuffersLeft) ||
                           (!inflightReq.haveResultMetadata))) {
                auto timeout = std::chrono::system_clock::now() +
                               std::chrono::seconds(kStreamBufferTimeoutSec);
                ASSERT_NE(std::cv_status::timeout,
                        mResultCondition.wait_until(l, timeout));
            }

            ASSERT_FALSE(inflightReq.errorCodeValid);
            ASSERT_NE(inflightReq.resultOutputBuffers.size(), 0u);
            ASSERT_EQ(previewStream.id, inflightReq.resultOutputBuffers[0].streamId);
        }

        if (useHalBufManager) {
            verifyBuffersReturned(session, deviceVersion, previewStream.id, cb);
        }

        ret = session->close();
        ASSERT_TRUE(ret.isOk());
    }
}

// Generate and verify a multi-camera capture request
TEST_F(CameraHidlTest, processMultiCaptureRequestPreview) {
    hidl_vec<hidl_string> cameraDeviceNames = getCameraDeviceNames(mProvider);
    AvailableStream previewThreshold = {kMaxPreviewWidth, kMaxPreviewHeight,
                                        static_cast<int32_t>(PixelFormat::YCBCR_420_888)};
    uint64_t bufferId = 1;
    uint32_t frameNumber = 1;
    ::android::hardware::hidl_vec<uint8_t> settings;
    ::android::hardware::hidl_vec<uint8_t> emptySettings;
    hidl_string invalidPhysicalId = "-1";

    for (const auto& name : cameraDeviceNames) {
        int deviceVersion = getCameraDeviceVersion(name, mProviderType);
        if (deviceVersion < CAMERA_DEVICE_API_VERSION_3_5) {
            continue;
        }
        std::string version, deviceId;
        ASSERT_TRUE(::matchDeviceName(name, mProviderType, &version, &deviceId));
        camera_metadata_t* staticMeta;
        Return<void> ret;
        sp<ICameraDeviceSession> session;
        openEmptyDeviceSession(name, mProvider, &session /*out*/, &staticMeta /*out*/);

        Status rc = isLogicalMultiCamera(staticMeta);
        if (Status::METHOD_NOT_SUPPORTED == rc) {
            free_camera_metadata(staticMeta);
            ret = session->close();
            ASSERT_TRUE(ret.isOk());
            continue;
        }
        std::unordered_set<std::string> physicalIds;
        rc = getPhysicalCameraIds(staticMeta, &physicalIds);
        ASSERT_TRUE(Status::OK == rc);
        ASSERT_TRUE(physicalIds.size() > 1);

        std::unordered_set<int32_t> physicalRequestKeyIDs;
        rc = getSupportedKeys(staticMeta,
                ANDROID_REQUEST_AVAILABLE_PHYSICAL_CAMERA_REQUEST_KEYS, &physicalRequestKeyIDs);
        ASSERT_TRUE(Status::OK == rc);
        if (physicalRequestKeyIDs.empty()) {
            free_camera_metadata(staticMeta);
            ret = session->close();
            ASSERT_TRUE(ret.isOk());
            // The logical camera doesn't support any individual physical requests.
            continue;
        }

        android::hardware::camera::common::V1_0::helper::CameraMetadata defaultPreviewSettings;
        android::hardware::camera::common::V1_0::helper::CameraMetadata filteredSettings;
        constructFilteredSettings(session, physicalRequestKeyIDs, RequestTemplate::PREVIEW,
                &defaultPreviewSettings, &filteredSettings);
        if (filteredSettings.isEmpty()) {
            // No physical device settings in default request.
            free_camera_metadata(staticMeta);
            ret = session->close();
            ASSERT_TRUE(ret.isOk());
            continue;
        }

        const camera_metadata_t *settingsBuffer = defaultPreviewSettings.getAndLock();
        settings.setToExternal(
                reinterpret_cast<uint8_t *> (const_cast<camera_metadata_t *> (settingsBuffer)),
                get_camera_metadata_size(settingsBuffer));

        free_camera_metadata(staticMeta);
        ret = session->close();
        ASSERT_TRUE(ret.isOk());

        // Leave only 2 physical devices in the id set.
        auto it = physicalIds.begin();
        string physicalDeviceId = *it; it++;
        physicalIds.erase(++it, physicalIds.end());
        ASSERT_EQ(physicalIds.size(), 2u);

        V3_4::HalStreamConfiguration halStreamConfig;
        bool supportsPartialResults = false;
        bool useHalBufManager = false;
        uint32_t partialResultCount = 0;
        V3_2::Stream previewStream;
        sp<device::V3_4::ICameraDeviceSession> session3_4;
        sp<device::V3_5::ICameraDeviceSession> session3_5;
        sp<DeviceCb> cb;
        configurePreviewStreams3_4(name, deviceVersion, mProvider, &previewThreshold, physicalIds,
                &session3_4, &session3_5, &previewStream, &halStreamConfig /*out*/,
                &supportsPartialResults /*out*/, &partialResultCount /*out*/,
                &useHalBufManager /*out*/, &cb /*out*/, 0 /*streamConfigCounter*/,
                true /*allowUnsupport*/);
        if (session3_5 == nullptr) {
            ret = session3_4->close();
            ASSERT_TRUE(ret.isOk());
            continue;
        }

        std::shared_ptr<ResultMetadataQueue> resultQueue;
        auto resultQueueRet =
            session3_4->getCaptureResultMetadataQueue(
                [&resultQueue](const auto& descriptor) {
                    resultQueue = std::make_shared<ResultMetadataQueue>(
                            descriptor);
                    if (!resultQueue->isValid() ||
                            resultQueue->availableToWrite() <= 0) {
                        ALOGE("%s: HAL returns empty result metadata fmq,"
                                " not use it", __func__);
                        resultQueue = nullptr;
                        // Don't use the queue onwards.
                    }
                });
        ASSERT_TRUE(resultQueueRet.isOk());

        InFlightRequest inflightReq = {static_cast<ssize_t> (halStreamConfig.streams.size()), false,
            supportsPartialResults, partialResultCount, resultQueue};

        std::vector<hidl_handle> graphicBuffers;
        graphicBuffers.reserve(halStreamConfig.streams.size());
        ::android::hardware::hidl_vec<StreamBuffer> outputBuffers;
        outputBuffers.resize(halStreamConfig.streams.size());
        size_t k = 0;
        for (const auto& halStream : halStreamConfig.streams) {
            hidl_handle buffer_handle;
            if (useHalBufManager) {
                outputBuffers[k] = {halStream.v3_3.v3_2.id, /*bufferId*/0, buffer_handle,
                    BufferStatus::OK, nullptr, nullptr};
            } else {
                allocateGraphicBuffer(previewStream.width, previewStream.height,
                        android_convertGralloc1To0Usage(halStream.v3_3.v3_2.producerUsage,
                            halStream.v3_3.v3_2.consumerUsage),
                        halStream.v3_3.v3_2.overrideFormat, &buffer_handle);
                graphicBuffers.push_back(buffer_handle);
                outputBuffers[k] = {halStream.v3_3.v3_2.id, bufferId, buffer_handle,
                    BufferStatus::OK, nullptr, nullptr};
                bufferId++;
            }
            k++;
        }
        hidl_vec<V3_4::PhysicalCameraSetting> camSettings(1);
        const camera_metadata_t *filteredSettingsBuffer = filteredSettings.getAndLock();
        camSettings[0].settings.setToExternal(
                reinterpret_cast<uint8_t *> (const_cast<camera_metadata_t *> (
                        filteredSettingsBuffer)),
                get_camera_metadata_size(filteredSettingsBuffer));
        camSettings[0].fmqSettingsSize = 0;
        camSettings[0].physicalCameraId = physicalDeviceId;

        StreamBuffer emptyInputBuffer = {-1, 0, nullptr, BufferStatus::ERROR, nullptr, nullptr};
        V3_4::CaptureRequest request = {{frameNumber, 0 /* fmqSettingsSize */, settings,
                                  emptyInputBuffer, outputBuffers}, camSettings};

        {
            std::unique_lock<std::mutex> l(mLock);
            mInflightMap.clear();
            mInflightMap.add(frameNumber, &inflightReq);
        }

        Status stat = Status::INTERNAL_ERROR;
        uint32_t numRequestProcessed = 0;
        hidl_vec<BufferCache> cachesToRemove;
        Return<void> returnStatus = session3_4->processCaptureRequest_3_4(
            {request}, cachesToRemove, [&stat, &numRequestProcessed](auto s, uint32_t n) {
                stat = s;
                numRequestProcessed = n;
            });
        ASSERT_TRUE(returnStatus.isOk());
        ASSERT_EQ(Status::OK, stat);
        ASSERT_EQ(numRequestProcessed, 1u);

        {
            std::unique_lock<std::mutex> l(mLock);
            while (!inflightReq.errorCodeValid &&
                    ((0 < inflightReq.numBuffersLeft) ||
                     (!inflightReq.haveResultMetadata))) {
                auto timeout = std::chrono::system_clock::now() +
                    std::chrono::seconds(kStreamBufferTimeoutSec);
                ASSERT_NE(std::cv_status::timeout,
                        mResultCondition.wait_until(l, timeout));
            }

            ASSERT_FALSE(inflightReq.errorCodeValid);
            ASSERT_NE(inflightReq.resultOutputBuffers.size(), 0u);

            request.v3_2.frameNumber++;
            // Empty settings should be supported after the first call
            // for repeating requests.
            request.v3_2.settings.setToExternal(nullptr, 0, true);
            request.physicalCameraSettings[0].settings.setToExternal(nullptr, 0, true);
            // The buffer has been registered to HAL by bufferId, so per
            // API contract we should send a null handle for this buffer
            request.v3_2.outputBuffers[0].buffer = nullptr;
            mInflightMap.clear();
            inflightReq = {static_cast<ssize_t> (physicalIds.size()), false,
                supportsPartialResults, partialResultCount, resultQueue};
            mInflightMap.add(request.v3_2.frameNumber, &inflightReq);
        }

        returnStatus = session3_4->processCaptureRequest_3_4(
            {request}, cachesToRemove, [&stat, &numRequestProcessed](auto s, uint32_t n) {
                stat = s;
                numRequestProcessed = n;
            });
        ASSERT_TRUE(returnStatus.isOk());
        ASSERT_EQ(Status::OK, stat);
        ASSERT_EQ(numRequestProcessed, 1u);

        {
            std::unique_lock<std::mutex> l(mLock);
            while (!inflightReq.errorCodeValid &&
                    ((0 < inflightReq.numBuffersLeft) ||
                     (!inflightReq.haveResultMetadata))) {
                auto timeout = std::chrono::system_clock::now() +
                    std::chrono::seconds(kStreamBufferTimeoutSec);
                ASSERT_NE(std::cv_status::timeout,
                        mResultCondition.wait_until(l, timeout));
            }

            ASSERT_FALSE(inflightReq.errorCodeValid);
            ASSERT_NE(inflightReq.resultOutputBuffers.size(), 0u);
        }

        // Invalid physical camera id should fail process requests
        frameNumber++;
        camSettings[0].physicalCameraId = invalidPhysicalId;
        camSettings[0].settings = settings;
        request = {{frameNumber, 0 /* fmqSettingsSize */, settings,
            emptyInputBuffer, outputBuffers}, camSettings};
        returnStatus = session3_4->processCaptureRequest_3_4(
            {request}, cachesToRemove, [&stat, &numRequestProcessed](auto s, uint32_t n) {
                stat = s;
                numRequestProcessed = n;
            });
        ASSERT_TRUE(returnStatus.isOk());
        ASSERT_EQ(Status::ILLEGAL_ARGUMENT, stat);

        defaultPreviewSettings.unlock(settingsBuffer);
        filteredSettings.unlock(filteredSettingsBuffer);

        if (useHalBufManager) {
            hidl_vec<int32_t> streamIds(halStreamConfig.streams.size());
            for (size_t i = 0; i < streamIds.size(); i++) {
                streamIds[i] = halStreamConfig.streams[i].v3_3.v3_2.id;
            }
            verifyBuffersReturned(session3_4, streamIds, cb);
        }

        ret = session3_4->close();
        ASSERT_TRUE(ret.isOk());
    }
}

// Generate and verify a burst containing alternating sensor sensitivity values
TEST_F(CameraHidlTest, processCaptureRequestBurstISO) {
    hidl_vec<hidl_string> cameraDeviceNames = getCameraDeviceNames(mProvider);
    AvailableStream previewThreshold = {kMaxPreviewWidth, kMaxPreviewHeight,
                                        static_cast<int32_t>(PixelFormat::IMPLEMENTATION_DEFINED)};
    uint64_t bufferId = 1;
    uint32_t frameNumber = 1;
    float isoTol = .03f;
    ::android::hardware::hidl_vec<uint8_t> settings;

    for (const auto& name : cameraDeviceNames) {
        int deviceVersion = getCameraDeviceVersion(name, mProviderType);
        if (deviceVersion == CAMERA_DEVICE_API_VERSION_1_0) {
            continue;
        } else if (deviceVersion <= 0) {
            ALOGE("%s: Unsupported device version %d", __func__, deviceVersion);
            ADD_FAILURE();
            return;
        }
        camera_metadata_t* staticMetaBuffer;
        Return<void> ret;
        sp<ICameraDeviceSession> session;
        openEmptyDeviceSession(name, mProvider, &session /*out*/, &staticMetaBuffer /*out*/);
        ::android::hardware::camera::common::V1_0::helper::CameraMetadata staticMeta(
                staticMetaBuffer);

        camera_metadata_entry_t hwLevel = staticMeta.find(ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL);
        ASSERT_TRUE(0 < hwLevel.count);
        if (ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED == hwLevel.data.u8[0] ||
                ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL_EXTERNAL == hwLevel.data.u8[0]) {
            //Limited/External devices can skip this test
            ret = session->close();
            ASSERT_TRUE(ret.isOk());
            continue;
        }

        camera_metadata_entry_t isoRange = staticMeta.find(ANDROID_SENSOR_INFO_SENSITIVITY_RANGE);
        ASSERT_EQ(isoRange.count, 2u);

        ret = session->close();
        ASSERT_TRUE(ret.isOk());

        bool supportsPartialResults = false;
        bool useHalBufManager = false;
        uint32_t partialResultCount = 0;
        V3_2::Stream previewStream;
        HalStreamConfiguration halStreamConfig;
        sp<DeviceCb> cb;
        configurePreviewStream(name, deviceVersion, mProvider, &previewThreshold,
                &session /*out*/, &previewStream /*out*/, &halStreamConfig /*out*/,
                &supportsPartialResults /*out*/, &partialResultCount /*out*/,
                &useHalBufManager /*out*/, &cb /*out*/);
        std::shared_ptr<ResultMetadataQueue> resultQueue;

        auto resultQueueRet = session->getCaptureResultMetadataQueue(
            [&resultQueue](const auto& descriptor) {
                resultQueue = std::make_shared<ResultMetadataQueue>(descriptor);
                if (!resultQueue->isValid() || resultQueue->availableToWrite() <= 0) {
                    ALOGE("%s: HAL returns empty result metadata fmq,"
                            " not use it", __func__);
                    resultQueue = nullptr;
                    // Don't use the queue onwards.
                }
            });
        ASSERT_TRUE(resultQueueRet.isOk());
        ASSERT_NE(nullptr, resultQueue);

        ret = session->constructDefaultRequestSettings(RequestTemplate::PREVIEW,
            [&](auto status, const auto& req) {
                ASSERT_EQ(Status::OK, status);
                settings = req; });
        ASSERT_TRUE(ret.isOk());

        ::android::hardware::camera::common::V1_0::helper::CameraMetadata requestMeta;
        StreamBuffer emptyInputBuffer = {-1, 0, nullptr, BufferStatus::ERROR, nullptr, nullptr};
        hidl_handle buffers[kBurstFrameCount];
        StreamBuffer outputBuffers[kBurstFrameCount];
        CaptureRequest requests[kBurstFrameCount];
        InFlightRequest inflightReqs[kBurstFrameCount];
        int32_t isoValues[kBurstFrameCount];
        hidl_vec<uint8_t> requestSettings[kBurstFrameCount];
        for (uint32_t i = 0; i < kBurstFrameCount; i++) {
            std::unique_lock<std::mutex> l(mLock);

            isoValues[i] = ((i % 2) == 0) ? isoRange.data.i32[0] : isoRange.data.i32[1];
            if (useHalBufManager) {
                outputBuffers[i] = {halStreamConfig.streams[0].id, /*bufferId*/0,
                    nullptr, BufferStatus::OK, nullptr, nullptr};
            } else {
                allocateGraphicBuffer(previewStream.width, previewStream.height,
                        android_convertGralloc1To0Usage(halStreamConfig.streams[0].producerUsage,
                            halStreamConfig.streams[0].consumerUsage),
                        halStreamConfig.streams[0].overrideFormat, &buffers[i]);
                outputBuffers[i] = {halStreamConfig.streams[0].id, bufferId + i,
                    buffers[i], BufferStatus::OK, nullptr, nullptr};
            }

            requestMeta.append(reinterpret_cast<camera_metadata_t *> (settings.data()));

            // Disable all 3A routines
            uint8_t mode = static_cast<uint8_t>(ANDROID_CONTROL_MODE_OFF);
            ASSERT_EQ(::android::OK, requestMeta.update(ANDROID_CONTROL_MODE, &mode, 1));
            ASSERT_EQ(::android::OK, requestMeta.update(ANDROID_SENSOR_SENSITIVITY, &isoValues[i],
                        1));
            camera_metadata_t *metaBuffer = requestMeta.release();
            requestSettings[i].setToExternal(reinterpret_cast<uint8_t *> (metaBuffer),
                    get_camera_metadata_size(metaBuffer), true);

            requests[i] = {frameNumber + i, 0 /* fmqSettingsSize */, requestSettings[i],
                emptyInputBuffer, {outputBuffers[i]}};

            inflightReqs[i] = {1, false, supportsPartialResults, partialResultCount, resultQueue};
            mInflightMap.add(frameNumber + i, &inflightReqs[i]);
        }

        Status status = Status::INTERNAL_ERROR;
        uint32_t numRequestProcessed = 0;
        hidl_vec<BufferCache> cachesToRemove;
        hidl_vec<CaptureRequest> burstRequest;
        burstRequest.setToExternal(requests, kBurstFrameCount);
        Return<void> returnStatus = session->processCaptureRequest(burstRequest, cachesToRemove,
                [&status, &numRequestProcessed] (auto s, uint32_t n) {
                    status = s;
                    numRequestProcessed = n;
                });
        ASSERT_TRUE(returnStatus.isOk());
        ASSERT_EQ(Status::OK, status);
        ASSERT_EQ(numRequestProcessed, kBurstFrameCount);

        for (size_t i = 0; i < kBurstFrameCount; i++) {
            std::unique_lock<std::mutex> l(mLock);
            while (!inflightReqs[i].errorCodeValid && ((0 < inflightReqs[i].numBuffersLeft) ||
                            (!inflightReqs[i].haveResultMetadata))) {
                auto timeout = std::chrono::system_clock::now() +
                        std::chrono::seconds(kStreamBufferTimeoutSec);
                ASSERT_NE(std::cv_status::timeout, mResultCondition.wait_until(l, timeout));
            }

            ASSERT_FALSE(inflightReqs[i].errorCodeValid);
            ASSERT_NE(inflightReqs[i].resultOutputBuffers.size(), 0u);
            ASSERT_EQ(previewStream.id, inflightReqs[i].resultOutputBuffers[0].streamId);
            ASSERT_FALSE(inflightReqs[i].collectedResult.isEmpty());
            ASSERT_TRUE(inflightReqs[i].collectedResult.exists(ANDROID_SENSOR_SENSITIVITY));
            camera_metadata_entry_t isoResult = inflightReqs[i].collectedResult.find(
                    ANDROID_SENSOR_SENSITIVITY);
            ASSERT_TRUE(std::abs(isoResult.data.i32[0] - isoValues[i]) <=
                        std::round(isoValues[i]*isoTol));
        }

        if (useHalBufManager) {
            verifyBuffersReturned(session, deviceVersion, previewStream.id, cb);
        }
        ret = session->close();
        ASSERT_TRUE(ret.isOk());
    }
}

// Test whether an incorrect capture request with missing settings will
// be reported correctly.
TEST_F(CameraHidlTest, processCaptureRequestInvalidSinglePreview) {
    hidl_vec<hidl_string> cameraDeviceNames = getCameraDeviceNames(mProvider);
    std::vector<AvailableStream> outputPreviewStreams;
    AvailableStream previewThreshold = {kMaxPreviewWidth, kMaxPreviewHeight,
                                        static_cast<int32_t>(PixelFormat::IMPLEMENTATION_DEFINED)};
    uint64_t bufferId = 1;
    uint32_t frameNumber = 1;
    ::android::hardware::hidl_vec<uint8_t> settings;

    for (const auto& name : cameraDeviceNames) {
        int deviceVersion = getCameraDeviceVersion(name, mProviderType);
        if (deviceVersion == CAMERA_DEVICE_API_VERSION_1_0) {
            continue;
        } else if (deviceVersion <= 0) {
            ALOGE("%s: Unsupported device version %d", __func__, deviceVersion);
            ADD_FAILURE();
            return;
        }

        V3_2::Stream previewStream;
        HalStreamConfiguration halStreamConfig;
        sp<ICameraDeviceSession> session;
        sp<DeviceCb> cb;
        bool supportsPartialResults = false;
        bool useHalBufManager = false;
        uint32_t partialResultCount = 0;
        configurePreviewStream(name, deviceVersion, mProvider, &previewThreshold, &session /*out*/,
                &previewStream /*out*/, &halStreamConfig /*out*/,
                &supportsPartialResults /*out*/,
                &partialResultCount /*out*/, &useHalBufManager /*out*/, &cb /*out*/);

        hidl_handle buffer_handle;

        if (useHalBufManager) {
            bufferId = 0;
        } else {
            allocateGraphicBuffer(previewStream.width, previewStream.height,
                    android_convertGralloc1To0Usage(halStreamConfig.streams[0].producerUsage,
                        halStreamConfig.streams[0].consumerUsage),
                    halStreamConfig.streams[0].overrideFormat, &buffer_handle);
        }

        StreamBuffer outputBuffer = {halStreamConfig.streams[0].id,
                                     bufferId,
                                     buffer_handle,
                                     BufferStatus::OK,
                                     nullptr,
                                     nullptr};
        ::android::hardware::hidl_vec<StreamBuffer> outputBuffers = {outputBuffer};
        StreamBuffer emptyInputBuffer = {-1, 0, nullptr, BufferStatus::ERROR, nullptr,
                                         nullptr};
        CaptureRequest request = {frameNumber, 0 /* fmqSettingsSize */, settings,
                                  emptyInputBuffer, outputBuffers};

        // Settings were not correctly initialized, we should fail here
        Status status = Status::OK;
        uint32_t numRequestProcessed = 0;
        hidl_vec<BufferCache> cachesToRemove;
        Return<void> ret = session->processCaptureRequest(
            {request}, cachesToRemove, [&status, &numRequestProcessed](auto s,
                    uint32_t n) {
                status = s;
                numRequestProcessed = n;
            });
        ASSERT_TRUE(ret.isOk());
        ASSERT_EQ(Status::ILLEGAL_ARGUMENT, status);
        ASSERT_EQ(numRequestProcessed, 0u);

        ret = session->close();
        ASSERT_TRUE(ret.isOk());
    }
}

// Check whether an invalid capture request with missing output buffers
// will be reported correctly.
TEST_F(CameraHidlTest, processCaptureRequestInvalidBuffer) {
    hidl_vec<hidl_string> cameraDeviceNames = getCameraDeviceNames(mProvider);
    std::vector<AvailableStream> outputBlobStreams;
    AvailableStream previewThreshold = {kMaxPreviewWidth, kMaxPreviewHeight,
                                        static_cast<int32_t>(PixelFormat::IMPLEMENTATION_DEFINED)};
    uint32_t frameNumber = 1;
    ::android::hardware::hidl_vec<uint8_t> settings;

    for (const auto& name : cameraDeviceNames) {
        int deviceVersion = getCameraDeviceVersion(name, mProviderType);
        if (deviceVersion == CAMERA_DEVICE_API_VERSION_1_0) {
            continue;
        } else if (deviceVersion <= 0) {
            ALOGE("%s: Unsupported device version %d", __func__, deviceVersion);
            ADD_FAILURE();
            return;
        }

        V3_2::Stream previewStream;
        HalStreamConfiguration halStreamConfig;
        sp<ICameraDeviceSession> session;
        sp<DeviceCb> cb;
        bool supportsPartialResults = false;
        bool useHalBufManager = false;
        uint32_t partialResultCount = 0;
        configurePreviewStream(name, deviceVersion, mProvider, &previewThreshold, &session /*out*/,
                &previewStream /*out*/, &halStreamConfig /*out*/,
                &supportsPartialResults /*out*/,
                &partialResultCount /*out*/, &useHalBufManager /*out*/, &cb /*out*/);

        RequestTemplate reqTemplate = RequestTemplate::PREVIEW;
        Return<void> ret;
        ret = session->constructDefaultRequestSettings(reqTemplate,
                                                       [&](auto status, const auto& req) {
                                                           ASSERT_EQ(Status::OK, status);
                                                           settings = req;
                                                       });
        ASSERT_TRUE(ret.isOk());

        ::android::hardware::hidl_vec<StreamBuffer> emptyOutputBuffers;
        StreamBuffer emptyInputBuffer = {-1, 0, nullptr, BufferStatus::ERROR, nullptr,
                                         nullptr};
        CaptureRequest request = {frameNumber, 0 /* fmqSettingsSize */, settings,
                                  emptyInputBuffer, emptyOutputBuffers};

        // Output buffers are missing, we should fail here
        Status status = Status::OK;
        uint32_t numRequestProcessed = 0;
        hidl_vec<BufferCache> cachesToRemove;
        ret = session->processCaptureRequest(
            {request}, cachesToRemove, [&status, &numRequestProcessed](auto s,
                    uint32_t n) {
                status = s;
                numRequestProcessed = n;
            });
        ASSERT_TRUE(ret.isOk());
        ASSERT_EQ(Status::ILLEGAL_ARGUMENT, status);
        ASSERT_EQ(numRequestProcessed, 0u);

        ret = session->close();
        ASSERT_TRUE(ret.isOk());
    }
}

// Generate, trigger and flush a preview request
TEST_F(CameraHidlTest, flushPreviewRequest) {
    hidl_vec<hidl_string> cameraDeviceNames = getCameraDeviceNames(mProvider);
    std::vector<AvailableStream> outputPreviewStreams;
    AvailableStream previewThreshold = {kMaxPreviewWidth, kMaxPreviewHeight,
                                        static_cast<int32_t>(PixelFormat::IMPLEMENTATION_DEFINED)};
    uint64_t bufferId = 1;
    uint32_t frameNumber = 1;
    ::android::hardware::hidl_vec<uint8_t> settings;

    for (const auto& name : cameraDeviceNames) {
        int deviceVersion = getCameraDeviceVersion(name, mProviderType);
        if (deviceVersion == CAMERA_DEVICE_API_VERSION_1_0) {
            continue;
        } else if (deviceVersion <= 0) {
            ALOGE("%s: Unsupported device version %d", __func__, deviceVersion);
            ADD_FAILURE();
            return;
        }

        V3_2::Stream previewStream;
        HalStreamConfiguration halStreamConfig;
        sp<ICameraDeviceSession> session;
        sp<DeviceCb> cb;
        bool supportsPartialResults = false;
        bool useHalBufManager = false;
        uint32_t partialResultCount = 0;
        configurePreviewStream(name, deviceVersion, mProvider, &previewThreshold, &session /*out*/,
                &previewStream /*out*/, &halStreamConfig /*out*/,
                &supportsPartialResults /*out*/,
                &partialResultCount /*out*/, &useHalBufManager /*out*/, &cb /*out*/);

        std::shared_ptr<ResultMetadataQueue> resultQueue;
        auto resultQueueRet =
            session->getCaptureResultMetadataQueue(
                [&resultQueue](const auto& descriptor) {
                    resultQueue = std::make_shared<ResultMetadataQueue>(
                            descriptor);
                    if (!resultQueue->isValid() ||
                            resultQueue->availableToWrite() <= 0) {
                        ALOGE("%s: HAL returns empty result metadata fmq,"
                                " not use it", __func__);
                        resultQueue = nullptr;
                        // Don't use the queue onwards.
                    }
                });
        ASSERT_TRUE(resultQueueRet.isOk());

        InFlightRequest inflightReq = {1, false, supportsPartialResults,
                                       partialResultCount, resultQueue};
        RequestTemplate reqTemplate = RequestTemplate::PREVIEW;
        Return<void> ret;
        ret = session->constructDefaultRequestSettings(reqTemplate,
                                                       [&](auto status, const auto& req) {
                                                           ASSERT_EQ(Status::OK, status);
                                                           settings = req;
                                                       });
        ASSERT_TRUE(ret.isOk());

        hidl_handle buffer_handle;
        if (useHalBufManager) {
            bufferId = 0;
        } else {
            allocateGraphicBuffer(previewStream.width, previewStream.height,
                    android_convertGralloc1To0Usage(halStreamConfig.streams[0].producerUsage,
                        halStreamConfig.streams[0].consumerUsage),
                    halStreamConfig.streams[0].overrideFormat, &buffer_handle);
        }

        StreamBuffer outputBuffer = {halStreamConfig.streams[0].id,
                                     bufferId,
                                     buffer_handle,
                                     BufferStatus::OK,
                                     nullptr,
                                     nullptr};
        ::android::hardware::hidl_vec<StreamBuffer> outputBuffers = {outputBuffer};
        const StreamBuffer emptyInputBuffer = {-1, 0, nullptr,
                                               BufferStatus::ERROR, nullptr, nullptr};
        CaptureRequest request = {frameNumber, 0 /* fmqSettingsSize */, settings,
                                  emptyInputBuffer, outputBuffers};

        {
            std::unique_lock<std::mutex> l(mLock);
            mInflightMap.clear();
            mInflightMap.add(frameNumber, &inflightReq);
        }

        Status status = Status::INTERNAL_ERROR;
        uint32_t numRequestProcessed = 0;
        hidl_vec<BufferCache> cachesToRemove;
        ret = session->processCaptureRequest(
            {request}, cachesToRemove, [&status, &numRequestProcessed](auto s,
                    uint32_t n) {
                status = s;
                numRequestProcessed = n;
            });

        ASSERT_TRUE(ret.isOk());
        ASSERT_EQ(Status::OK, status);
        ASSERT_EQ(numRequestProcessed, 1u);
        // Flush before waiting for request to complete.
        Return<Status> returnStatus = session->flush();
        ASSERT_TRUE(returnStatus.isOk());
        ASSERT_EQ(Status::OK, returnStatus);

        {
            std::unique_lock<std::mutex> l(mLock);
            while (!inflightReq.errorCodeValid &&
                   ((0 < inflightReq.numBuffersLeft) ||
                           (!inflightReq.haveResultMetadata))) {
                auto timeout = std::chrono::system_clock::now() +
                               std::chrono::seconds(kStreamBufferTimeoutSec);
                ASSERT_NE(std::cv_status::timeout, mResultCondition.wait_until(l,
                        timeout));
            }

            if (!inflightReq.errorCodeValid) {
                ASSERT_NE(inflightReq.resultOutputBuffers.size(), 0u);
                ASSERT_EQ(previewStream.id, inflightReq.resultOutputBuffers[0].streamId);
            } else {
                switch (inflightReq.errorCode) {
                    case ErrorCode::ERROR_REQUEST:
                    case ErrorCode::ERROR_RESULT:
                    case ErrorCode::ERROR_BUFFER:
                        // Expected
                        break;
                    case ErrorCode::ERROR_DEVICE:
                    default:
                        FAIL() << "Unexpected error:"
                               << static_cast<uint32_t>(inflightReq.errorCode);
                }
            }
        }

        if (useHalBufManager) {
            verifyBuffersReturned(session, deviceVersion, previewStream.id, cb);
        }

        ret = session->close();
        ASSERT_TRUE(ret.isOk());
    }
}

// Verify that camera flushes correctly without any pending requests.
TEST_F(CameraHidlTest, flushEmpty) {
    hidl_vec<hidl_string> cameraDeviceNames = getCameraDeviceNames(mProvider);
    std::vector<AvailableStream> outputPreviewStreams;
    AvailableStream previewThreshold = {kMaxPreviewWidth, kMaxPreviewHeight,
                                        static_cast<int32_t>(PixelFormat::IMPLEMENTATION_DEFINED)};

    for (const auto& name : cameraDeviceNames) {
        int deviceVersion = getCameraDeviceVersion(name, mProviderType);
        if (deviceVersion == CAMERA_DEVICE_API_VERSION_1_0) {
            continue;
        } else if (deviceVersion <= 0) {
            ALOGE("%s: Unsupported device version %d", __func__, deviceVersion);
            ADD_FAILURE();
            return;
        }

        V3_2::Stream previewStream;
        HalStreamConfiguration halStreamConfig;
        sp<ICameraDeviceSession> session;
        sp<DeviceCb> cb;
        bool supportsPartialResults = false;
        bool useHalBufManager = false;
        uint32_t partialResultCount = 0;
        configurePreviewStream(name, deviceVersion, mProvider, &previewThreshold, &session /*out*/,
                &previewStream /*out*/, &halStreamConfig /*out*/,
                &supportsPartialResults /*out*/,
                &partialResultCount /*out*/, &useHalBufManager /*out*/, &cb /*out*/);

        Return<Status> returnStatus = session->flush();
        ASSERT_TRUE(returnStatus.isOk());
        ASSERT_EQ(Status::OK, returnStatus);

        {
            std::unique_lock<std::mutex> l(mLock);
            auto timeout = std::chrono::system_clock::now() +
                           std::chrono::milliseconds(kEmptyFlushTimeoutMSec);
            ASSERT_EQ(std::cv_status::timeout, mResultCondition.wait_until(l, timeout));
        }

        Return<void> ret = session->close();
        ASSERT_TRUE(ret.isOk());
    }
}

// Test camera provider@2.5 notify method
TEST_F(CameraHidlTest, providerDeviceStateNotification) {

    notifyDeviceState(provider::V2_5::DeviceState::BACK_COVERED);
    notifyDeviceState(provider::V2_5::DeviceState::NORMAL);
}

// Retrieve all valid output stream resolutions from the camera
// static characteristics.
Status CameraHidlTest::getAvailableOutputStreams(camera_metadata_t *staticMeta,
        std::vector<AvailableStream> &outputStreams,
        const AvailableStream *threshold) {
    AvailableStream depthPreviewThreshold = {kMaxPreviewWidth, kMaxPreviewHeight,
                                             static_cast<int32_t>(PixelFormat::Y16)};
    if (nullptr == staticMeta) {
        return Status::ILLEGAL_ARGUMENT;
    }

    camera_metadata_ro_entry scalarEntry;
    camera_metadata_ro_entry depthEntry;
    int foundScalar = find_camera_metadata_ro_entry(staticMeta,
            ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS, &scalarEntry);
    int foundDepth = find_camera_metadata_ro_entry(staticMeta,
            ANDROID_DEPTH_AVAILABLE_DEPTH_STREAM_CONFIGURATIONS, &depthEntry);
    if ((0 != foundScalar || (0 != (scalarEntry.count % 4))) &&
        (0 != foundDepth || (0 != (depthEntry.count % 4)))) {
        return Status::ILLEGAL_ARGUMENT;
    }

    if(foundScalar == 0 && (0 == (scalarEntry.count % 4))) {
        fillOutputStreams(&scalarEntry, outputStreams, threshold,
                ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT);
    }

    if(foundDepth == 0 && (0 == (depthEntry.count % 4))) {
        fillOutputStreams(&depthEntry, outputStreams, &depthPreviewThreshold,
                ANDROID_DEPTH_AVAILABLE_DEPTH_STREAM_CONFIGURATIONS_OUTPUT);
    }

    return Status::OK;
}

void CameraHidlTest::fillOutputStreams(camera_metadata_ro_entry_t* entry,
        std::vector<AvailableStream>& outputStreams, const AvailableStream* threshold,
        const int32_t availableConfigOutputTag) {
    for (size_t i = 0; i < entry->count; i+=4) {
        if (availableConfigOutputTag == 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);
                }
            }
        }
    }
}

// Get max jpeg buffer size in android.jpeg.maxSize
Status CameraHidlTest::getJpegBufferSize(camera_metadata_t *staticMeta, uint32_t* outBufSize) {
    if (nullptr == staticMeta || nullptr == outBufSize) {
        return Status::ILLEGAL_ARGUMENT;
    }

    camera_metadata_ro_entry entry;
    int rc = find_camera_metadata_ro_entry(staticMeta,
            ANDROID_JPEG_MAX_SIZE, &entry);
    if ((0 != rc) || (1 != entry.count)) {
        return Status::ILLEGAL_ARGUMENT;
    }

    *outBufSize = static_cast<uint32_t>(entry.data.i32[0]);
    return Status::OK;
}

// Check if the camera device has logical multi-camera capability.
Status CameraHidlTest::isLogicalMultiCamera(const 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_LOGICAL_MULTI_CAMERA == entry.data.u8[i]) {
            ret = Status::OK;
            break;
        }
    }

    return ret;
}

// Generate a list of physical camera ids backing a logical multi-camera.
Status CameraHidlTest::getPhysicalCameraIds(const camera_metadata_t *staticMeta,
        std::unordered_set<std::string> *physicalIds) {
    if ((nullptr == staticMeta) || (nullptr == physicalIds)) {
        return Status::ILLEGAL_ARGUMENT;
    }

    camera_metadata_ro_entry entry;
    int rc = find_camera_metadata_ro_entry(staticMeta, ANDROID_LOGICAL_MULTI_CAMERA_PHYSICAL_IDS,
            &entry);
    if (0 != rc) {
        return Status::ILLEGAL_ARGUMENT;
    }

    const uint8_t* ids = entry.data.u8;
    size_t start = 0;
    for (size_t i = 0; i < entry.count; i++) {
        if (ids[i] == '\0') {
            if (start != i) {
                std::string currentId(reinterpret_cast<const char *> (ids + start));
                physicalIds->emplace(currentId);
            }
            start = i + 1;
        }
    }

    return Status::OK;
}

// Generate a set of suported camera key ids.
Status CameraHidlTest::getSupportedKeys(camera_metadata_t *staticMeta,
        uint32_t tagId, std::unordered_set<int32_t> *requestIDs) {
    if ((nullptr == staticMeta) || (nullptr == requestIDs)) {
        return Status::ILLEGAL_ARGUMENT;
    }

    camera_metadata_ro_entry entry;
    int rc = find_camera_metadata_ro_entry(staticMeta, tagId, &entry);
    if ((0 != rc) || (entry.count == 0)) {
        return Status::OK;
    }

    requestIDs->insert(entry.data.i32, entry.data.i32 + entry.count);

    return Status::OK;
}

void CameraHidlTest::constructFilteredSettings(const sp<ICameraDeviceSession>& session,
        const std::unordered_set<int32_t>& availableKeys, RequestTemplate reqTemplate,
        android::hardware::camera::common::V1_0::helper::CameraMetadata* defaultSettings,
        android::hardware::camera::common::V1_0::helper::CameraMetadata* filteredSettings) {
    ASSERT_NE(defaultSettings, nullptr);
    ASSERT_NE(filteredSettings, nullptr);

    auto ret = session->constructDefaultRequestSettings(reqTemplate,
            [&defaultSettings] (auto status, const auto& req) mutable {
                ASSERT_EQ(Status::OK, status);

                const camera_metadata_t *metadata = reinterpret_cast<const camera_metadata_t*> (
                        req.data());
                size_t expectedSize = req.size();
                int result = validate_camera_metadata_structure(metadata, &expectedSize);
                ASSERT_TRUE((result == 0) || (result == CAMERA_METADATA_VALIDATION_SHIFTED));

                size_t entryCount = get_camera_metadata_entry_count(metadata);
                ASSERT_GT(entryCount, 0u);
                *defaultSettings = metadata;
                });
    ASSERT_TRUE(ret.isOk());
    const android::hardware::camera::common::V1_0::helper::CameraMetadata &constSettings =
        *defaultSettings;
    for (const auto& keyIt : availableKeys) {
        camera_metadata_ro_entry entry = constSettings.find(keyIt);
        if (entry.count > 0) {
            filteredSettings->update(entry);
        }
    }
}

// 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(const camera_metadata_t *staticMeta) {
    if (Status::OK == isZSLModeAvailable(staticMeta, PRIV_REPROCESS)) {
        return Status::OK;
    } else {
        return isZSLModeAvailable(staticMeta, YUV_REPROCESS);
    }
}

Status CameraHidlTest::isZSLModeAvailable(const camera_metadata_t *staticMeta,
        ReprocessType reprocType) {

    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 ((reprocType == PRIV_REPROCESS &&
                ANDROID_REQUEST_AVAILABLE_CAPABILITIES_PRIVATE_REPROCESSING == entry.data.u8[i]) ||
                (reprocType == YUV_REPROCESS &&
                ANDROID_REQUEST_AVAILABLE_CAPABILITIES_YUV_REPROCESSING == entry.data.u8[i])) {
            ret = Status::OK;
            break;
        }
    }

    return ret;
}

// Check whether this is a monochrome camera using the static camera characteristics.
Status CameraHidlTest::isMonochromeCamera(const 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_MONOCHROME == 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;
}

// Check whether the camera device supports specific focus mode.
Status CameraHidlTest::isAutoFocusModeAvailable(
        CameraParameters &cameraParams,
        const char *mode) {
    ::android::String8 focusModes(cameraParams.get(
            CameraParameters::KEY_SUPPORTED_FOCUS_MODES));
    if (focusModes.contains(mode)) {
        return Status::OK;
    }

    return Status::METHOD_NOT_SUPPORTED;
}

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*/,
        uint32_t jpegBufferSize) {
    ASSERT_NE(nullptr, config3_2);
    ASSERT_NE(nullptr, config3_4);
    ASSERT_NE(nullptr, config3_5);

    ::android::hardware::hidl_vec<V3_4::Stream> streams3_4(streams3_2.size());
    size_t idx = 0;
    for (auto& stream3_2 : streams3_2) {
        V3_4::Stream stream;
        stream.v3_2 = stream3_2;
        stream.bufferSize = 0;
        if (stream3_2.format == PixelFormat::BLOB &&
                stream3_2.dataSpace == static_cast<V3_2::DataspaceFlags>(Dataspace::V0_JFIF)) {
            stream.bufferSize = jpegBufferSize;
        }
        streams3_4[idx++] = stream;
    }
    // Caller is responsible to fill in non-zero config3_5->streamConfigCounter after this returns
    *config3_5 = {{streams3_4, configMode, {}}, 0};
    *config3_4 = config3_5->v3_4;
    *config3_2 = {streams3_2, configMode};
}

// Configure multiple preview streams using different physical ids.
void CameraHidlTest::configurePreviewStreams3_4(const std::string &name, int32_t deviceVersion,
        sp<ICameraProvider> provider,
        const AvailableStream *previewThreshold,
        const std::unordered_set<std::string>& physicalIds,
        sp<device::V3_4::ICameraDeviceSession> *session3_4 /*out*/,
        sp<device::V3_5::ICameraDeviceSession> *session3_5 /*out*/,
        V3_2::Stream *previewStream /*out*/,
        device::V3_4::HalStreamConfiguration *halStreamConfig /*out*/,
        bool *supportsPartialResults /*out*/,
        uint32_t *partialResultCount /*out*/,
        bool *useHalBufManager /*out*/,
        sp<DeviceCb> *outCb /*out*/,
        uint32_t streamConfigCounter,
        bool allowUnsupport) {
    ASSERT_NE(nullptr, session3_4);
    ASSERT_NE(nullptr, session3_5);
    ASSERT_NE(nullptr, halStreamConfig);
    ASSERT_NE(nullptr, previewStream);
    ASSERT_NE(nullptr, supportsPartialResults);
    ASSERT_NE(nullptr, partialResultCount);
    ASSERT_NE(nullptr, useHalBufManager);
    ASSERT_NE(nullptr, outCb);
    ASSERT_FALSE(physicalIds.empty());

    std::vector<AvailableStream> outputPreviewStreams;
    ::android::sp<ICameraDevice> device3_x;
    ALOGI("configureStreams: Testing camera device %s", name.c_str());
    Return<void> ret;
    ret = provider->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_x = device;
        });
    ASSERT_TRUE(ret.isOk());

    camera_metadata_t *staticMeta;
    ret = device3_x->getCameraCharacteristics([&] (Status s,
            CameraMetadata metadata) {
        ASSERT_EQ(Status::OK, s);
        staticMeta = clone_camera_metadata(
                reinterpret_cast<const camera_metadata_t*>(metadata.data()));
        ASSERT_NE(nullptr, staticMeta);
    });
    ASSERT_TRUE(ret.isOk());

    camera_metadata_ro_entry entry;
    auto status = find_camera_metadata_ro_entry(staticMeta,
            ANDROID_REQUEST_PARTIAL_RESULT_COUNT, &entry);
    if ((0 == status) && (entry.count > 0)) {
        *partialResultCount = entry.data.i32[0];
        *supportsPartialResults = (*partialResultCount > 1);
    }

    sp<DeviceCb> cb = new DeviceCb(this, deviceVersion, staticMeta);
    sp<ICameraDeviceSession> session;
    ret = device3_x->open(
        cb,
        [&session](auto status, const auto& newSession) {
            ALOGI("device::open returns status:%d", (int)status);
            ASSERT_EQ(Status::OK, status);
            ASSERT_NE(newSession, nullptr);
            session = newSession;
        });
    ASSERT_TRUE(ret.isOk());
    *outCb = cb;

    sp<device::V3_3::ICameraDeviceSession> session3_3;
    castSession(session, deviceVersion, &session3_3, session3_4, session3_5);
    ASSERT_NE(nullptr, (*session3_4).get());

    *useHalBufManager = false;
    status = find_camera_metadata_ro_entry(staticMeta,
            ANDROID_INFO_SUPPORTED_BUFFER_MANAGEMENT_VERSION, &entry);
    if ((0 == status) && (entry.count == 1)) {
        *useHalBufManager = (entry.data.u8[0] ==
            ANDROID_INFO_SUPPORTED_BUFFER_MANAGEMENT_VERSION_HIDL_DEVICE_3_5);
    }

    outputPreviewStreams.clear();
    auto rc = getAvailableOutputStreams(staticMeta,
            outputPreviewStreams, previewThreshold);
    free_camera_metadata(staticMeta);
    ASSERT_EQ(Status::OK, rc);
    ASSERT_FALSE(outputPreviewStreams.empty());

    ::android::hardware::hidl_vec<V3_4::Stream> streams3_4(physicalIds.size());
    int32_t streamId = 0;
    for (auto const& physicalId : physicalIds) {
        V3_4::Stream stream3_4 = {{streamId, StreamType::OUTPUT,
            static_cast<uint32_t> (outputPreviewStreams[0].width),
            static_cast<uint32_t> (outputPreviewStreams[0].height),
            static_cast<PixelFormat> (outputPreviewStreams[0].format),
            GRALLOC1_CONSUMER_USAGE_HWCOMPOSER, 0, StreamRotation::ROTATION_0},
            physicalId.c_str(), /*bufferSize*/ 0};
        streams3_4[streamId++] = stream3_4;
    }

    ::android::hardware::camera::device::V3_4::StreamConfiguration config3_4;
    ::android::hardware::camera::device::V3_5::StreamConfiguration config3_5;
    config3_4 = {streams3_4, StreamConfigurationMode::NORMAL_MODE, {}};
    RequestTemplate reqTemplate = RequestTemplate::PREVIEW;
    ret = (*session3_4)->constructDefaultRequestSettings(reqTemplate,
            [&config3_4](auto status, const auto& req) {
            ASSERT_EQ(Status::OK, status);
            config3_4.sessionParams = req;
            });
    ASSERT_TRUE(ret.isOk());

    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);

        bool supported = false;
        ret = cameraDevice3_5->isStreamCombinationSupported(config3_4,
                [&supported](Status s, bool combStatus) {
                    ASSERT_TRUE((Status::OK == s) ||
                            (Status::METHOD_NOT_SUPPORTED == s));
                    if (Status::OK == s) {
                        supported = combStatus;
                    }
                });
        ASSERT_TRUE(ret.isOk());
        // If stream combination is not supported, return null session.
        if (!supported) {
            *session3_5 = nullptr;
            return;
        }
    }

    if (*session3_5 != nullptr) {
        config3_5.v3_4 = config3_4;
        config3_5.streamConfigCounter = streamConfigCounter;
        ret = (*session3_5)->configureStreams_3_5(config3_5,
                [&] (Status s, device::V3_4::HalStreamConfiguration halConfig) {
                    ASSERT_EQ(Status::OK, s);
                    ASSERT_EQ(physicalIds.size(), halConfig.streams.size());
                    *halStreamConfig = halConfig;
                    if (*useHalBufManager) {
                        hidl_vec<V3_2::Stream> streams(physicalIds.size());
                        hidl_vec<V3_2::HalStream> halStreams(physicalIds.size());
                        for (size_t i = 0; i < physicalIds.size(); i++) {
                            streams[i] = streams3_4[i].v3_2;
                            halStreams[i] = halConfig.streams[i].v3_3.v3_2;
                        }
                        cb->setCurrentStreamConfig(streams, halStreams);
                    }
                });
    } else {
        ret = (*session3_4)->configureStreams_3_4(config3_4,
                [&] (Status s, device::V3_4::HalStreamConfiguration halConfig) {
                ASSERT_EQ(Status::OK, s);
                ASSERT_EQ(physicalIds.size(), halConfig.streams.size());
                *halStreamConfig = halConfig;
                });
    }
    *previewStream = streams3_4[0].v3_2;
    ASSERT_TRUE(ret.isOk());
}

bool CameraHidlTest::isDepthOnly(camera_metadata_t* staticMeta) {
    camera_metadata_ro_entry scalarEntry;
    camera_metadata_ro_entry depthEntry;

    int rc = find_camera_metadata_ro_entry(
        staticMeta, ANDROID_REQUEST_AVAILABLE_CAPABILITIES, &scalarEntry);
    if (rc == 0) {
        for (uint32_t i = 0; i < scalarEntry.count; i++) {
            if (scalarEntry.data.u8[i] == ANDROID_REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE) {
                return false;
            }
        }
    }

    for (uint32_t i = 0; i < scalarEntry.count; i++) {
        if (scalarEntry.data.u8[i] == ANDROID_REQUEST_AVAILABLE_CAPABILITIES_DEPTH_OUTPUT) {

            rc = find_camera_metadata_ro_entry(
                staticMeta, ANDROID_DEPTH_AVAILABLE_DEPTH_STREAM_CONFIGURATIONS, &depthEntry);
            size_t i = 0;
            if (rc == 0 && depthEntry.data.i32[i] == static_cast<int32_t>(PixelFormat::Y16)) {
                // only Depth16 format is supported now
                return true;
            }
            break;
        }
    }

    return false;
}

// Open a device session and configure a preview stream.
void CameraHidlTest::configurePreviewStream(const std::string &name, int32_t deviceVersion,
        sp<ICameraProvider> provider,
        const AvailableStream *previewThreshold,
        sp<ICameraDeviceSession> *session /*out*/,
        V3_2::Stream *previewStream /*out*/,
        HalStreamConfiguration *halStreamConfig /*out*/,
        bool *supportsPartialResults /*out*/,
        uint32_t *partialResultCount /*out*/,
        bool *useHalBufManager /*out*/,
        sp<DeviceCb> *outCb /*out*/,
        uint32_t streamConfigCounter) {
    ASSERT_NE(nullptr, session);
    ASSERT_NE(nullptr, previewStream);
    ASSERT_NE(nullptr, halStreamConfig);
    ASSERT_NE(nullptr, supportsPartialResults);
    ASSERT_NE(nullptr, partialResultCount);
    ASSERT_NE(nullptr, useHalBufManager);
    ASSERT_NE(nullptr, outCb);

    std::vector<AvailableStream> outputPreviewStreams;
    ::android::sp<ICameraDevice> device3_x;
    ALOGI("configureStreams: Testing camera device %s", name.c_str());
    Return<void> ret;
    ret = provider->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_x = device;
        });
    ASSERT_TRUE(ret.isOk());

    camera_metadata_t *staticMeta;
    ret = device3_x->getCameraCharacteristics([&] (Status s,
            CameraMetadata metadata) {
        ASSERT_EQ(Status::OK, s);
        staticMeta = clone_camera_metadata(
                reinterpret_cast<const camera_metadata_t*>(metadata.data()));
        ASSERT_NE(nullptr, staticMeta);
    });
    ASSERT_TRUE(ret.isOk());

    camera_metadata_ro_entry entry;
    auto status = find_camera_metadata_ro_entry(staticMeta,
            ANDROID_REQUEST_PARTIAL_RESULT_COUNT, &entry);
    if ((0 == status) && (entry.count > 0)) {
        *partialResultCount = entry.data.i32[0];
        *supportsPartialResults = (*partialResultCount > 1);
    }

    sp<DeviceCb> cb = new DeviceCb(this, deviceVersion, staticMeta);
    ret = device3_x->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;
        });
    ASSERT_TRUE(ret.isOk());
    *outCb = cb;

    sp<device::V3_3::ICameraDeviceSession> session3_3;
    sp<device::V3_4::ICameraDeviceSession> session3_4;
    sp<device::V3_5::ICameraDeviceSession> session3_5;
    castSession(*session, deviceVersion, &session3_3, &session3_4, &session3_5);

    *useHalBufManager = false;
    status = find_camera_metadata_ro_entry(staticMeta,
            ANDROID_INFO_SUPPORTED_BUFFER_MANAGEMENT_VERSION, &entry);
    if ((0 == status) && (entry.count == 1)) {
        *useHalBufManager = (entry.data.u8[0] ==
            ANDROID_INFO_SUPPORTED_BUFFER_MANAGEMENT_VERSION_HIDL_DEVICE_3_5);
    }

    outputPreviewStreams.clear();
    auto rc = getAvailableOutputStreams(staticMeta,
            outputPreviewStreams, previewThreshold);

    uint32_t jpegBufferSize = 0;
    ASSERT_EQ(Status::OK, getJpegBufferSize(staticMeta, &jpegBufferSize));
    ASSERT_NE(0u, jpegBufferSize);

    free_camera_metadata(staticMeta);
    ASSERT_EQ(Status::OK, rc);
    ASSERT_FALSE(outputPreviewStreams.empty());

    V3_2::DataspaceFlags dataspaceFlag = 0;
    switch (static_cast<PixelFormat>(outputPreviewStreams[0].format)) {
        case PixelFormat::Y16:
            dataspaceFlag = static_cast<V3_2::DataspaceFlags>(Dataspace::DEPTH);
            break;
        default:
            dataspaceFlag = static_cast<V3_2::DataspaceFlags>(Dataspace::UNKNOWN);
    }

    V3_2::Stream stream3_2 = {0, StreamType::OUTPUT,
            static_cast<uint32_t> (outputPreviewStreams[0].width),
            static_cast<uint32_t> (outputPreviewStreams[0].height),
            static_cast<PixelFormat> (outputPreviewStreams[0].format),
            GRALLOC1_CONSUMER_USAGE_HWCOMPOSER, dataspaceFlag, StreamRotation::ROTATION_0};
    ::android::hardware::hidl_vec<V3_2::Stream> streams3_2 = {stream3_2};
    ::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) {
        RequestTemplate reqTemplate = RequestTemplate::PREVIEW;
        ret = session3_5->constructDefaultRequestSettings(reqTemplate,
                                                       [&config3_5](auto status, const auto& req) {
                                                           ASSERT_EQ(Status::OK, status);
                                                           config3_5.v3_4.sessionParams = req;
                                                       });
        ASSERT_TRUE(ret.isOk());
        config3_5.streamConfigCounter = streamConfigCounter;
        ret = session3_5->configureStreams_3_5(config3_5,
                [&] (Status s, device::V3_4::HalStreamConfiguration halConfig) {
                    ASSERT_EQ(Status::OK, s);
                    ASSERT_EQ(1u, halConfig.streams.size());
                    halStreamConfig->streams.resize(1);
                    halStreamConfig->streams[0] = halConfig.streams[0].v3_3.v3_2;
                    if (*useHalBufManager) {
                        hidl_vec<V3_2::Stream> streams(1);
                        hidl_vec<V3_2::HalStream> halStreams(1);
                        streams[0] = stream3_2;
                        halStreams[0] = halConfig.streams[0].v3_3.v3_2;
                        cb->setCurrentStreamConfig(streams, halStreams);
                    }
                });
    } else if (session3_4 != nullptr) {
        RequestTemplate reqTemplate = RequestTemplate::PREVIEW;
        ret = session3_4->constructDefaultRequestSettings(reqTemplate,
                                                       [&config3_4](auto status, const auto& req) {
                                                           ASSERT_EQ(Status::OK, status);
                                                           config3_4.sessionParams = req;
                                                       });
        ASSERT_TRUE(ret.isOk());
        ret = session3_4->configureStreams_3_4(config3_4,
                [&] (Status s, device::V3_4::HalStreamConfiguration halConfig) {
                    ASSERT_EQ(Status::OK, s);
                    ASSERT_EQ(1u, halConfig.streams.size());
                    halStreamConfig->streams.resize(halConfig.streams.size());
                    for (size_t i = 0; i < halConfig.streams.size(); i++) {
                        halStreamConfig->streams[i] = halConfig.streams[i].v3_3.v3_2;
                    }
                });
    } else if (session3_3 != nullptr) {
        ret = session3_3->configureStreams_3_3(config3_2,
                [&] (Status s, device::V3_3::HalStreamConfiguration halConfig) {
                    ASSERT_EQ(Status::OK, s);
                    ASSERT_EQ(1u, halConfig.streams.size());
                    halStreamConfig->streams.resize(halConfig.streams.size());
                    for (size_t i = 0; i < halConfig.streams.size(); i++) {
                        halStreamConfig->streams[i] = halConfig.streams[i].v3_2;
                    }
                });
    } else {
        ret = (*session)->configureStreams(config3_2,
                [&] (Status s, HalStreamConfiguration halConfig) {
                    ASSERT_EQ(Status::OK, s);
                    ASSERT_EQ(1u, halConfig.streams.size());
                    *halStreamConfig = halConfig;
                });
    }
    *previewStream = stream3_2;
    ASSERT_TRUE(ret.isOk());
}

void CameraHidlTest::castDevice(const sp<device::V3_2::ICameraDevice> &device,
        int32_t deviceVersion, sp<device::V3_5::ICameraDevice> *device3_5/*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;
    }
}

//Cast camera provider to corresponding version if available
void CameraHidlTest::castProvider(const sp<ICameraProvider> &provider,
        sp<provider::V2_5::ICameraProvider> *provider2_5 /*out*/) {
    ASSERT_NE(nullptr, provider2_5);
    auto castResult = provider::V2_5::ICameraProvider::castFrom(provider);
    if (castResult.isOk()) {
        *provider2_5 = castResult;
    }
}

//Cast camera device session to corresponding version
void CameraHidlTest::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*/) {
    ASSERT_NE(nullptr, session3_3);
    ASSERT_NE(nullptr, session3_4);
    ASSERT_NE(nullptr, session3_5);

    switch (deviceVersion) {
        case CAMERA_DEVICE_API_VERSION_3_5: {
            auto castResult = device::V3_5::ICameraDeviceSession::castFrom(session);
            ASSERT_TRUE(castResult.isOk());
            *session3_5 = castResult;
        }
        [[fallthrough]];
        case CAMERA_DEVICE_API_VERSION_3_4: {
            auto castResult = device::V3_4::ICameraDeviceSession::castFrom(session);
            ASSERT_TRUE(castResult.isOk());
            *session3_4 = castResult;
        }
        [[fallthrough]];
        case CAMERA_DEVICE_API_VERSION_3_3: {
            auto castResult = device::V3_3::ICameraDeviceSession::castFrom(session);
            ASSERT_TRUE(castResult.isOk());
            *session3_3 = castResult;
            break;
        }
        default:
            //no-op
            return;
    }
}

void CameraHidlTest::verifyStreamCombination(sp<device::V3_5::ICameraDevice> cameraDevice3_5,
        const ::android::hardware::camera::device::V3_4::StreamConfiguration &config3_4,
        bool expectedStatus, bool expectMethodSupported) {
    if (cameraDevice3_5.get() != nullptr) {
        auto ret = cameraDevice3_5->isStreamCombinationSupported(config3_4,
                [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());
    }
}

// Verify logical camera static metadata
void CameraHidlTest::verifyLogicalCameraMetadata(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 camera_metadata_t* metadata = (camera_metadata_t*)chars.data();
    ASSERT_NE(nullptr, metadata);

    Status rc = isLogicalMultiCamera(metadata);
    ASSERT_TRUE(Status::OK == rc || Status::METHOD_NOT_SUPPORTED == rc);
    if (Status::METHOD_NOT_SUPPORTED == rc) {
        return;
    }

    std::string version, cameraId;
    ASSERT_TRUE(::matchDeviceName(cameraName, mProviderType, &version, &cameraId));
    std::unordered_set<std::string> physicalIds;
    ASSERT_TRUE(Status::OK == getPhysicalCameraIds(metadata, &physicalIds));
    for (auto physicalId : physicalIds) {
        ASSERT_NE(physicalId, cameraId);
        bool isPublicId = false;
        for (auto& deviceName : deviceNames) {
            std::string publicVersion, publicId;
            ASSERT_TRUE(::matchDeviceName(deviceName, mProviderType, &publicVersion, &publicId));
            if (physicalId == publicId) {
                isPublicId = true;
                break;
            }
        }
        if (isPublicId) {
            continue;
        }

        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);
        });
        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());
    }

    // Make sure ANDROID_LOGICAL_MULTI_CAMERA_ACTIVE_PHYSICAL_ID is available in
    // result keys.
    if (deviceVersion >= CAMERA_DEVICE_API_VERSION_3_5) {
        camera_metadata_ro_entry entry;
        int retcode = find_camera_metadata_ro_entry(metadata,
                ANDROID_REQUEST_AVAILABLE_RESULT_KEYS, &entry);
        if ((0 == retcode) && (entry.count > 0)) {
                ASSERT_NE(std::find(entry.data.i32, entry.data.i32 + entry.count,
                    static_cast<int32_t>(
                            CameraMetadataTag::ANDROID_LOGICAL_MULTI_CAMERA_ACTIVE_PHYSICAL_ID)),
                    entry.data.i32 + entry.count);
        } else {
            ADD_FAILURE() << "Get camera availableResultKeys failed!";
        }
    }
}

void CameraHidlTest::verifyCameraCharacteristics(Status status, const CameraMetadata& chars) {
    ASSERT_EQ(Status::OK, status);
    const camera_metadata_t* metadata = (camera_metadata_t*)chars.data();
    size_t expectedSize = chars.size();
    int result = validate_camera_metadata_structure(metadata, &expectedSize);
    ASSERT_TRUE((result == 0) || (result == CAMERA_METADATA_VALIDATION_SHIFTED));
    size_t entryCount = get_camera_metadata_entry_count(metadata);
    // TODO: we can do better than 0 here. Need to check how many required
    // characteristics keys we've defined.
    ASSERT_GT(entryCount, 0u);

    camera_metadata_ro_entry entry;
    int retcode = find_camera_metadata_ro_entry(metadata,
            ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL, &entry);
    if ((0 == retcode) && (entry.count > 0)) {
        uint8_t hardwareLevel = entry.data.u8[0];
        ASSERT_TRUE(
                hardwareLevel == ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED ||
                hardwareLevel == ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL_FULL ||
                hardwareLevel == ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL_3 ||
                hardwareLevel == ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL_EXTERNAL);
    } else {
        ADD_FAILURE() << "Get camera hardware level failed!";
    }

    entry.count = 0;
    retcode = find_camera_metadata_ro_entry(metadata,
            ANDROID_REQUEST_CHARACTERISTIC_KEYS_NEEDING_PERMISSION, &entry);
    if ((0 == retcode) || (entry.count > 0)) {
        ADD_FAILURE() << "ANDROID_REQUEST_CHARACTERISTIC_KEYS_NEEDING_PERMISSION "
            << " per API contract should never be set by Hal!";
    }
    retcode = find_camera_metadata_ro_entry(metadata,
            ANDROID_DEPTH_AVAILABLE_DYNAMIC_DEPTH_STREAM_CONFIGURATIONS, &entry);
    if ((0 == retcode) || (entry.count > 0)) {
        ADD_FAILURE() << "ANDROID_DEPTH_AVAILABLE_DYNAMIC_DEPTH_STREAM_CONFIGURATIONS"
            << " per API contract should never be set by Hal!";
    }
    retcode = find_camera_metadata_ro_entry(metadata,
            ANDROID_DEPTH_AVAILABLE_DYNAMIC_DEPTH_MIN_FRAME_DURATIONS, &entry);
    if ((0 == retcode) || (entry.count > 0)) {
        ADD_FAILURE() << "ANDROID_DEPTH_AVAILABLE_DYNAMIC_DEPTH_MIN_FRAME_DURATIONS"
            << " per API contract should never be set by Hal!";
    }
    retcode = find_camera_metadata_ro_entry(metadata,
            ANDROID_DEPTH_AVAILABLE_DYNAMIC_DEPTH_STALL_DURATIONS, &entry);
    if ((0 == retcode) || (entry.count > 0)) {
        ADD_FAILURE() << "ANDROID_DEPTH_AVAILABLE_DYNAMIC_DEPTH_STALL_DURATIONS"
            << " per API contract should never be set by Hal!";
    }

    retcode = find_camera_metadata_ro_entry(metadata,
            ANDROID_HEIC_AVAILABLE_HEIC_STREAM_CONFIGURATIONS, &entry);
    if (0 == retcode || entry.count > 0) {
        ADD_FAILURE() << "ANDROID_HEIC_AVAILABLE_HEIC_STREAM_CONFIGURATIONS "
            << " per API contract should never be set by Hal!";
    }

    retcode = find_camera_metadata_ro_entry(metadata,
            ANDROID_HEIC_AVAILABLE_HEIC_MIN_FRAME_DURATIONS, &entry);
    if (0 == retcode || entry.count > 0) {
        ADD_FAILURE() << "ANDROID_HEIC_AVAILABLE_HEIC_MIN_FRAME_DURATIONS "
            << " per API contract should never be set by Hal!";
    }

    retcode = find_camera_metadata_ro_entry(metadata,
            ANDROID_HEIC_AVAILABLE_HEIC_STALL_DURATIONS, &entry);
    if (0 == retcode || entry.count > 0) {
        ADD_FAILURE() << "ANDROID_HEIC_AVAILABLE_HEIC_STALL_DURATIONS "
            << " per API contract should never be set by Hal!";
    }

    retcode = find_camera_metadata_ro_entry(metadata,
            ANDROID_HEIC_INFO_SUPPORTED, &entry);
    if (0 == retcode && entry.count > 0) {
        retcode = find_camera_metadata_ro_entry(metadata,
            ANDROID_HEIC_INFO_MAX_JPEG_APP_SEGMENTS_COUNT, &entry);
        if (0 == retcode && entry.count > 0) {
            uint8_t maxJpegAppSegmentsCount = entry.data.u8[0];
            ASSERT_TRUE(maxJpegAppSegmentsCount >= 1 &&
                    maxJpegAppSegmentsCount <= 16);
        } else {
            ADD_FAILURE() << "Get Heic maxJpegAppSegmentsCount failed!";
        }
    }
}

void CameraHidlTest::verifyMonochromeCharacteristics(const CameraMetadata& chars,
        int deviceVersion) {
    const camera_metadata_t* metadata = (camera_metadata_t*)chars.data();
    Status rc = isMonochromeCamera(metadata);
    if (Status::METHOD_NOT_SUPPORTED == rc) {
        return;
    }
    ASSERT_EQ(Status::OK, rc);

    camera_metadata_ro_entry entry;
    // Check capabilities
    int retcode = find_camera_metadata_ro_entry(metadata,
                ANDROID_REQUEST_AVAILABLE_CAPABILITIES, &entry);
    if ((0 == retcode) && (entry.count > 0)) {
        ASSERT_EQ(std::find(entry.data.u8, entry.data.u8 + entry.count,
                ANDROID_REQUEST_AVAILABLE_CAPABILITIES_MANUAL_POST_PROCESSING),
                entry.data.u8 + entry.count);
        if (deviceVersion < CAMERA_DEVICE_API_VERSION_3_5) {
            ASSERT_EQ(std::find(entry.data.u8, entry.data.u8 + entry.count,
                    ANDROID_REQUEST_AVAILABLE_CAPABILITIES_RAW),
                    entry.data.u8 + entry.count);
        }
    }

    if (deviceVersion >= CAMERA_DEVICE_API_VERSION_3_5) {
        // Check Cfa
        retcode = find_camera_metadata_ro_entry(metadata,
                ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT, &entry);
        if ((0 == retcode) && (entry.count == 1)) {
            ASSERT_TRUE(entry.data.i32[0] == static_cast<int32_t>(
                    CameraMetadataEnumAndroidSensorInfoColorFilterArrangement::ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_MONO)
                    || entry.data.i32[0] == static_cast<int32_t>(
                    CameraMetadataEnumAndroidSensorInfoColorFilterArrangement::ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_NIR));
        }

        // Check availableRequestKeys
        retcode = find_camera_metadata_ro_entry(metadata,
                ANDROID_REQUEST_AVAILABLE_REQUEST_KEYS, &entry);
        if ((0 == retcode) && (entry.count > 0)) {
            for (size_t i = 0; i < entry.count; i++) {
                ASSERT_NE(entry.data.i32[i], ANDROID_COLOR_CORRECTION_MODE);
                ASSERT_NE(entry.data.i32[i], ANDROID_COLOR_CORRECTION_TRANSFORM);
                ASSERT_NE(entry.data.i32[i], ANDROID_COLOR_CORRECTION_GAINS);
            }
        } else {
            ADD_FAILURE() << "Get camera availableRequestKeys failed!";
        }

        // Check availableResultKeys
        retcode = find_camera_metadata_ro_entry(metadata,
                ANDROID_REQUEST_AVAILABLE_RESULT_KEYS, &entry);
        if ((0 == retcode) && (entry.count > 0)) {
            for (size_t i = 0; i < entry.count; i++) {
                ASSERT_NE(entry.data.i32[i], ANDROID_SENSOR_GREEN_SPLIT);
                ASSERT_NE(entry.data.i32[i], ANDROID_SENSOR_NEUTRAL_COLOR_POINT);
                ASSERT_NE(entry.data.i32[i], ANDROID_COLOR_CORRECTION_MODE);
                ASSERT_NE(entry.data.i32[i], ANDROID_COLOR_CORRECTION_TRANSFORM);
                ASSERT_NE(entry.data.i32[i], ANDROID_COLOR_CORRECTION_GAINS);
            }
        } else {
            ADD_FAILURE() << "Get camera availableResultKeys failed!";
        }

        // Check availableCharacteristicKeys
        retcode = find_camera_metadata_ro_entry(metadata,
                ANDROID_REQUEST_AVAILABLE_CHARACTERISTICS_KEYS, &entry);
        if ((0 == retcode) && (entry.count > 0)) {
            for (size_t i = 0; i < entry.count; i++) {
                ASSERT_NE(entry.data.i32[i], ANDROID_SENSOR_REFERENCE_ILLUMINANT1);
                ASSERT_NE(entry.data.i32[i], ANDROID_SENSOR_REFERENCE_ILLUMINANT2);
                ASSERT_NE(entry.data.i32[i], ANDROID_SENSOR_CALIBRATION_TRANSFORM1);
                ASSERT_NE(entry.data.i32[i], ANDROID_SENSOR_CALIBRATION_TRANSFORM2);
                ASSERT_NE(entry.data.i32[i], ANDROID_SENSOR_COLOR_TRANSFORM1);
                ASSERT_NE(entry.data.i32[i], ANDROID_SENSOR_COLOR_TRANSFORM2);
                ASSERT_NE(entry.data.i32[i], ANDROID_SENSOR_FORWARD_MATRIX1);
                ASSERT_NE(entry.data.i32[i], ANDROID_SENSOR_FORWARD_MATRIX2);
            }
        } else {
            ADD_FAILURE() << "Get camera availableResultKeys failed!";
        }

        // Check blackLevelPattern
        retcode = find_camera_metadata_ro_entry(metadata,
                ANDROID_SENSOR_BLACK_LEVEL_PATTERN, &entry);
        if ((0 == retcode) && (entry.count > 0)) {
            ASSERT_EQ(entry.count, 4);
            for (size_t i = 1; i < entry.count; i++) {
                ASSERT_EQ(entry.data.i32[i], entry.data.i32[0]);
            }
        }
    }
}

void CameraHidlTest::verifyMonochromeCameraResult(
        const ::android::hardware::camera::common::V1_0::helper::CameraMetadata& metadata) {
    camera_metadata_ro_entry entry;

    // Check tags that are not applicable for monochrome camera
    ASSERT_FALSE(metadata.exists(ANDROID_SENSOR_GREEN_SPLIT));
    ASSERT_FALSE(metadata.exists(ANDROID_SENSOR_NEUTRAL_COLOR_POINT));
    ASSERT_FALSE(metadata.exists(ANDROID_COLOR_CORRECTION_MODE));
    ASSERT_FALSE(metadata.exists(ANDROID_COLOR_CORRECTION_TRANSFORM));
    ASSERT_FALSE(metadata.exists(ANDROID_COLOR_CORRECTION_GAINS));

    // Check dynamicBlackLevel
    entry = metadata.find(ANDROID_SENSOR_DYNAMIC_BLACK_LEVEL);
    if (entry.count > 0) {
        ASSERT_EQ(entry.count, 4);
        for (size_t i = 1; i < entry.count; i++) {
            ASSERT_FLOAT_EQ(entry.data.f[i], entry.data.f[0]);
        }
    }

    // Check noiseProfile
    entry = metadata.find(ANDROID_SENSOR_NOISE_PROFILE);
    if (entry.count > 0) {
        ASSERT_EQ(entry.count, 2);
    }

    // Check lensShadingMap
    entry = metadata.find(ANDROID_STATISTICS_LENS_SHADING_MAP);
    if (entry.count > 0) {
        ASSERT_EQ(entry.count % 4, 0);
        for (size_t i = 0; i < entry.count/4; i++) {
            ASSERT_FLOAT_EQ(entry.data.f[i*4+1], entry.data.f[i*4]);
            ASSERT_FLOAT_EQ(entry.data.f[i*4+2], entry.data.f[i*4]);
            ASSERT_FLOAT_EQ(entry.data.f[i*4+3], entry.data.f[i*4]);
        }
    }

    // Check tonemapCurve
    camera_metadata_ro_entry curveRed = metadata.find(ANDROID_TONEMAP_CURVE_RED);
    camera_metadata_ro_entry curveGreen = metadata.find(ANDROID_TONEMAP_CURVE_GREEN);
    camera_metadata_ro_entry curveBlue = metadata.find(ANDROID_TONEMAP_CURVE_BLUE);
    if (curveRed.count > 0 && curveGreen.count > 0 && curveBlue.count > 0) {
        ASSERT_EQ(curveRed.count, curveGreen.count);
        ASSERT_EQ(curveRed.count, curveBlue.count);
        for (size_t i = 0; i < curveRed.count; i++) {
            ASSERT_FLOAT_EQ(curveGreen.data.f[i], curveRed.data.f[i]);
            ASSERT_FLOAT_EQ(curveBlue.data.f[i], curveRed.data.f[i]);
        }
    }
}

void CameraHidlTest::verifyBuffersReturned(
        sp<device::V3_2::ICameraDeviceSession> session,
        int deviceVersion, int32_t streamId,
        sp<DeviceCb> cb, uint32_t streamConfigCounter) {
    sp<device::V3_3::ICameraDeviceSession> session3_3;
    sp<device::V3_4::ICameraDeviceSession> session3_4;
    sp<device::V3_5::ICameraDeviceSession> session3_5;
    castSession(session, deviceVersion, &session3_3, &session3_4, &session3_5);
    ASSERT_NE(nullptr, session3_5.get());

    hidl_vec<int32_t> streamIds(1);
    streamIds[0] = streamId;
    session3_5->signalStreamFlush(streamIds, /*streamConfigCounter*/streamConfigCounter);
    cb->waitForBuffersReturned();
}

void CameraHidlTest::verifyBuffersReturned(
        sp<device::V3_4::ICameraDeviceSession> session3_4,
        hidl_vec<int32_t> streamIds, sp<DeviceCb> cb, uint32_t streamConfigCounter) {
    auto castResult = device::V3_5::ICameraDeviceSession::castFrom(session3_4);
    ASSERT_TRUE(castResult.isOk());
    sp<device::V3_5::ICameraDeviceSession> session3_5 = castResult;
    ASSERT_NE(nullptr, session3_5.get());

    session3_5->signalStreamFlush(streamIds, /*streamConfigCounter*/streamConfigCounter);
    cb->waitForBuffersReturned();
}

void CameraHidlTest::verifyLogicalCameraResult(const camera_metadata_t* staticMetadata,
        const ::android::hardware::camera::common::V1_0::helper::CameraMetadata& resultMetadata) {
    std::unordered_set<std::string> physicalIds;
    Status rc = getPhysicalCameraIds(staticMetadata, &physicalIds);
    ASSERT_TRUE(Status::OK == rc);
    ASSERT_TRUE(physicalIds.size() > 1);

    camera_metadata_ro_entry entry;
    // Check mainPhysicalId
    entry = resultMetadata.find(ANDROID_LOGICAL_MULTI_CAMERA_ACTIVE_PHYSICAL_ID);
    if (entry.count > 0) {
        std::string mainPhysicalId(reinterpret_cast<const char *>(entry.data.u8));
        ASSERT_NE(physicalIds.find(mainPhysicalId), physicalIds.end());
    } else {
        ADD_FAILURE() << "Get LOGICAL_MULTI_CAMERA_ACTIVE_PHYSICAL_ID failed!";
    }
}

// Open a device session with empty callbacks and return static metadata.
void CameraHidlTest::openEmptyDeviceSession(const std::string &name, sp<ICameraProvider> provider,
        sp<ICameraDeviceSession> *session /*out*/, camera_metadata_t **staticMeta /*out*/,
        ::android::sp<ICameraDevice> *cameraDevice /*out*/) {
    ASSERT_NE(nullptr, session);
    ASSERT_NE(nullptr, staticMeta);

    ::android::sp<ICameraDevice> device3_x;
    ALOGI("configureStreams: Testing camera device %s", name.c_str());
    Return<void> ret;
    ret = provider->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_x = device;
        });
    ASSERT_TRUE(ret.isOk());
    if (cameraDevice != nullptr) {
        *cameraDevice = device3_x;
    }

    sp<EmptyDeviceCb> cb = new EmptyDeviceCb();
    ret = device3_x->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;
        });
    ASSERT_TRUE(ret.isOk());

    ret = device3_x->getCameraCharacteristics([&] (Status s,
            CameraMetadata metadata) {
        ASSERT_EQ(Status::OK, s);
        *staticMeta = clone_camera_metadata(
                reinterpret_cast<const camera_metadata_t*>(metadata.data()));
        ASSERT_NE(nullptr, *staticMeta);
    });
    ASSERT_TRUE(ret.isOk());
}

void CameraHidlTest::notifyDeviceState(provider::V2_5::DeviceState newState) {
    if (mProvider2_5.get() == nullptr) return;

    mProvider2_5->notifyDeviceStateChange(
            static_cast<hidl_bitfield<provider::V2_5::DeviceState>>(newState));
}

// Open a particular camera device.
void CameraHidlTest::openCameraDevice(const std::string &name,
        sp<ICameraProvider> provider,
        sp<::android::hardware::camera::device::V1_0::ICameraDevice> *device1 /*out*/) {
    ASSERT_TRUE(nullptr != device1);

    Return<void> ret;
    ret = provider->getCameraDeviceInterface_V1_x(
            name,
            [&](auto status, const auto& device) {
            ALOGI("getCameraDeviceInterface_V1_x returns status:%d",
                  (int)status);
            ASSERT_EQ(Status::OK, status);
            ASSERT_NE(device, nullptr);
            *device1 = device;
        });
    ASSERT_TRUE(ret.isOk());

    sp<Camera1DeviceCb> deviceCb = new Camera1DeviceCb(this);
    Return<Status> returnStatus = (*device1)->open(deviceCb);
    ASSERT_TRUE(returnStatus.isOk());
    ASSERT_EQ(Status::OK, returnStatus);
}

// Initialize and configure a preview window.
void CameraHidlTest::setupPreviewWindow(
        const sp<::android::hardware::camera::device::V1_0::ICameraDevice> &device,
        sp<BufferItemConsumer> *bufferItemConsumer /*out*/,
        sp<BufferItemHander> *bufferHandler /*out*/) {
    ASSERT_NE(nullptr, device.get());
    ASSERT_NE(nullptr, bufferItemConsumer);
    ASSERT_NE(nullptr, bufferHandler);

    sp<IGraphicBufferProducer> producer;
    sp<IGraphicBufferConsumer> consumer;
    BufferQueue::createBufferQueue(&producer, &consumer);
    *bufferItemConsumer = new BufferItemConsumer(consumer,
            GraphicBuffer::USAGE_HW_TEXTURE); //Use GLConsumer default usage flags
    ASSERT_NE(nullptr, (*bufferItemConsumer).get());
    *bufferHandler = new BufferItemHander(*bufferItemConsumer);
    ASSERT_NE(nullptr, (*bufferHandler).get());
    (*bufferItemConsumer)->setFrameAvailableListener(*bufferHandler);
    sp<Surface> surface = new Surface(producer);
    sp<PreviewWindowCb> previewCb = new PreviewWindowCb(surface);

    auto rc = device->setPreviewWindow(previewCb);
    ASSERT_TRUE(rc.isOk());
    ASSERT_EQ(Status::OK, rc);
}

// Stop camera preview and close camera.
void CameraHidlTest::stopPreviewAndClose(
        const sp<::android::hardware::camera::device::V1_0::ICameraDevice> &device) {
    Return<void> ret = device->stopPreview();
    ASSERT_TRUE(ret.isOk());

    ret = device->close();
    ASSERT_TRUE(ret.isOk());
}

// Enable a specific camera message type.
void CameraHidlTest::enableMsgType(unsigned int msgType,
        const sp<::android::hardware::camera::device::V1_0::ICameraDevice> &device) {
    Return<void> ret = device->enableMsgType(msgType);
    ASSERT_TRUE(ret.isOk());

    Return<bool> returnBoolStatus = device->msgTypeEnabled(msgType);
    ASSERT_TRUE(returnBoolStatus.isOk());
    ASSERT_TRUE(returnBoolStatus);
}

// Disable a specific camera message type.
void CameraHidlTest::disableMsgType(unsigned int msgType,
        const sp<::android::hardware::camera::device::V1_0::ICameraDevice> &device) {
    Return<void> ret = device->disableMsgType(msgType);
    ASSERT_TRUE(ret.isOk());

    Return<bool> returnBoolStatus = device->msgTypeEnabled(msgType);
    ASSERT_TRUE(returnBoolStatus.isOk());
    ASSERT_FALSE(returnBoolStatus);
}

// Wait until a specific frame notification arrives.
void CameraHidlTest::waitForFrameLocked(DataCallbackMsg msgFrame,
        std::unique_lock<std::mutex> &l) {
    while (msgFrame != mDataMessageTypeReceived) {
        auto timeout = std::chrono::system_clock::now() +
                std::chrono::seconds(kStreamBufferTimeoutSec);
        ASSERT_NE(std::cv_status::timeout,
                mResultCondition.wait_until(l, timeout));
    }
}

// Start preview on a particular camera device
void CameraHidlTest::startPreview(
        const sp<::android::hardware::camera::device::V1_0::ICameraDevice> &device) {
    Return<Status> returnStatus = device->startPreview();
    ASSERT_TRUE(returnStatus.isOk());
    ASSERT_EQ(Status::OK, returnStatus);
}

// Retrieve camera parameters.
void CameraHidlTest::getParameters(
        const sp<::android::hardware::camera::device::V1_0::ICameraDevice> &device,
        CameraParameters *cameraParams /*out*/) {
    ASSERT_NE(nullptr, cameraParams);

    Return<void> ret;
    ret = device->getParameters([&] (const ::android::hardware::hidl_string& params) {
        ASSERT_FALSE(params.empty());
        ::android::String8 paramString(params.c_str());
        (*cameraParams).unflatten(paramString);
    });
    ASSERT_TRUE(ret.isOk());
}

// Set camera parameters.
void CameraHidlTest::setParameters(
        const sp<::android::hardware::camera::device::V1_0::ICameraDevice> &device,
        const CameraParameters &cameraParams) {
    Return<Status> returnStatus = device->setParameters(
            cameraParams.flatten().string());
    ASSERT_TRUE(returnStatus.isOk());
    ASSERT_EQ(Status::OK, returnStatus);
}

void CameraHidlTest::allocateGraphicBuffer(uint32_t width, uint32_t height, uint64_t usage,
        PixelFormat format, hidl_handle *buffer_handle /*out*/) {
    ASSERT_NE(buffer_handle, nullptr);

    sp<android::hardware::graphics::allocator::V2_0::IAllocator> allocator =
        android::hardware::graphics::allocator::V2_0::IAllocator::getService();
    sp<android::hardware::graphics::allocator::V3_0::IAllocator> allocatorV3 =
        android::hardware::graphics::allocator::V3_0::IAllocator::getService();

    sp<android::hardware::graphics::mapper::V3_0::IMapper> mapperV3 =
        android::hardware::graphics::mapper::V3_0::IMapper::getService();
    sp<android::hardware::graphics::mapper::V2_0::IMapper> mapper =
        android::hardware::graphics::mapper::V2_0::IMapper::getService();
    ::android::hardware::hidl_vec<uint32_t> descriptor;
    if (mapperV3 != nullptr && allocatorV3 != nullptr) {
        android::hardware::graphics::mapper::V3_0::IMapper::BufferDescriptorInfo descriptorInfo {};
        descriptorInfo.width = width;
        descriptorInfo.height = height;
        descriptorInfo.layerCount = 1;
        descriptorInfo.format =
                static_cast<android::hardware::graphics::common::V1_2::PixelFormat>(format);
        descriptorInfo.usage = usage;

        auto ret = mapperV3->createDescriptor(
            descriptorInfo, [&descriptor](android::hardware::graphics::mapper::V3_0::Error err,
                                ::android::hardware::hidl_vec<uint32_t> desc) {
                ASSERT_EQ(err, android::hardware::graphics::mapper::V3_0::Error::NONE);
                descriptor = desc;
            });
        ASSERT_TRUE(ret.isOk());

        ret = allocatorV3->allocate(descriptor, 1u,
            [&](android::hardware::graphics::mapper::V3_0::Error err, uint32_t /*stride*/,
                const ::android::hardware::hidl_vec<::android::hardware::hidl_handle>& buffers) {
                ASSERT_EQ(android::hardware::graphics::mapper::V3_0::Error::NONE, err);
                ASSERT_EQ(buffers.size(), 1u);
                *buffer_handle = buffers[0];
            });
        ASSERT_TRUE(ret.isOk());
    } else {
        ASSERT_NE(mapper.get(), nullptr);
        ASSERT_NE(allocator.get(), nullptr);
        android::hardware::graphics::mapper::V2_0::IMapper::BufferDescriptorInfo descriptorInfo {};
        descriptorInfo.width = width;
        descriptorInfo.height = height;
        descriptorInfo.layerCount = 1;
        descriptorInfo.format = format;
        descriptorInfo.usage = usage;

        auto ret = mapper->createDescriptor(
            descriptorInfo, [&descriptor](android::hardware::graphics::mapper::V2_0::Error err,
                                ::android::hardware::hidl_vec<uint32_t> desc) {
                ASSERT_EQ(err, android::hardware::graphics::mapper::V2_0::Error::NONE);
                descriptor = desc;
            });
        ASSERT_TRUE(ret.isOk());

        ret = allocator->allocate(descriptor, 1u,
            [&](android::hardware::graphics::mapper::V2_0::Error err, uint32_t /*stride*/,
                const ::android::hardware::hidl_vec<::android::hardware::hidl_handle>& buffers) {
                ASSERT_EQ(android::hardware::graphics::mapper::V2_0::Error::NONE, err);
                ASSERT_EQ(buffers.size(), 1u);
                *buffer_handle = buffers[0];
            });
        ASSERT_TRUE(ret.isOk());
    }
}

void CameraHidlTest::verifyRecommendedConfigs(const CameraMetadata& chars) {
    size_t CONFIG_ENTRY_SIZE = 5;
    size_t CONFIG_ENTRY_TYPE_OFFSET = 3;
    size_t CONFIG_ENTRY_BITFIELD_OFFSET = 4;
    uint32_t maxPublicUsecase =
            ANDROID_SCALER_AVAILABLE_RECOMMENDED_STREAM_CONFIGURATIONS_PUBLIC_END;
    uint32_t vendorUsecaseStart =
            ANDROID_SCALER_AVAILABLE_RECOMMENDED_STREAM_CONFIGURATIONS_VENDOR_START;
    uint32_t usecaseMask = (1 << vendorUsecaseStart) - 1;
    usecaseMask &= ~((1 << maxPublicUsecase) - 1);

    const camera_metadata_t* metadata = reinterpret_cast<const camera_metadata_t*> (chars.data());

    camera_metadata_ro_entry recommendedConfigsEntry, recommendedDepthConfigsEntry, ioMapEntry;
    recommendedConfigsEntry.count = recommendedDepthConfigsEntry.count = ioMapEntry.count = 0;
    int retCode = find_camera_metadata_ro_entry(metadata,
            ANDROID_SCALER_AVAILABLE_RECOMMENDED_STREAM_CONFIGURATIONS, &recommendedConfigsEntry);
    int depthRetCode = find_camera_metadata_ro_entry(metadata,
            ANDROID_DEPTH_AVAILABLE_RECOMMENDED_DEPTH_STREAM_CONFIGURATIONS,
            &recommendedDepthConfigsEntry);
    int ioRetCode = find_camera_metadata_ro_entry(metadata,
            ANDROID_SCALER_AVAILABLE_RECOMMENDED_INPUT_OUTPUT_FORMATS_MAP, &ioMapEntry);
    if ((0 != retCode) && (0 != depthRetCode)) {
        //In case both regular and depth recommended configurations are absent,
        //I/O should be absent as well.
        ASSERT_NE(ioRetCode, 0);
        return;
    }

    camera_metadata_ro_entry availableKeysEntry;
    retCode = find_camera_metadata_ro_entry(metadata,
            ANDROID_REQUEST_AVAILABLE_CHARACTERISTICS_KEYS, &availableKeysEntry);
    ASSERT_TRUE((0 == retCode) && (availableKeysEntry.count > 0));
    std::vector<int32_t> availableKeys;
    availableKeys.reserve(availableKeysEntry.count);
    availableKeys.insert(availableKeys.end(), availableKeysEntry.data.i32,
            availableKeysEntry.data.i32 + availableKeysEntry.count);

    if (recommendedConfigsEntry.count > 0) {
        ASSERT_NE(std::find(availableKeys.begin(), availableKeys.end(),
                    ANDROID_SCALER_AVAILABLE_RECOMMENDED_STREAM_CONFIGURATIONS),
                availableKeys.end());
        ASSERT_EQ((recommendedConfigsEntry.count % CONFIG_ENTRY_SIZE), 0);
        for (size_t i = 0; i < recommendedConfigsEntry.count; i += CONFIG_ENTRY_SIZE) {
            int32_t entryType =
                recommendedConfigsEntry.data.i32[i + CONFIG_ENTRY_TYPE_OFFSET];
            uint32_t bitfield =
                recommendedConfigsEntry.data.i32[i + CONFIG_ENTRY_BITFIELD_OFFSET];
            ASSERT_TRUE((entryType ==
                     ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT) ||
                    (entryType ==
                     ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_INPUT));
            ASSERT_TRUE((bitfield & usecaseMask) == 0);
        }
    }

    if (recommendedDepthConfigsEntry.count > 0) {
        ASSERT_NE(std::find(availableKeys.begin(), availableKeys.end(),
                    ANDROID_DEPTH_AVAILABLE_RECOMMENDED_DEPTH_STREAM_CONFIGURATIONS),
                availableKeys.end());
        ASSERT_EQ((recommendedDepthConfigsEntry.count % CONFIG_ENTRY_SIZE), 0);
        for (size_t i = 0; i < recommendedDepthConfigsEntry.count; i += CONFIG_ENTRY_SIZE) {
            int32_t entryType =
                recommendedDepthConfigsEntry.data.i32[i + CONFIG_ENTRY_TYPE_OFFSET];
            uint32_t bitfield =
                recommendedDepthConfigsEntry.data.i32[i + CONFIG_ENTRY_BITFIELD_OFFSET];
            ASSERT_TRUE((entryType ==
                     ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT) ||
                    (entryType ==
                     ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_INPUT));
            ASSERT_TRUE((bitfield & usecaseMask) == 0);
        }

        if (recommendedConfigsEntry.count == 0) {
            //In case regular recommended configurations are absent but suggested depth
            //configurations are present, I/O should be absent.
            ASSERT_NE(ioRetCode, 0);
        }
    }

    if ((ioRetCode == 0) && (ioMapEntry.count > 0)) {
        ASSERT_NE(std::find(availableKeys.begin(), availableKeys.end(),
                    ANDROID_SCALER_AVAILABLE_RECOMMENDED_INPUT_OUTPUT_FORMATS_MAP),
                availableKeys.end());
        ASSERT_EQ(isZSLModeAvailable(metadata), Status::OK);
    }
}

void CameraHidlTest::verifySessionReconfigurationQuery(
        sp<device::V3_5::ICameraDeviceSession> session3_5, camera_metadata* oldSessionParams,
        camera_metadata* newSessionParams) {
    ASSERT_NE(nullptr, session3_5.get());
    ASSERT_NE(nullptr, oldSessionParams);
    ASSERT_NE(nullptr, newSessionParams);

    android::hardware::hidl_vec<uint8_t> oldParams, newParams;
    oldParams.setToExternal(reinterpret_cast<uint8_t*>(oldSessionParams),
            get_camera_metadata_size(oldSessionParams));
    newParams.setToExternal(reinterpret_cast<uint8_t*>(newSessionParams),
            get_camera_metadata_size(newSessionParams));
    android::hardware::camera::common::V1_0::Status callStatus;
    auto hidlCb = [&callStatus] (android::hardware::camera::common::V1_0::Status s,
            bool /*requiredFlag*/) {
        callStatus = s;
    };
    auto ret = session3_5->isReconfigurationRequired(oldParams, newParams, hidlCb);
    ASSERT_TRUE(ret.isOk());
    switch (callStatus) {
        case android::hardware::camera::common::V1_0::Status::OK:
        case android::hardware::camera::common::V1_0::Status::METHOD_NOT_SUPPORTED:
            break;
        case android::hardware::camera::common::V1_0::Status::INTERNAL_ERROR:
        default:
            ADD_FAILURE() << "Query calllback failed";
    }
}

int main(int argc, char **argv) {
  ::testing::AddGlobalTestEnvironment(CameraHidlEnvironment::Instance());
  ::testing::InitGoogleTest(&argc, argv);
  CameraHidlEnvironment::Instance()->init(&argc, argv);
  int status = RUN_ALL_TESTS();
  ALOGI("Test result = %d", status);
  return status;
}