codec2: C2VDAComponentIntf implementation

C2VDAComponentIntf is the component interface class of C2VDAComponent (V4L2
codec 2.0 implementation). Framework could config and query codec parameters
via C2VDAComponentIntf interface. There is the correspondent native test of it.

This CL also includes C2ComponentStore implementation and factory functions for
creating C2VDAComponentIntf objects.

Bug: 63828275
Test: run native tests (on nyc branch, cheets_arm device). Steps:
1) mmm external/v4l2_codec2/tests/
2) adb push out/target/product/cheets_arm/data/nativetest/C2VDAComponent_test /data/local/tmp/
3) adb shell /data/local/tmp/C2VDAComponent_test/C2VDAComponent_test

Change-Id: Ibaea85bc2f157b8099544a5e38a812808e71405a
diff --git a/Android.mk b/Android.mk
index f7c8483..1a036c3 100644
--- a/Android.mk
+++ b/Android.mk
@@ -2,6 +2,7 @@
 include $(CLEAR_VARS)
 
 LOCAL_SRC_FILES:= \
+        C2VDAComponent.cpp \
         C2VDAAdaptor.cpp   \
 
 LOCAL_C_INCLUDES += \
@@ -9,12 +10,19 @@
         $(TOP)/external/gtest/include \
         $(TOP)/external/v4l2_codec2 \
         $(TOP)/external/v4l2_codec2/vda \
+        $(TOP)/frameworks/av/media/libstagefright/codec2/include \
+        $(TOP)/frameworks/av/media/libstagefright/codec2/vndk/include \
+        $(TOP)/frameworks/av/media/libstagefright/include \
 
 LOCAL_MODULE:= libv4l2_codec2
 LOCAL_MODULE_TAGS := optional
 
 LOCAL_SHARED_LIBRARIES := libchrome \
                           liblog \
+                          libmedia \
+                          libstagefright \
+                          libstagefright_codec2 \
+                          libstagefright_foundation \
                           libutils \
                           libv4l2_codec2_vda \
 
diff --git a/C2VDAAdaptor.cpp b/C2VDAAdaptor.cpp
index f15eb71..bc6b28d 100644
--- a/C2VDAAdaptor.cpp
+++ b/C2VDAAdaptor.cpp
@@ -8,7 +8,9 @@
 #include "C2VDAAdaptor.h"
 #include "bitstream_buffer.h"
 #include "native_pixmap_handle.h"
+#include "v4l2_device.h"
 #include "v4l2_slice_video_decode_accelerator.h"
+#include "videodev2.h"
 
 #include <system/graphics.h>
 #include <utils/Log.h>
@@ -104,6 +106,22 @@
     mPictureSize = media::Size();
 }
 
+//static
+media::VideoDecodeAccelerator::SupportedProfiles C2VDAAdaptor::GetSupportedProfiles(
+        uint32_t inputFormatFourcc) {
+    media::VideoDecodeAccelerator::SupportedProfiles supportedProfiles;
+    auto allProfiles = media::V4L2SliceVideoDecodeAccelerator::GetSupportedProfiles();
+    bool isSliceBased = (inputFormatFourcc == V4L2_PIX_FMT_H264_SLICE) ||
+                        (inputFormatFourcc == V4L2_PIX_FMT_VP8_FRAME) ||
+                        (inputFormatFourcc == V4L2_PIX_FMT_VP9_FRAME);
+    for (const auto& profile : allProfiles) {
+        if (inputFormatFourcc == media::V4L2Device::VideoCodecProfileToV4L2PixFmt(
+                profile.profile, isSliceBased)) {
+            supportedProfiles.push_back(profile);
+        }
+    }
+    return supportedProfiles;
+}
 
 void C2VDAAdaptor::ProvidePictureBuffers(uint32_t requested_num_of_buffers,
                                          media::VideoPixelFormat output_format,
@@ -166,7 +184,7 @@
         case media::VideoDecodeAccelerator::PLATFORM_FAILURE:
             return VideoDecodeAcceleratorAdaptor::PLATFORM_FAILURE;
         default:
-            ALOGE("Unknown error code: %d", error);
+            ALOGE("Unknown error code: %d", static_cast<int>(error));
             return VideoDecodeAcceleratorAdaptor::PLATFORM_FAILURE;
     }
 }
diff --git a/C2VDAAdaptor.h b/C2VDAAdaptor.h
index 6f997e4..3027615 100644
--- a/C2VDAAdaptor.h
+++ b/C2VDAAdaptor.h
@@ -31,6 +31,9 @@
     void reset() override;
     void destroy() override;
 
+    static media::VideoDecodeAccelerator::SupportedProfiles GetSupportedProfiles(
+            uint32_t inputFormatFourcc);
+
     // Implementation of the media::VideoDecodeAccelerator::Client interface.
     void ProvidePictureBuffers(uint32_t requested_num_of_buffers,
                                media::VideoPixelFormat output_format,
diff --git a/C2VDAComponent.cpp b/C2VDAComponent.cpp
new file mode 100644
index 0000000..7c0ba9d
--- /dev/null
+++ b/C2VDAComponent.cpp
@@ -0,0 +1,566 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "C2VDAComponent"
+
+#include <algorithm>
+
+#include "C2VDAAdaptor.h"
+#include "C2VDAComponent.h"
+#include "video_codecs.h"
+#include "videodev2.h"
+
+#include <media/stagefright/MediaDefs.h>
+#include <utils/Log.h>
+#include <utils/misc.h>
+
+#define UNUSED(expr) do { (void)(expr); } while (0)
+
+namespace android {
+
+namespace {
+
+// Get index from C2param object. Use index to identify the type of the parameter.
+// Currently there is no wise way to get index from a parameter because index is private.
+uint32_t restoreIndex(const C2Param* param) {
+    return (param->forStream() ? (0x02000000 | ((param->stream() << 17) & 0x01FE0000)) : 0)
+            | param->type();
+}
+
+
+// Helper function to allocate string type parameters.
+template <class T>
+std::unique_ptr<T> alloc_unique_cstr(const char* cstr) {
+    size_t len = strlen(cstr);
+    std::unique_ptr<T> ptr = T::alloc_unique(len);
+    memcpy(ptr->m.mValue, cstr, len);
+    return ptr;
+}
+
+template <class T>
+std::unique_ptr<C2SettingResult> reportReadOnlyFailure(C2Param* c2Param) {
+    T* param = (T*)c2Param;
+    return std::unique_ptr<C2SettingResult>(
+            new C2SettingResult { C2ParamField(param, &T::mValue),
+                                  C2SettingResult::READ_ONLY,
+                                  nullptr /* supportedValues */,
+                                  {} /* conflictedFields */ });
+}
+
+template <class T>
+std::unique_ptr<C2SettingResult> reportReadOnlyFlexFailure(C2Param* c2Param) {
+    T* param = (T*)c2Param;
+    return std::unique_ptr<C2SettingResult>(
+            new C2SettingResult { C2ParamField(param, &T::m),
+                                  C2SettingResult::READ_ONLY,
+                                  nullptr /* supportedValues */,
+                                  {} /* conflictedFields */ });
+}
+
+// Helper function to find int32_t value from C2Value::Primitive vector.
+bool findInt32FromPrimitiveValues(const int32_t& v, const C2FieldSupportedValues& values) {
+    if (values.type == C2FieldSupportedValues::FLAGS) {
+        ALOGE("Type of field supported values should not be FLAGS.");
+        return false;
+    }
+    if (values.type == C2FieldSupportedValues::RANGE) {
+        // Only support min/max/step case only.
+        return v >= values.range.min.i32 && v <= values.range.max.i32 &&
+               ((v - values.range.min.i32) % values.range.step.i32 == 0);
+    }
+    // if values.type == C2FieldSupportedValues::VALUES
+    for (const auto& value : values.values) {
+        if (value.i32 == v) {
+            return true;
+        }
+    }
+    return false;
+}
+
+// Helper function to find uint32_t value from C2Value::Primitive vector.
+bool findUint32FromPrimitiveValues(const uint32_t& v, const C2FieldSupportedValues& values) {
+    if (values.type == C2FieldSupportedValues::FLAGS) {
+        ALOGE("Type of field supported values should not be FLAGS.");
+        return false;
+    }
+    if (values.type == C2FieldSupportedValues::RANGE) {
+        // Only support min/max/step case only.
+        return v >= values.range.min.u32 && v <= values.range.max.u32 &&
+               ((v - values.range.min.u32) % values.range.step.u32 == 0);
+    }
+    // if values.type == C2FieldSupportedValues::VALUES
+    for (const auto& value : values.values) {
+        if (value.u32 == v) {
+            return true;
+        }
+    }
+    return false;
+}
+
+}  // namespace
+
+
+// Neglect flexible flag while matching parameter indices.
+#define CASE(paramType) \
+    case (paramType::baseIndex & ~(C2Param::BaseIndex::_kFlexibleFlag)): \
+        return std::unique_ptr<C2StructDescriptor>(new C2StructDescriptor{ \
+                paramType::baseIndex, paramType::fieldList, })
+
+class C2VDAComponentIntf::ParamReflector : public C2ParamReflector {
+public:
+    virtual std::unique_ptr<C2StructDescriptor> describe(C2Param::BaseIndex paramIndex) override {
+        switch (paramIndex.baseIndex()) {
+            //CASE(C2ComponentDomainInfo);  //TODO: known codec2 framework bug
+            CASE(C2StreamFormatConfig);
+            CASE(C2VideoSizeStreamInfo);
+            CASE(C2PortMimeConfig);
+            CASE(C2MaxVideoSizeHintPortSetting);
+        }
+        return nullptr;
+    }
+};
+
+#undef CASE
+
+// static
+const uint32_t C2VDAComponentIntf::kInputFormatFourcc = V4L2_PIX_FMT_H264_SLICE;
+
+C2VDAComponentIntf::C2VDAComponentIntf(C2String name, node_id id)
+    : kName(name),
+      kId(id),
+      mParamReflector(std::make_shared<ParamReflector>()),
+      mDomainInfo(C2DomainVideo),
+      mOutputColorFormat(0u, kColorFormatYUV420Flexible),
+      // Support H264 only for now.
+      mInputPortMime(alloc_unique_cstr<C2PortMimeConfig::input>(MEDIA_MIMETYPE_VIDEO_AVC)),
+      mOutputPortMime(alloc_unique_cstr<C2PortMimeConfig::output>(MEDIA_MIMETYPE_VIDEO_RAW)),
+      mSupportedProfiles(C2VDAAdaptor::GetSupportedProfiles(kInputFormatFourcc)) {
+    // Get supported profiles from VDA.
+    CHECK_GT(mSupportedProfiles.size(), 0u);
+
+    // Set default codec profile.
+    mInputCodecProfile.mValue = mSupportedProfiles[0].profile;
+
+    mMinVideoSize = mSupportedProfiles[0].min_resolution;
+    mMaxVideoSize = mSupportedProfiles[0].max_resolution;
+    // Set default output video size.
+    mVideoSize.mWidth = mMinVideoSize.width();
+    mVideoSize.mHeight = mMinVideoSize.height();
+    // Set default max video size.
+    mMaxVideoSizeHint.mWidth = mMaxVideoSize.width();
+    mMaxVideoSizeHint.mHeight = mMaxVideoSize.height();
+
+    for (const auto& supportedProfile : mSupportedProfiles) {
+        mSupportedCodecProfiles.push_back(supportedProfile.profile);
+        ALOGI("Get supported profile: profile=%d, min_res=%s, max_res=%s",
+              supportedProfile.profile, supportedProfile.min_resolution.ToString().c_str(),
+              supportedProfile.max_resolution.ToString().c_str());
+    }
+
+    auto insertParam = [&params = mParams] (C2Param* param) {
+        params[restoreIndex(param)] = param;
+    };
+
+    insertParam(&mDomainInfo);
+    insertParam(&mOutputColorFormat);
+    insertParam(mInputPortMime.get());
+    insertParam(mOutputPortMime.get());
+
+    insertParam(&mInputCodecProfile);
+    // TODO(johnylin): C2FieldSupportedValues for mInputCodecProfile should be VALUES type. But
+    // right now VALUES type only accepts initializer_list argument, we cannot pass a vector.
+    mSupportedValues.emplace(
+            C2ParamField(&mInputCodecProfile, &C2StreamFormatConfig::mValue),
+            C2FieldSupportedValues(
+                    mSupportedCodecProfiles.front(), mSupportedCodecProfiles.back(), 1u));
+
+    // TODO(johnylin): min/max resolution may change by chosen profile, we should dynamically change
+    // the supported values in the future.
+    insertParam(&mVideoSize);
+    mSupportedValues.emplace(
+            C2ParamField(&mVideoSize, &C2VideoSizeStreamInfo::mWidth),
+            C2FieldSupportedValues(mMinVideoSize.width(), mMaxVideoSize.width(), 16));
+    mSupportedValues.emplace(
+            C2ParamField(&mVideoSize, &C2VideoSizeStreamInfo::mHeight),
+            C2FieldSupportedValues(mMinVideoSize.height(), mMaxVideoSize.height(), 16));
+
+    insertParam(&mMaxVideoSizeHint);
+    mSupportedValues.emplace(
+            C2ParamField(&mMaxVideoSizeHint, &C2MaxVideoSizeHintPortSetting::mWidth),
+            C2FieldSupportedValues(mMinVideoSize.width(), mMaxVideoSize.width(), 16));
+    mSupportedValues.emplace(
+            C2ParamField(&mMaxVideoSizeHint, &C2MaxVideoSizeHintPortSetting::mHeight),
+            C2FieldSupportedValues(mMinVideoSize.height(), mMaxVideoSize.height(), 16));
+
+    mParamDescs.push_back(std::make_shared<C2ParamDescriptor>(
+            true, "_domain", &mDomainInfo));
+    mParamDescs.push_back(std::make_shared<C2ParamDescriptor>(
+            false, "_output_color_format", &mOutputColorFormat));
+    mParamDescs.push_back(std::make_shared<C2ParamDescriptor>(
+            true, "_input_port_mime", mInputPortMime.get()));
+    mParamDescs.push_back(std::make_shared<C2ParamDescriptor>(
+            true, "_output_port_mime", mOutputPortMime.get()));
+    mParamDescs.push_back(std::make_shared<C2ParamDescriptor>(
+            false, "_input_codec_profile", &mInputCodecProfile));
+    mParamDescs.push_back(std::make_shared<C2ParamDescriptor>(
+            false, "_video_size", &mVideoSize));
+    mParamDescs.push_back(std::make_shared<C2ParamDescriptor>(
+            false, "_max_video_size_hint", &mMaxVideoSizeHint));
+}
+
+C2String C2VDAComponentIntf::getName() const {
+    return kName;
+}
+
+node_id C2VDAComponentIntf::getId() const {
+    return kId;
+}
+
+status_t C2VDAComponentIntf::query_nb(
+        const std::vector<C2Param* const>& stackParams,
+        const std::vector<C2Param::Index>& heapParamIndices,
+        std::vector<std::unique_ptr<C2Param>>* const heapParams) const {
+    status_t err = C2_OK;
+    for (C2Param* const param : stackParams) {
+        if (!param || !*param) {
+            continue;
+        }
+
+        uint32_t index = restoreIndex(param);
+        C2Param* myParam = getParamByIndex(index);
+        if (!myParam || (myParam->size() != param->size())) {
+            param->invalidate();
+            err = C2_BAD_INDEX;
+            continue;
+        }
+
+        param->updateFrom(*myParam);
+    }
+
+    // heapParams should not be nullptr if heapParamIndices is not empty.
+    CHECK(heapParamIndices.size() == 0 || heapParams);
+    for (const C2Param::Index index : heapParamIndices) {
+        C2Param* myParam = getParamByIndex(index);
+        if (myParam) {
+            heapParams->emplace_back(C2Param::Copy(*myParam));
+        } else {
+            err = C2_BAD_INDEX;
+        }
+    }
+
+    return err;
+}
+
+status_t C2VDAComponentIntf::config_nb(
+        const std::vector<C2Param* const> &params,
+        std::vector<std::unique_ptr<C2SettingResult>>* const failures) {
+    status_t err = C2_OK;
+    for (C2Param* const param : params) {
+        uint32_t index = restoreIndex(param);
+        C2Param* myParam = getParamByIndex(index);
+        if (!myParam) {
+            // C2_BAD_INDEX should be the lowest priority except for C2_OK.
+            err = (err == C2_OK) ? C2_BAD_INDEX : err;
+            continue;
+        }
+
+        if (index == restoreIndex(&mDomainInfo)) {  // read-only
+            failures->push_back(reportReadOnlyFailure<decltype(mDomainInfo)>(param));
+            err = C2_BAD_VALUE;
+            continue;
+        } else if (index == restoreIndex(&mOutputColorFormat)) {  // read-only
+            failures->push_back(reportReadOnlyFailure<decltype(mOutputColorFormat)>(param));
+            err = C2_BAD_VALUE;
+            continue;
+        } else if (index == restoreIndex(mInputPortMime.get())) {  // read-only
+            failures->push_back(
+                    reportReadOnlyFlexFailure<std::remove_pointer<decltype(mInputPortMime.get())>::type>(param));
+            err = C2_BAD_VALUE;
+            continue;
+        } else if (index == restoreIndex(mOutputPortMime.get())) {  // read-only
+            failures->push_back(
+                    reportReadOnlyFlexFailure<std::remove_pointer<decltype(mOutputPortMime.get())>::type>(param));
+            err = C2_BAD_VALUE;
+            continue;
+        } else if (index == restoreIndex(&mInputCodecProfile)) {
+            std::unique_ptr<C2SettingResult> result = validateUint32Config<decltype(mInputCodecProfile)>(param);
+            if (result) {
+                failures->push_back(std::move(result));
+                err = C2_BAD_VALUE;
+                continue;
+            }
+        } else if (index == restoreIndex(&mVideoSize)) {
+            std::unique_ptr<C2SettingResult> result = validateVideoSizeConfig<decltype(mVideoSize)>(param);
+            if (result) {
+                failures->push_back(std::move(result));
+                err = C2_BAD_VALUE;
+                continue;
+            }
+        } else if (index == restoreIndex(&mMaxVideoSizeHint)) {
+            std::unique_ptr<C2SettingResult> result = validateVideoSizeConfig<decltype(mMaxVideoSizeHint)>(param);
+            if (result) {
+                failures->push_back(std::move(result));
+                err = C2_BAD_VALUE;
+                continue;
+            }
+        }
+        myParam->updateFrom(*param);
+    }
+    return err;
+}
+
+status_t C2VDAComponentIntf::commit_sm(
+        const std::vector<C2Param* const>& params,
+        std::vector<std::unique_ptr<C2SettingResult>>* const failures) {
+    UNUSED(params);
+    UNUSED(failures);
+    return C2_NOT_IMPLEMENTED;
+}
+
+status_t C2VDAComponentIntf::createTunnel_sm(node_id targetComponent) {
+    UNUSED(targetComponent);
+    return C2_UNSUPPORTED;  // Tunneling is not supported by now
+}
+
+status_t C2VDAComponentIntf::releaseTunnel_sm(node_id targetComponent) {
+    UNUSED(targetComponent);
+    return C2_UNSUPPORTED;  // Tunneling is not supported by now
+}
+
+std::shared_ptr<C2ParamReflector> C2VDAComponentIntf::getParamReflector() const {
+    return mParamReflector;
+}
+
+status_t C2VDAComponentIntf::getSupportedParams(
+        std::vector<std::shared_ptr<C2ParamDescriptor>>* const params) const {
+    params->insert(params->begin(), mParamDescs.begin(), mParamDescs.end());
+    return C2_OK;
+}
+
+status_t C2VDAComponentIntf::getSupportedValues(
+        const std::vector<const C2ParamField>& fields,
+        std::vector<C2FieldSupportedValues>* const values) const {
+    status_t err = C2_OK;
+    for (const auto& field : fields) {
+        if (mSupportedValues.count(field) == 0) {
+            err = C2_BAD_INDEX;
+            continue;
+        }
+        values->push_back(mSupportedValues.at(field));
+    }
+    return err;
+}
+
+C2Param* C2VDAComponentIntf::getParamByIndex(uint32_t index) const {
+    auto iter = mParams.find(index);
+    return (iter != mParams.end()) ? iter->second : nullptr;
+}
+
+template<class T>
+std::unique_ptr<C2SettingResult> C2VDAComponentIntf::validateVideoSizeConfig(
+        C2Param* c2Param) const {
+    T* videoSize = (T*)c2Param;
+
+    C2ParamField fieldWidth(videoSize, &T::mWidth);
+    const C2FieldSupportedValues &widths = mSupportedValues.at(fieldWidth);
+    CHECK_EQ(widths.type, C2FieldSupportedValues::RANGE);
+    if (!findInt32FromPrimitiveValues(videoSize->mWidth, widths)) {
+        std::unique_ptr<C2SettingResult> result(
+                new C2SettingResult { fieldWidth,
+                                      C2SettingResult::BAD_VALUE,
+                                      nullptr /* supportedValues */,
+                                      {} /* conflictinfFields */ });
+        result->supportedValues.reset(
+                new C2FieldSupportedValues(widths.range.min,
+                                           widths.range.max,
+                                           widths.range.step));
+        return result;
+    }
+
+    C2ParamField fieldHeight(videoSize, &T::mHeight);
+    const C2FieldSupportedValues &heights = mSupportedValues.at(fieldHeight);
+    CHECK_EQ(heights.type, C2FieldSupportedValues::RANGE);
+    if (!findInt32FromPrimitiveValues(videoSize->mHeight, heights)) {
+        std::unique_ptr<C2SettingResult> result(
+                new C2SettingResult { fieldHeight,
+                                      C2SettingResult::BAD_VALUE,
+                                      nullptr /* supportedValues */,
+                                      {} /* conflictinfFields */ });
+        result->supportedValues.reset(
+                new C2FieldSupportedValues(heights.range.min,
+                                           heights.range.max,
+                                           heights.range.step));
+        return result;
+    }
+
+    return nullptr;
+}
+
+template<class T>
+std::unique_ptr<C2SettingResult> C2VDAComponentIntf::validateUint32Config(
+        C2Param* c2Param) const {
+    T* config = (T*)c2Param;
+
+    C2ParamField field(config, &T::mValue);
+    const C2FieldSupportedValues &configs = mSupportedValues.at(field);
+    CHECK_EQ(configs.type, C2FieldSupportedValues::RANGE);
+    if (!findUint32FromPrimitiveValues(config->mValue, configs)) {
+        std::unique_ptr<C2SettingResult> result(
+                new C2SettingResult { field,
+                                      C2SettingResult::BAD_VALUE,
+                                      nullptr /* supportedValues */,
+                                      {} /* conflictinfFields */ });
+        result->supportedValues.reset(
+                new C2FieldSupportedValues(configs.range.min,
+                                           configs.range.max,
+                                           configs.range.step));
+        return result;
+    }
+
+    return nullptr;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+C2VDAComponent::C2VDAComponent(C2String name,
+                               node_id id,
+                               const std::shared_ptr<C2ComponentListener>& listener)
+    : mIntf(std::make_shared<C2VDAComponentIntf>(name, id)),
+      mListener(listener) {
+}
+
+C2VDAComponent::~C2VDAComponent() {
+}
+
+status_t C2VDAComponent::queue_nb(std::list<std::unique_ptr<C2Work>>* const items) {
+    UNUSED(items);
+    return C2_NOT_IMPLEMENTED;
+}
+
+status_t C2VDAComponent::announce_nb(const std::vector<C2WorkOutline>& items) {
+    UNUSED(items);
+    return C2_UNSUPPORTED;  // Tunneling is not supported by now
+}
+
+status_t C2VDAComponent::flush_sm(
+        bool flushThrough, std::list<std::unique_ptr<C2Work>>* const flushedWork) {
+    if (flushThrough)
+        return C2_UNSUPPORTED;  // Tunneling is not supported by now
+    UNUSED(flushedWork);
+    return C2_NOT_IMPLEMENTED;
+}
+
+status_t C2VDAComponent::drain_nb(bool drainThrough) {
+    if (drainThrough)
+        return C2_UNSUPPORTED;  // Tunneling is not supported by now
+    return C2_NOT_IMPLEMENTED;
+}
+
+status_t C2VDAComponent::start() {
+    return C2_NOT_IMPLEMENTED;
+}
+
+status_t C2VDAComponent::stop() {
+    return C2_NOT_IMPLEMENTED;
+}
+
+void C2VDAComponent::reset() {
+}
+
+void C2VDAComponent::release() {
+}
+
+std::shared_ptr<C2ComponentInterface> C2VDAComponent::intf() {
+    return mIntf;
+}
+
+void C2VDAComponent::providePictureBuffers(
+        uint32_t pixelFormat, uint32_t minNumBuffers, const media::Size& codedSize) {
+    UNUSED(pixelFormat);
+    UNUSED(minNumBuffers);
+    UNUSED(codedSize);
+}
+
+void C2VDAComponent::dismissPictureBuffer(int32_t picture_id) {
+    UNUSED(picture_id);
+}
+
+void C2VDAComponent::pictureReady(
+        int32_t picture_id, int32_t bitstream_id, const media::Rect& cropRect) {
+    UNUSED(picture_id);
+    UNUSED(bitstream_id);
+    UNUSED(cropRect);
+}
+
+void C2VDAComponent::notifyEndOfBitstreamBuffer(int32_t bitstream_id) {
+    UNUSED(bitstream_id);
+}
+
+void C2VDAComponent::notifyFlushDone() {
+}
+
+void C2VDAComponent::notifyResetDone() {
+}
+
+void C2VDAComponent::notifyError(VideoDecodeAcceleratorAdaptor::Result error) {
+    UNUSED(error);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+status_t C2VDAComponentStore::createComponent(
+        C2String name, std::shared_ptr<C2Component>* const component) {
+    UNUSED(name);
+    UNUSED(component);
+    return C2_NOT_IMPLEMENTED;
+}
+
+status_t C2VDAComponentStore::createInterface(
+        C2String name, std::shared_ptr<C2ComponentInterface>* const interface) {
+    interface->reset(new C2VDAComponentIntf(name, 12345));
+    return C2_OK;
+}
+
+std::vector<std::unique_ptr<const C2ComponentInfo>> C2VDAComponentStore::getComponents() {
+    return std::vector<std::unique_ptr<const C2ComponentInfo>>();
+}
+
+status_t C2VDAComponentStore::copyBuffer(
+        std::shared_ptr<C2GraphicBuffer> src, std::shared_ptr<C2GraphicBuffer> dst) {
+    UNUSED(src);
+    UNUSED(dst);
+    return C2_NOT_IMPLEMENTED;
+}
+
+status_t C2VDAComponentStore::query_nb(
+        const std::vector<C2Param* const>& stackParams,
+        const std::vector<C2Param::Index>& heapParamIndices,
+        std::vector<std::unique_ptr<C2Param>>* const heapParams) {
+    UNUSED(stackParams);
+    UNUSED(heapParamIndices);
+    UNUSED(heapParams);
+    return C2_NOT_IMPLEMENTED;
+}
+
+status_t C2VDAComponentStore::config_nb(
+        const std::vector<C2Param* const> &params,
+        std::list<std::unique_ptr<C2SettingResult>>* const failures) {
+    UNUSED(params);
+    UNUSED(failures);
+    return C2_NOT_IMPLEMENTED;
+}
+
+}  // namespace android
+
+// ---------------------- Factory Functions Interface ----------------
+
+using namespace android;
+
+extern "C" C2ComponentStore* create_store() {
+    return new C2VDAComponentStore();
+}
+
+extern "C" void destroy_store(C2ComponentStore* store) {
+    delete store;
+}
diff --git a/C2VDAComponent.h b/C2VDAComponent.h
new file mode 100644
index 0000000..4c5d79d
--- /dev/null
+++ b/C2VDAComponent.h
@@ -0,0 +1,170 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef C2_VDA_COMPONENT_H_
+#define C2_VDA_COMPONENT_H_
+
+#include <map>
+#include <unordered_map>
+
+#include "VideoDecodeAcceleratorAdaptor.h"
+#include "base/macros.h"
+#include "rect.h"
+#include "size.h"
+#include "video_decode_accelerator.h"
+
+#include <C2Component.h>
+#include <C2Param.h>
+
+namespace android {
+
+C2ENUM(
+    ColorFormat, uint32_t,
+    kColorFormatYUV420Flexible = 0x7F420888,
+)
+
+class C2VDAComponentIntf : public C2ComponentInterface {
+public:
+    C2VDAComponentIntf(C2String name, node_id id);
+    virtual ~C2VDAComponentIntf() {}
+
+    // Impementation of C2ComponentInterface interface
+    virtual C2String getName() const override;
+    virtual node_id getId() const override;
+    virtual status_t query_nb(
+            const std::vector<C2Param* const> &stackParams,
+            const std::vector<C2Param::Index> &heapParamIndices,
+            std::vector<std::unique_ptr<C2Param>>* const heapParams) const override;
+    virtual status_t config_nb(
+            const std::vector<C2Param* const>& params,
+            std::vector<std::unique_ptr<C2SettingResult>>* const failures) override;
+    virtual status_t commit_sm(
+            const std::vector<C2Param* const>& params,
+            std::vector<std::unique_ptr<C2SettingResult>>* const failures) override;
+    virtual status_t createTunnel_sm(node_id targetComponent) override;
+    virtual status_t releaseTunnel_sm(node_id targetComponent) override;
+    virtual std::shared_ptr<C2ParamReflector> getParamReflector() const override;
+    virtual status_t getSupportedParams(
+            std::vector<std::shared_ptr<C2ParamDescriptor>>* const params) const override;
+    virtual status_t getSupportedValues(
+            const std::vector<const C2ParamField>& fields,
+            std::vector<C2FieldSupportedValues>* const values) const override;
+
+private:
+    const C2String kName;
+    const node_id kId;
+    //TODO: in the future different codec (h264/vp8/vp9) would be different class inherited from a
+    //      base class. This static const should be moved to each super class.
+    static const uint32_t kInputFormatFourcc;
+
+    class ParamReflector;
+
+    C2Param* getParamByIndex(uint32_t index) const;
+    template<class T>
+    std::unique_ptr<C2SettingResult> validateVideoSizeConfig(C2Param* c2Param) const;
+    template<class T>
+    std::unique_ptr<C2SettingResult> validateUint32Config(C2Param* c2Param) const;
+
+    std::shared_ptr<C2ParamReflector> mParamReflector;
+
+    // The following parameters are read-only.
+
+    // The component domain; should be C2DomainVideo.
+    C2ComponentDomainInfo mDomainInfo;
+    // The color format of video output.
+    C2StreamFormatConfig::output mOutputColorFormat;
+    // The MIME type of input port.
+    std::unique_ptr<C2PortMimeConfig::input> mInputPortMime;
+    // The MIME type of output port; should be MEDIA_MIMETYPE_VIDEO_RAW.
+    std::unique_ptr<C2PortMimeConfig::output> mOutputPortMime;
+
+    // The following parameters are also writable.
+
+    // The input video codec profile.
+    C2StreamFormatConfig::input mInputCodecProfile;
+    // Decoded video size for output.
+    C2VideoSizeStreamInfo::output mVideoSize;
+    // Max video size for video decoder.
+    C2MaxVideoSizeHintPortSetting::input mMaxVideoSizeHint;
+
+    std::unordered_map<uint32_t, C2Param*> mParams;
+    // C2ParamField is LessThanComparable
+    std::map<C2ParamField, C2FieldSupportedValues> mSupportedValues;
+    std::vector<std::shared_ptr<C2ParamDescriptor>> mParamDescs;
+
+    media::VideoDecodeAccelerator::SupportedProfiles mSupportedProfiles;
+    std::vector<uint32_t> mSupportedCodecProfiles;
+    media::Size mMaxVideoSize;
+    media::Size mMinVideoSize;
+
+};
+
+class C2VDAComponent
+    : public C2Component,
+      public VideoDecodeAcceleratorAdaptor::Client,
+      public std::enable_shared_from_this<C2VDAComponent> {
+public:
+    C2VDAComponent(
+            C2String name, node_id id, const std::shared_ptr<C2ComponentListener>& listener);
+    virtual ~C2VDAComponent() override;
+
+    // Implementation of C2Component interface
+    virtual status_t queue_nb(std::list<std::unique_ptr<C2Work>>* const items) override;
+    virtual status_t announce_nb(const std::vector<C2WorkOutline>& items) override;
+    virtual status_t flush_sm(
+            bool flushThrough, std::list<std::unique_ptr<C2Work>>* const flushedWork) override;
+    virtual status_t drain_nb(bool drainThrough) override;
+    virtual status_t start() override;
+    virtual status_t stop() override;
+    virtual void reset() override;
+    virtual void release() override;
+    virtual std::shared_ptr<C2ComponentInterface> intf() override;
+
+    // Implementation of VideDecodeAcceleratorAdaptor::Client interface
+    virtual void providePictureBuffers(uint32_t pixelFormat,
+                                       uint32_t minNumBuffers,
+                                       const media::Size& codedSize) override;
+    virtual void dismissPictureBuffer(int32_t picture_id) override;
+    virtual void pictureReady(int32_t picture_id, int32_t bitstream_id,
+                              const media::Rect& cropRect) override;
+    virtual void notifyEndOfBitstreamBuffer(int32_t bitstream_id) override;
+    virtual void notifyFlushDone() override;
+    virtual void notifyResetDone() override;
+    virtual void notifyError(VideoDecodeAcceleratorAdaptor::Result error) override;
+private:
+    const std::shared_ptr<C2VDAComponentIntf> mIntf;
+    const std::shared_ptr<C2ComponentListener> mListener;
+
+    std::unique_ptr<VideoDecodeAcceleratorAdaptor> mVDAAdaptor;
+
+    DISALLOW_COPY_AND_ASSIGN(C2VDAComponent);
+};
+
+class C2VDAComponentStore : public C2ComponentStore {
+public:
+    C2VDAComponentStore() {}
+    ~C2VDAComponentStore() override {}
+
+    status_t createComponent(C2String name,
+                             std::shared_ptr<C2Component>* const component) override;
+
+    status_t createInterface(C2String name,
+                             std::shared_ptr<C2ComponentInterface>* const interface) override;
+
+    std::vector<std::unique_ptr<const C2ComponentInfo>> getComponents() override;
+
+    status_t copyBuffer(std::shared_ptr<C2GraphicBuffer> src,
+                        std::shared_ptr<C2GraphicBuffer> dst) override;
+
+    status_t query_nb(const std::vector<C2Param* const>& stackParams,
+                      const std::vector<C2Param::Index>& heapParamIndices,
+                      std::vector<std::unique_ptr<C2Param>>* const heapParams) override;
+
+    status_t config_nb(const std::vector<C2Param* const>& params,
+                       std::list<std::unique_ptr<C2SettingResult>>* const failures) override;
+};
+
+}  // namespace android
+
+#endif  // C2_VDA_COMPONENT_H_
diff --git a/tests/Android.mk b/tests/Android.mk
new file mode 100644
index 0000000..fa02126
--- /dev/null
+++ b/tests/Android.mk
@@ -0,0 +1,31 @@
+# Build the unit tests.
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
+
+LOCAL_MODULE := C2VDAComponent_test
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := \
+  C2VDAComponent_test.cpp \
+
+LOCAL_SHARED_LIBRARIES := \
+  libchrome \
+  libcutils \
+  liblog \
+  libutils \
+  libv4l2_codec2 \
+  libv4l2_codec2_vda \
+
+LOCAL_C_INCLUDES += \
+  $(TOP)/external/v4l2_codec2 \
+  $(TOP)/external/v4l2_codec2/vda \
+  $(TOP)/frameworks/av/media/libstagefright/codec2/include \
+
+LOCAL_CFLAGS += -Werror -Wall -std=c++14
+LOCAL_CLANG := true
+
+LOCAL_LDFLAGS := -Wl,-Bsymbolic
+
+include $(BUILD_NATIVE_TEST)
diff --git a/tests/C2VDAComponent_test.cpp b/tests/C2VDAComponent_test.cpp
new file mode 100644
index 0000000..b85d868
--- /dev/null
+++ b/tests/C2VDAComponent_test.cpp
@@ -0,0 +1,469 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "C2VDAComponent_test"
+
+#include <limits>
+#include <stdio.h>
+
+#include <C2VDAComponent.h>
+
+#include <gtest/gtest.h>
+#include <utils/Log.h>
+
+#define UNUSED(expr) do { (void)(expr); } while (0)
+
+namespace android {
+
+template <class T>
+std::unique_ptr<T> alloc_unique_cstr(const char* cstr) {
+    size_t len = strlen(cstr);
+    std::unique_ptr<T> ptr = T::alloc_unique(len);
+    memcpy(ptr->m.mValue, cstr, len);
+    return ptr;
+}
+
+// TODO(johnylin): should be fixed by ag/1930596, remove this then.
+C2ComponentListener::~C2ComponentListener() {}
+
+class TestListener: public C2ComponentListener {
+public:
+    ~TestListener() override {}
+    void onWorkDone(std::weak_ptr<C2Component> component,
+                    std::vector<std::unique_ptr<C2Work>> workItems) override {
+        UNUSED(workItems);
+        auto comp = component.lock();
+        printf("TestListener::onWorkDone from component %s\n",
+               comp->intf()->getName().c_str());
+    }
+
+    void onTripped(std::weak_ptr<C2Component> component,
+                   std::vector<std::shared_ptr<C2SettingResult>> settingResult) override {
+        UNUSED(settingResult);
+        auto comp = component.lock();
+        printf("TestListener::onTripped from component %s\n",
+               comp->intf()->getName().c_str());
+    }
+
+    void onError(std::weak_ptr<C2Component> component,
+                 uint32_t errorCode) override {
+        auto comp = component.lock();
+        printf("TestListener::onError Errno = %u from component %s\n",
+               errorCode, comp->intf()->getName().c_str());
+    }
+};
+
+const C2String testCompName = "v4l2.decoder";
+const node_id testCompNodeId = 12345;
+
+const char* MEDIA_MIMETYPE_VIDEO_RAW = "video/raw";
+const char* MEDIA_MIMETYPE_VIDEO_AVC = "video/avc";
+
+class C2VDAComponentTest : public ::testing::Test {
+protected:
+    C2VDAComponentTest() {
+        mListener = std::make_shared<TestListener>();
+        mComponent = std::make_shared<C2VDAComponent>(testCompName, testCompNodeId, mListener);
+    }
+    ~C2VDAComponentTest() override {}
+
+    void SetUp() override {
+        mIntf = mComponent->intf();
+    }
+
+    template <typename T>
+    void testReadOnlyParam(const T* expected, const T* invalid);
+
+    template <typename T>
+    void testReadOnlyParamOnStack(const T* expected, const T* invalid);
+
+    template <typename T>
+    void testReadOnlyParamOnHeap(const T* expected, const T* invalid);
+
+    template <typename T>
+    void testReadOnlyFlexParam(
+            const std::unique_ptr<T>& expected, const std::unique_ptr<T>& invalid);
+
+    template <typename T>
+    void testWritableParam(const T* const newParam);
+
+    template <typename T>
+    void testInvalidWritableParam(const T* const invalidParam);
+
+    template <typename T>
+    void testWritableVideoSizeParam(int32_t widthMin, int32_t widthMax, int32_t widthStep,
+                                    int32_t heightMin, int32_t heightMax, int32_t heightStep);
+
+    std::shared_ptr<C2Component> mComponent;
+    std::shared_ptr<C2ComponentListener> mListener;
+    std::shared_ptr<C2ComponentInterface> mIntf;
+};
+
+template <typename T>
+void C2VDAComponentTest::testReadOnlyParam(const T* expected, const T* invalid) {
+    testReadOnlyParamOnStack(expected, invalid);
+    testReadOnlyParamOnHeap(expected, invalid);
+}
+
+template <typename T>
+void C2VDAComponentTest::testReadOnlyParamOnStack(const T* expected, const T* invalid) {
+    T param;
+    std::vector<C2Param* const> stackParams{ &param };
+    ASSERT_EQ(C2_OK, mIntf->query_nb(stackParams, {}, nullptr));
+    EXPECT_EQ(*expected, param);
+
+    std::vector<C2Param* const> params{ (C2Param* const)invalid };
+    std::vector<std::unique_ptr<C2SettingResult>> failures;
+    ASSERT_EQ(C2_BAD_VALUE, mIntf->config_nb(params, &failures));
+    ASSERT_EQ(1u, failures.size());
+    EXPECT_EQ(C2SettingResult::READ_ONLY, failures[0]->failure);
+
+    // The param must not change after failed config.
+    ASSERT_EQ(C2_OK, mIntf->query_nb(stackParams, {}, nullptr));
+    EXPECT_EQ(*expected, param);
+}
+
+template <typename T>
+void C2VDAComponentTest::testReadOnlyParamOnHeap(const T* expected, const T* invalid) {
+    std::vector<std::unique_ptr<C2Param>> heapParams;
+
+    uint32_t index = expected->type();
+    if (expected->forStream()) {
+        index |= ((expected->stream() << 17) & 0x01FE0000) | 0x02000000;
+    }
+
+    ASSERT_EQ(C2_OK, mIntf->query_nb({}, {index}, &heapParams));
+    ASSERT_EQ(1u, heapParams.size());
+    EXPECT_EQ(*expected, *heapParams[0]);
+
+    std::vector<C2Param* const> params{ (C2Param* const)invalid };
+    std::vector<std::unique_ptr<C2SettingResult>> failures;
+    ASSERT_EQ(C2_BAD_VALUE, mIntf->config_nb(params, &failures));
+    ASSERT_EQ(1u, failures.size());
+    EXPECT_EQ(C2SettingResult::READ_ONLY, failures[0]->failure);
+
+    // The param must not change after failed config.
+    heapParams.clear();
+    ASSERT_EQ(C2_OK, mIntf->query_nb({}, {index}, &heapParams));
+    ASSERT_EQ(1u, heapParams.size());
+    EXPECT_EQ(*expected, *heapParams[0]);
+}
+
+template <typename T>
+void C2VDAComponentTest::testReadOnlyFlexParam(
+        const std::unique_ptr<T>& expected, const std::unique_ptr<T>& invalid) {
+    std::vector<std::unique_ptr<C2Param>> heapParams;
+
+    uint32_t index = expected->type();
+    if (expected->forStream()) {
+        index |= ((expected->stream() << 17) & 0x01FE0000) | 0x02000000;
+    }
+
+    ASSERT_EQ(C2_OK, mIntf->query_nb({}, {index}, &heapParams));
+    ASSERT_EQ(1u, heapParams.size());
+    EXPECT_EQ(*expected, *heapParams[0]);
+
+    std::vector<C2Param* const> params{ invalid.get() };
+    std::vector<std::unique_ptr<C2SettingResult>> failures;
+    ASSERT_EQ(C2_BAD_VALUE, mIntf->config_nb(params, &failures));
+    ASSERT_EQ(1u, failures.size());
+    EXPECT_EQ(C2SettingResult::READ_ONLY, failures[0]->failure);
+
+    // The param must not change after failed config.
+    heapParams.clear();
+    ASSERT_EQ(C2_OK, mIntf->query_nb({}, {index}, &heapParams));
+    ASSERT_EQ(1u, heapParams.size());
+    EXPECT_EQ(*expected, *heapParams[0]);
+}
+
+template <typename T>
+void C2VDAComponentTest::testWritableParam(const T* const newParam) {
+    std::vector<C2Param* const> params{ (C2Param* const)newParam };
+    std::vector<std::unique_ptr<C2SettingResult>> failures;
+    ASSERT_EQ(C2_OK, mIntf->config_nb(params, &failures));
+    EXPECT_EQ(0u, failures.size());
+
+    // The param must change to newParam
+    // Check like param on stack
+    T param;
+    std::vector<C2Param* const> stackParams{ &param };
+    ASSERT_EQ(C2_OK, mIntf->query_nb(stackParams, {}, nullptr));
+    EXPECT_EQ(*newParam, param);
+
+    // Check also like param on heap
+    std::vector<std::unique_ptr<C2Param>> heapParams;
+    uint32_t index = newParam->type();
+    if (newParam->forStream()) {
+        index |= ((newParam->stream() << 17) & 0x01FE0000) | 0x02000000;
+    }
+    ASSERT_EQ(C2_OK, mIntf->query_nb({}, {index}, &heapParams));
+    ASSERT_EQ(1u, heapParams.size());
+    EXPECT_EQ(*newParam, *heapParams[0]);
+}
+
+template <typename T>
+void C2VDAComponentTest::testInvalidWritableParam(const T* const invalidParam) {
+    // Get the current parameter info
+    T preParam;
+    std::vector<C2Param* const> stackParams { &preParam };
+    ASSERT_EQ(C2_OK, mIntf->query_nb(stackParams, {}, nullptr));
+
+    // Config invalid value. The failure is expected
+    std::vector<C2Param* const> params{ (C2Param* const)invalidParam };
+    std::vector<std::unique_ptr<C2SettingResult>> failures;
+    ASSERT_EQ(C2_BAD_VALUE, mIntf->config_nb(params, &failures));
+    EXPECT_EQ(1u, failures.size());
+
+    //The param must not change after config failed
+    T param;
+    std::vector<C2Param* const> stackParams2 { &param };
+    ASSERT_EQ(C2_OK, mIntf->query_nb(stackParams2, {}, nullptr));
+    EXPECT_EQ(preParam, param);
+
+    // Check also like param on heap
+    std::vector<std::unique_ptr<C2Param>> heapParams;
+    uint32_t index = invalidParam->type();
+    if (invalidParam->forStream()) {
+        index |= ((invalidParam->stream() << 17) & 0x01FE0000) | 0x02000000;
+    }
+    ASSERT_EQ(C2_OK, mIntf->query_nb({}, {index}, &heapParams));
+    ASSERT_EQ(1u, heapParams.size());
+    EXPECT_EQ(preParam, *heapParams[0]);
+}
+
+bool isUnderflowSubstract(int32_t a, int32_t b) {
+    return a < 0 && b > a - std::numeric_limits<int32_t>::min();
+}
+
+bool isOverflowAdd(int32_t a, int32_t b) {
+    return a > 0 && b > std::numeric_limits<int32_t>::max() - a;
+}
+
+template <typename T>
+void C2VDAComponentTest::testWritableVideoSizeParam(
+        int32_t widthMin, int32_t widthMax, int32_t widthStep,
+        int32_t heightMin, int32_t heightMax, int32_t heightStep) {
+    // Test supported values of video size
+    T valid;
+    for (int32_t h = heightMin; h <= heightMax; h += heightStep) {
+        for (int32_t w = widthMin; w <= widthMax; w += widthStep) {
+            valid.mWidth = w;
+            valid.mHeight = h;
+            {
+                SCOPED_TRACE("testWritableParam");
+                testWritableParam(&valid);
+                if (HasFailure()) {
+                    printf("Failed while config width = %d, height = %d\n",
+                           valid.mWidth, valid.mHeight);
+                }
+                if (HasFatalFailure()) return;
+            }
+        }
+    }
+
+    // Test invalid values video size
+    T invalid;
+    // Width or height is smaller than min values
+    if (!isUnderflowSubstract(widthMin, widthStep)) {
+        invalid.mWidth = widthMin - widthStep;
+        invalid.mHeight = heightMin;
+        testInvalidWritableParam(&invalid);
+    }
+    if (!isUnderflowSubstract(heightMin, heightStep)) {
+        invalid.mWidth = widthMin;
+        invalid.mHeight = heightMin - heightStep;
+        testInvalidWritableParam(&invalid);
+    }
+
+    // Width or height is bigger than max values
+    if (!isOverflowAdd(widthMax, widthStep)) {
+        invalid.mWidth = widthMax + widthStep;
+        invalid.mHeight = heightMax;
+        testInvalidWritableParam(&invalid);
+    }
+    if (!isOverflowAdd(heightMax, heightStep)) {
+        invalid.mWidth = widthMax;
+        invalid.mHeight = heightMax + heightStep;
+        testInvalidWritableParam(&invalid);
+    }
+
+    // Invalid width/height within the range
+    if (widthStep != 1) {
+        invalid.mWidth = widthMin + 1;
+        invalid.mHeight = heightMin;
+        testInvalidWritableParam(&invalid);
+    }
+    if (heightStep != 1) {
+        invalid.mWidth = widthMin;
+        invalid.mHeight = heightMin + 1;
+        testInvalidWritableParam(&invalid);
+    }
+}
+
+#define TRACED_FAILURE(func) do { \
+    SCOPED_TRACE(#func); \
+    func; \
+    if (::testing::Test::HasFatalFailure()) return; \
+} while (false)
+
+TEST_F(C2VDAComponentTest, CreateInstance) {
+    auto name = mIntf->getName();
+    auto id = mIntf->getId();
+    printf("name = %s\n", name.c_str());
+    printf("node_id = %u\n", id);
+    EXPECT_STREQ(name.c_str(), testCompName.c_str());
+    EXPECT_EQ(id, testCompNodeId);
+}
+
+TEST_F(C2VDAComponentTest, TestDomainInfo) {
+    C2ComponentDomainInfo expected(C2DomainVideo);
+    C2ComponentDomainInfo invalid(C2DomainAudio);
+    TRACED_FAILURE(testReadOnlyParam(&expected, &invalid));
+}
+
+TEST_F(C2VDAComponentTest, TestOutputColorFormat) {
+    C2StreamFormatConfig::output expected(0u, kColorFormatYUV420Flexible);
+    C2StreamFormatConfig::output invalid(0u, 0xdeadbeef);
+    TRACED_FAILURE(testReadOnlyParam(&expected, &invalid));
+}
+
+TEST_F(C2VDAComponentTest, TestInputPortMime) {
+    std::unique_ptr<C2PortMimeConfig::input> expected(
+            alloc_unique_cstr<C2PortMimeConfig::input>(MEDIA_MIMETYPE_VIDEO_AVC));
+    std::unique_ptr<C2PortMimeConfig::input> invalid(
+            alloc_unique_cstr<C2PortMimeConfig::input>(MEDIA_MIMETYPE_VIDEO_RAW));
+    TRACED_FAILURE(testReadOnlyFlexParam(expected, invalid));
+}
+
+TEST_F(C2VDAComponentTest, TestOutputPortMime) {
+    std::unique_ptr<C2PortMimeConfig::output> expected(
+            alloc_unique_cstr<C2PortMimeConfig::output>(MEDIA_MIMETYPE_VIDEO_RAW));
+    std::unique_ptr<C2PortMimeConfig::output> invalid(
+            alloc_unique_cstr<C2PortMimeConfig::output>(MEDIA_MIMETYPE_VIDEO_AVC));
+    TRACED_FAILURE(testReadOnlyFlexParam(expected, invalid));
+}
+
+TEST_F(C2VDAComponentTest, TestVideoSize) {
+    C2VideoSizeStreamInfo::output videoSize;
+    std::vector<C2FieldSupportedValues> widthC2FSV;
+    ASSERT_EQ(
+        C2_OK,
+        mIntf->getSupportedValues({ C2ParamField(&videoSize, &C2VideoSizeStreamInfo::mWidth) },
+                                  &widthC2FSV));
+    std::vector<C2FieldSupportedValues> heightC2FSV;
+    ASSERT_EQ(
+        C2_OK,
+        mIntf->getSupportedValues({ C2ParamField(&videoSize, &C2VideoSizeStreamInfo::mHeight) },
+                                  &heightC2FSV));
+    ASSERT_EQ(1u, widthC2FSV.size());
+    auto& widthFSVRange = widthC2FSV[0].range;
+    int32_t widthMin = widthFSVRange.min.i32;
+    int32_t widthMax = widthFSVRange.max.i32;
+    int32_t widthStep = widthFSVRange.step.i32;
+
+    ASSERT_EQ(1u, heightC2FSV.size());
+    auto& heightFSVRange = heightC2FSV[0].range;
+    int32_t heightMin = heightFSVRange.min.i32;
+    int32_t heightMax = heightFSVRange.max.i32;
+    int32_t heightStep = heightFSVRange.step.i32;
+
+    // test updating invalid values
+    TRACED_FAILURE(testWritableVideoSizeParam<C2VideoSizeStreamInfo::output>(
+            widthMin, widthMax, widthStep, heightMin, heightMax, heightStep));
+}
+
+TEST_F(C2VDAComponentTest, TestMaxVideoSizeHint) {
+    C2MaxVideoSizeHintPortSetting::input maxVideoSizeHint;
+    std::vector<C2FieldSupportedValues> widthC2FSV;
+    mIntf->getSupportedValues({ C2ParamField(
+                                &maxVideoSizeHint,
+                                &C2MaxVideoSizeHintPortSetting::mWidth) }, &widthC2FSV);
+    std::vector<C2FieldSupportedValues> heightC2FSV;
+    mIntf->getSupportedValues({ C2ParamField(
+                                &maxVideoSizeHint,
+                                &C2MaxVideoSizeHintPortSetting::mHeight) }, &heightC2FSV);
+
+    ASSERT_EQ(1u, widthC2FSV.size());
+    auto &widthFSVRange = widthC2FSV[0].range;
+    int32_t widthMin = widthFSVRange.min.i32;
+    int32_t widthMax = widthFSVRange.max.i32;
+    int32_t widthStep = widthFSVRange.step.i32;
+
+    ASSERT_EQ(1u, heightC2FSV.size());
+    auto &heightFSVRange = heightC2FSV[0].range;
+    int32_t heightMin = heightFSVRange.min.i32;
+    int32_t heightMax = heightFSVRange.max.i32;
+    int32_t heightStep = heightFSVRange.step.i32;
+
+    TRACED_FAILURE(testWritableVideoSizeParam<C2MaxVideoSizeHintPortSetting::input>(
+            widthMin, widthMax, widthStep, heightMin, heightMax, heightStep));
+}
+
+TEST_F(C2VDAComponentTest, TestInputCodecProfile) {
+    C2StreamFormatConfig::input codecProfile;
+    std::vector<C2FieldSupportedValues> profileValues;
+    ASSERT_EQ(
+        C2_OK,
+        mIntf->getSupportedValues({ C2ParamField(&codecProfile, &C2StreamFormatConfig::mValue) },
+                                  &profileValues));
+    ASSERT_EQ(1u, profileValues.size());
+
+    uint32_t profileMin =   profileValues[0].range.min.u32;
+    uint32_t profileMax =   profileValues[0].range.max.u32;
+    //uint32_t profileStep =  profileValues[0].range.step.u32;
+    printf("Supported codec profile = [ %u, %u]\n", profileMin, profileMax);
+
+    codecProfile.mValue = profileMin;
+    TRACED_FAILURE(testWritableParam(&codecProfile));
+    codecProfile.mValue = profileMax;
+    TRACED_FAILURE(testWritableParam(&codecProfile));
+    codecProfile.mValue = profileMax + 1;
+    TRACED_FAILURE(testInvalidWritableParam(&codecProfile));
+}
+
+TEST_F(C2VDAComponentTest, TestUnsupportedParam) {
+    C2ComponentTemporalInfo unsupportedParam;
+    std::vector<C2Param* const> stackParams{ &unsupportedParam };
+    ASSERT_EQ(C2_BAD_INDEX, mIntf->query_nb(stackParams, {}, nullptr));
+    EXPECT_EQ(0u, unsupportedParam.size());  // invalidated
+}
+
+void dumpType(const C2FieldDescriptor::Type type) {
+    switch (type) {
+    case C2FieldDescriptor::INT32: printf("int32_t"); break;
+    case C2FieldDescriptor::UINT32: printf("uint32_t"); break;
+    case C2FieldDescriptor::INT64: printf("int64_t"); break;
+    case C2FieldDescriptor::UINT64: printf("uint64_t"); break;
+    case C2FieldDescriptor::FLOAT: printf("float"); break;
+    default: printf("<flex>"); break;
+    }
+}
+
+void dumpStruct(const C2StructDescriptor& sd) {
+    printf("  struct: { ");
+    for (const C2FieldDescriptor& f : sd) {
+        printf("%s:", f.name());
+        dumpType(f.type());
+        printf(", ");
+    }
+    printf("}\n");
+}
+
+TEST_F(C2VDAComponentTest, ParamReflector) {
+    std::vector<std::shared_ptr<C2ParamDescriptor>> params;
+
+    ASSERT_EQ(mIntf->getSupportedParams(&params), C2_OK);
+    for (const auto& paramDesc : params) {
+        printf("name: %s\n", paramDesc->name().c_str());
+        printf("  required: %s\n", paramDesc->isRequired() ? "yes" : "no");
+        printf("  type: %x\n", paramDesc->type().type());
+        std::unique_ptr<C2StructDescriptor> desc{
+                mIntf->getParamReflector()->describe(paramDesc->type().type())};
+        if (desc.get())
+            dumpStruct(*desc);
+    }
+}
+
+}  // namespace android