Move Codec2-related code from hardware/google/av

Test: None
Bug: 112362730
Change-Id: Ie2f8ff431d65c40333f267ab9877d47089adeea4
diff --git a/media/codec2/vndk/Android.bp b/media/codec2/vndk/Android.bp
new file mode 100644
index 0000000..cabe7ee
--- /dev/null
+++ b/media/codec2/vndk/Android.bp
@@ -0,0 +1,105 @@
+cc_library_headers {
+    name: "libstagefright_codec2_internal",
+
+    export_include_dirs: [
+        "internal",
+    ],
+
+    // TODO: Remove this when this module is moved back to frameworks/av.
+    vendor_available: true,
+}
+
+// !!!DO NOT DEPEND ON THIS SHARED LIBRARY DIRECTLY!!!
+// use libstagefright_codec2-impl-defaults instead
+cc_library_shared {
+    name: "libstagefright_codec2_vndk",
+    vendor_available: true,
+
+    srcs: [
+        "C2AllocatorIon.cpp",
+        "C2AllocatorGralloc.cpp",
+        "C2Buffer.cpp",
+        "C2Config.cpp",
+        "C2PlatformStorePluginLoader.cpp",
+        "C2Store.cpp",
+        "platform/C2BqBuffer.cpp",
+        "util/C2Debug.cpp",
+        "util/C2InterfaceHelper.cpp",
+        "util/C2InterfaceUtils.cpp",
+        "util/C2ParamUtils.cpp",
+    ],
+
+    export_include_dirs: [
+        "include",
+    ],
+
+    export_shared_lib_headers: [
+        "libbase",
+        "android.hardware.media.bufferpool@1.0",
+    ],
+
+    local_include_dirs: [
+        "internal",
+    ],
+
+    include_dirs: [
+        "frameworks/native/include/media/hardware",
+        "hardware/google/av/codec2/include",
+    ],
+
+    shared_libs: [
+        "android.hardware.graphics.allocator@2.0",
+        "android.hardware.graphics.bufferqueue@1.0",
+        "android.hardware.graphics.mapper@2.0",
+        "android.hardware.media.bufferpool@1.0",
+        "libbase",
+        "libbinder",
+        "libcutils",
+        "libdl",
+        "libhardware",
+        "libhidlbase",
+        "libion",
+        "libfmq",
+        "liblog",
+        "libstagefright_bufferqueue_helper",
+        "libstagefright_foundation",
+        "libstagefright_bufferpool@1.0",
+        "libui",
+        "libutils",
+    ],
+
+    cflags: [
+        "-Werror",
+        "-Wall",
+    ],
+}
+
+// public dependency for implementing Codec 2 components
+cc_defaults {
+    name: "libstagefright_codec2-impl-defaults",
+
+    shared_libs: [
+        "libbase", // for C2_LOG
+        "liblog", // for ALOG
+        "libstagefright_codec2",
+        "libstagefright_codec2_vndk",
+        "libutils",
+    ],
+}
+
+// public dependency for implementing Codec 2 framework utilities
+// THIS IS ONLY FOR FRAMEWORK USE ONLY
+cc_defaults {
+    name: "libstagefright_codec2-internal-defaults",
+    defaults: ["libstagefright_codec2-impl-defaults"],
+
+    shared_libs: [
+        "libcutils", // for properties
+    ],
+
+    // TODO: separate internal headers so they can be exposed here
+}
+
+subdirs = [
+    "bufferpool",
+]
diff --git a/media/codec2/vndk/C2AllocatorGralloc.cpp b/media/codec2/vndk/C2AllocatorGralloc.cpp
new file mode 100644
index 0000000..22e8d84
--- /dev/null
+++ b/media/codec2/vndk/C2AllocatorGralloc.cpp
@@ -0,0 +1,743 @@
+/*
+ * Copyright (C) 2016 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_NDEBUG 0
+#define LOG_TAG "C2AllocatorGralloc"
+#include <utils/Log.h>
+
+#include <android/hardware/graphics/allocator/2.0/IAllocator.h>
+#include <android/hardware/graphics/mapper/2.0/IMapper.h>
+#include <cutils/native_handle.h>
+#include <hardware/gralloc.h>
+
+#include <C2AllocatorGralloc.h>
+#include <C2Buffer.h>
+#include <C2PlatformSupport.h>
+
+namespace android {
+
+namespace {
+    enum : uint64_t {
+        /**
+         * Usage mask that is passed through from gralloc to Codec 2.0 usage.
+         */
+        PASSTHROUGH_USAGE_MASK =
+            ~(GRALLOC_USAGE_SW_READ_MASK | GRALLOC_USAGE_SW_WRITE_MASK | GRALLOC_USAGE_PROTECTED)
+    };
+
+    // verify that passthrough mask is within the platform mask
+    static_assert((~C2MemoryUsage::PLATFORM_MASK & PASSTHROUGH_USAGE_MASK) == 0, "");
+}
+
+C2MemoryUsage C2AndroidMemoryUsage::FromGrallocUsage(uint64_t usage) {
+    // gralloc does not support WRITE_PROTECTED
+    return C2MemoryUsage(
+            ((usage & GRALLOC_USAGE_SW_READ_MASK) ? C2MemoryUsage::CPU_READ : 0) |
+            ((usage & GRALLOC_USAGE_SW_WRITE_MASK) ? C2MemoryUsage::CPU_WRITE : 0) |
+            ((usage & GRALLOC_USAGE_PROTECTED) ? C2MemoryUsage::READ_PROTECTED : 0) |
+            (usage & PASSTHROUGH_USAGE_MASK));
+}
+
+uint64_t C2AndroidMemoryUsage::asGrallocUsage() const {
+    // gralloc does not support WRITE_PROTECTED
+    return (((expected & C2MemoryUsage::CPU_READ) ? GRALLOC_USAGE_SW_READ_OFTEN : 0) |
+            ((expected & C2MemoryUsage::CPU_WRITE) ? GRALLOC_USAGE_SW_WRITE_OFTEN : 0) |
+            ((expected & C2MemoryUsage::READ_PROTECTED) ? GRALLOC_USAGE_PROTECTED : 0) |
+            (expected & PASSTHROUGH_USAGE_MASK));
+}
+
+using ::android::hardware::graphics::allocator::V2_0::IAllocator;
+using ::android::hardware::graphics::common::V1_0::BufferUsage;
+using ::android::hardware::graphics::common::V1_0::PixelFormat;
+using ::android::hardware::graphics::mapper::V2_0::BufferDescriptor;
+using ::android::hardware::graphics::mapper::V2_0::Error;
+using ::android::hardware::graphics::mapper::V2_0::IMapper;
+using ::android::hardware::graphics::mapper::V2_0::YCbCrLayout;
+using ::android::hardware::hidl_handle;
+using ::android::hardware::hidl_vec;
+
+namespace {
+
+struct BufferDescriptorInfo {
+    IMapper::BufferDescriptorInfo mapperInfo;
+    uint32_t stride;
+};
+
+}
+
+/* ===================================== GRALLOC ALLOCATION ==================================== */
+static c2_status_t maperr2error(Error maperr) {
+    switch (maperr) {
+        case Error::NONE:           return C2_OK;
+        case Error::BAD_DESCRIPTOR: return C2_BAD_VALUE;
+        case Error::BAD_BUFFER:     return C2_BAD_VALUE;
+        case Error::BAD_VALUE:      return C2_BAD_VALUE;
+        case Error::NO_RESOURCES:   return C2_NO_MEMORY;
+        case Error::UNSUPPORTED:    return C2_CANNOT_DO;
+    }
+    return C2_CORRUPTED;
+}
+
+static
+bool native_handle_is_invalid(const native_handle_t *const handle) {
+    // perform basic validation of a native handle
+    if (handle == nullptr) {
+        // null handle is considered valid
+        return false;
+    }
+    return ((size_t)handle->version != sizeof(native_handle_t) ||
+            handle->numFds < 0 ||
+            handle->numInts < 0 ||
+            // for sanity assume handles must occupy less memory than INT_MAX bytes
+            handle->numFds > int((INT_MAX - handle->version) / sizeof(int)) - handle->numInts);
+}
+
+class C2HandleGralloc : public C2Handle {
+private:
+    struct ExtraData {
+        uint32_t width;
+        uint32_t height;
+        uint32_t format;
+        uint32_t usage_lo;
+        uint32_t usage_hi;
+        uint32_t stride;
+        uint32_t generation;
+        uint32_t igbp_id_lo;
+        uint32_t igbp_id_hi;
+        uint32_t igbp_slot;
+        uint32_t magic;
+    };
+
+    enum {
+        NUM_INTS = sizeof(ExtraData) / sizeof(int),
+    };
+    const static uint32_t MAGIC = '\xc2gr\x00';
+
+    static
+    const ExtraData* getExtraData(const C2Handle *const handle) {
+        if (handle == nullptr
+                || native_handle_is_invalid(handle)
+                || handle->numInts < NUM_INTS) {
+            return nullptr;
+        }
+        return reinterpret_cast<const ExtraData*>(
+                &handle->data[handle->numFds + handle->numInts - NUM_INTS]);
+    }
+
+    static
+    ExtraData *getExtraData(C2Handle *const handle) {
+        return const_cast<ExtraData *>(getExtraData(const_cast<const C2Handle *const>(handle)));
+    }
+
+public:
+    void getIgbpData(uint32_t *generation, uint64_t *igbp_id, uint32_t *igbp_slot) const {
+        const ExtraData *ed = getExtraData(this);
+        *generation = ed->generation;
+        *igbp_id = unsigned(ed->igbp_id_lo) | uint64_t(unsigned(ed->igbp_id_hi)) << 32;
+        *igbp_slot = ed->igbp_slot;
+    }
+
+    static bool isValid(const C2Handle *const o) {
+        if (o == nullptr) { // null handle is always valid
+            return true;
+        }
+        const ExtraData *xd = getExtraData(o);
+        // we cannot validate width/height/format/usage without accessing gralloc driver
+        return xd != nullptr && xd->magic == MAGIC;
+    }
+
+    static C2HandleGralloc* WrapNativeHandle(
+            const native_handle_t *const handle,
+            uint32_t width, uint32_t height, uint32_t format, uint64_t usage,
+            uint32_t stride, uint32_t generation, uint64_t igbp_id = 0, uint32_t igbp_slot = 0) {
+        //CHECK(handle != nullptr);
+        if (native_handle_is_invalid(handle) ||
+            handle->numInts > int((INT_MAX - handle->version) / sizeof(int)) - NUM_INTS - handle->numFds) {
+            return nullptr;
+        }
+        ExtraData xd = {
+            width, height, format, uint32_t(usage & 0xFFFFFFFF), uint32_t(usage >> 32),
+            stride, generation, uint32_t(igbp_id & 0xFFFFFFFF), uint32_t(igbp_id >> 32),
+            igbp_slot, MAGIC
+        };
+        native_handle_t *res = native_handle_create(handle->numFds, handle->numInts + NUM_INTS);
+        if (res != nullptr) {
+            memcpy(&res->data, &handle->data, sizeof(int) * (handle->numFds + handle->numInts));
+            *getExtraData(res) = xd;
+        }
+        return reinterpret_cast<C2HandleGralloc *>(res);
+    }
+
+    static native_handle_t* UnwrapNativeHandle(
+            const C2Handle *const handle) {
+        const ExtraData *xd = getExtraData(handle);
+        if (xd == nullptr || xd->magic != MAGIC) {
+            return nullptr;
+        }
+        native_handle_t *res = native_handle_create(handle->numFds, handle->numInts - NUM_INTS);
+        if (res != nullptr) {
+            memcpy(&res->data, &handle->data, sizeof(int) * (res->numFds + res->numInts));
+        }
+        return res;
+    }
+
+    static native_handle_t* UnwrapNativeHandle(
+            const C2Handle *const handle,
+            uint32_t *generation, uint64_t *igbp_id, uint32_t *igbp_slot) {
+        const ExtraData *xd = getExtraData(handle);
+        if (xd == nullptr || xd->magic != MAGIC) {
+            return nullptr;
+        }
+        *generation = xd->generation;
+        *igbp_id = unsigned(xd->igbp_id_lo) | uint64_t(unsigned(xd->igbp_id_hi)) << 32;
+        *igbp_slot = xd->igbp_slot;
+        native_handle_t *res = native_handle_create(handle->numFds, handle->numInts - NUM_INTS);
+        if (res != nullptr) {
+            memcpy(&res->data, &handle->data, sizeof(int) * (res->numFds + res->numInts));
+        }
+        return res;
+    }
+
+    static const C2HandleGralloc* Import(
+            const C2Handle *const handle,
+            uint32_t *width, uint32_t *height, uint32_t *format,
+            uint64_t *usage, uint32_t *stride,
+            uint32_t *generation, uint64_t *igbp_id, uint32_t *igbp_slot) {
+        const ExtraData *xd = getExtraData(handle);
+        if (xd == nullptr) {
+            return nullptr;
+        }
+        *width = xd->width;
+        *height = xd->height;
+        *format = xd->format;
+        *usage = xd->usage_lo | (uint64_t(xd->usage_hi) << 32);
+        *stride = xd->stride;
+        *generation = xd->generation;
+        *igbp_id = xd->igbp_id_lo | (uint64_t(xd->igbp_id_hi) << 32);
+        *igbp_slot = xd->igbp_slot;
+        return reinterpret_cast<const C2HandleGralloc *>(handle);
+    }
+};
+
+native_handle_t *UnwrapNativeCodec2GrallocHandle(const C2Handle *const handle) {
+    return C2HandleGralloc::UnwrapNativeHandle(handle);
+}
+
+native_handle_t *UnwrapNativeCodec2GrallocHandle(
+        const C2Handle *const handle,
+        uint32_t *generation, uint64_t *igbp_id, uint32_t *igbp_slot) {
+    return C2HandleGralloc::UnwrapNativeHandle(handle, generation, igbp_id, igbp_slot);
+}
+
+C2Handle *WrapNativeCodec2GrallocHandle(
+        const native_handle_t *const handle,
+        uint32_t width, uint32_t height, uint32_t format, uint64_t usage, uint32_t stride,
+        uint32_t generation, uint64_t igbp_id, uint32_t igbp_slot) {
+    return C2HandleGralloc::WrapNativeHandle(handle, width, height, format, usage, stride,
+                                             generation, igbp_id, igbp_slot);
+}
+
+class C2AllocationGralloc : public C2GraphicAllocation {
+public:
+    virtual ~C2AllocationGralloc() override;
+
+    virtual c2_status_t map(
+            C2Rect rect, C2MemoryUsage usage, C2Fence *fence,
+            C2PlanarLayout *layout /* nonnull */, uint8_t **addr /* nonnull */) override;
+    virtual c2_status_t unmap(
+            uint8_t **addr /* nonnull */, C2Rect rect, C2Fence *fence /* nullable */) override;
+    virtual C2Allocator::id_t getAllocatorId() const override { return mAllocatorId; }
+    virtual const C2Handle *handle() const override { return mLockedHandle ? : mHandle; }
+    virtual bool equals(const std::shared_ptr<const C2GraphicAllocation> &other) const override;
+
+    // internal methods
+    // |handle| will be moved.
+    C2AllocationGralloc(
+              const BufferDescriptorInfo &info,
+              const sp<IMapper> &mapper,
+              hidl_handle &hidlHandle,
+              const C2HandleGralloc *const handle,
+              C2Allocator::id_t allocatorId);
+    int dup() const;
+    c2_status_t status() const;
+
+private:
+    const BufferDescriptorInfo mInfo;
+    const sp<IMapper> mMapper;
+    const hidl_handle mHidlHandle;
+    const C2HandleGralloc *mHandle;
+    buffer_handle_t mBuffer;
+    const C2HandleGralloc *mLockedHandle;
+    bool mLocked;
+    C2Allocator::id_t mAllocatorId;
+    std::mutex mMappedLock;
+};
+
+C2AllocationGralloc::C2AllocationGralloc(
+          const BufferDescriptorInfo &info,
+          const sp<IMapper> &mapper,
+          hidl_handle &hidlHandle,
+          const C2HandleGralloc *const handle,
+          C2Allocator::id_t allocatorId)
+    : C2GraphicAllocation(info.mapperInfo.width, info.mapperInfo.height),
+      mInfo(info),
+      mMapper(mapper),
+      mHidlHandle(std::move(hidlHandle)),
+      mHandle(handle),
+      mBuffer(nullptr),
+      mLockedHandle(nullptr),
+      mLocked(false),
+      mAllocatorId(allocatorId) {
+}
+
+C2AllocationGralloc::~C2AllocationGralloc() {
+    if (!mBuffer) {
+        return;
+    }
+    if (mLocked) {
+        // implementation ignores addresss and rect
+        uint8_t* addr[C2PlanarLayout::MAX_NUM_PLANES] = {};
+        unmap(addr, C2Rect(), nullptr);
+    }
+    mMapper->freeBuffer(const_cast<native_handle_t *>(mBuffer));
+    native_handle_delete(const_cast<native_handle_t*>(
+            reinterpret_cast<const native_handle_t*>(mHandle)));
+}
+
+c2_status_t C2AllocationGralloc::map(
+        C2Rect rect, C2MemoryUsage usage, C2Fence *fence,
+        C2PlanarLayout *layout /* nonnull */, uint8_t **addr /* nonnull */) {
+    uint64_t grallocUsage = static_cast<C2AndroidMemoryUsage>(usage).asGrallocUsage();
+    ALOGV("mapping buffer with usage %#llx => %#llx",
+          (long long)usage.expected, (long long)grallocUsage);
+
+    // TODO
+    (void) fence;
+
+    std::lock_guard<std::mutex> lock(mMappedLock);
+    if (mBuffer && mLocked) {
+        ALOGD("already mapped");
+        return C2_DUPLICATE;
+    }
+    if (!layout || !addr) {
+        ALOGD("wrong param");
+        return C2_BAD_VALUE;
+    }
+
+    c2_status_t err = C2_OK;
+    if (!mBuffer) {
+        mMapper->importBuffer(
+                mHidlHandle, [&err, this](const auto &maperr, const auto &buffer) {
+                    err = maperr2error(maperr);
+                    if (err == C2_OK) {
+                        mBuffer = static_cast<buffer_handle_t>(buffer);
+                    }
+                });
+        if (err != C2_OK) {
+            ALOGD("importBuffer failed: %d", err);
+            return err;
+        }
+        if (mBuffer == nullptr) {
+            ALOGD("importBuffer returned null buffer");
+            return C2_CORRUPTED;
+        }
+        uint32_t generation = 0;
+        uint64_t igbp_id = 0;
+        uint32_t igbp_slot = 0;
+        if (mHandle) {
+            mHandle->getIgbpData(&generation, &igbp_id, &igbp_slot);
+        }
+        mLockedHandle = C2HandleGralloc::WrapNativeHandle(
+                mBuffer, mInfo.mapperInfo.width, mInfo.mapperInfo.height,
+                (uint32_t)mInfo.mapperInfo.format, mInfo.mapperInfo.usage, mInfo.stride,
+                generation, igbp_id, igbp_slot);
+    }
+
+    // UGLY HACK: assume YCbCr 4:2:0 8-bit format (and lockable via lockYCbCr) if we don't
+    // recognize the format
+    PixelFormat format = mInfo.mapperInfo.format;
+    if (format != PixelFormat::RGBA_8888 && format != PixelFormat::RGBX_8888) {
+        format = PixelFormat::YCBCR_420_888;
+    }
+
+    switch (format) {
+        case PixelFormat::YCBCR_420_888:
+        case PixelFormat::YV12: {
+            YCbCrLayout ycbcrLayout;
+            mMapper->lockYCbCr(
+                    const_cast<native_handle_t *>(mBuffer), grallocUsage,
+                    { (int32_t)rect.left, (int32_t)rect.top, (int32_t)rect.width, (int32_t)rect.height },
+                    // TODO: fence
+                    hidl_handle(),
+                    [&err, &ycbcrLayout](const auto &maperr, const auto &mapLayout) {
+                        err = maperr2error(maperr);
+                        if (err == C2_OK) {
+                            ycbcrLayout = mapLayout;
+                        }
+                    });
+            if (err != C2_OK) {
+                ALOGD("lockYCbCr failed: %d", err);
+                return err;
+            }
+            addr[C2PlanarLayout::PLANE_Y] = (uint8_t *)ycbcrLayout.y;
+            addr[C2PlanarLayout::PLANE_U] = (uint8_t *)ycbcrLayout.cb;
+            addr[C2PlanarLayout::PLANE_V] = (uint8_t *)ycbcrLayout.cr;
+            layout->type = C2PlanarLayout::TYPE_YUV;
+            layout->numPlanes = 3;
+            layout->rootPlanes = 3;
+            layout->planes[C2PlanarLayout::PLANE_Y] = {
+                C2PlaneInfo::CHANNEL_Y,         // channel
+                1,                              // colInc
+                (int32_t)ycbcrLayout.yStride,   // rowInc
+                1,                              // mColSampling
+                1,                              // mRowSampling
+                8,                              // allocatedDepth
+                8,                              // bitDepth
+                0,                              // rightShift
+                C2PlaneInfo::NATIVE,            // endianness
+                C2PlanarLayout::PLANE_Y,        // rootIx
+                0,                              // offset
+            };
+            layout->planes[C2PlanarLayout::PLANE_U] = {
+                C2PlaneInfo::CHANNEL_CB,          // channel
+                (int32_t)ycbcrLayout.chromaStep,  // colInc
+                (int32_t)ycbcrLayout.cStride,     // rowInc
+                2,                                // mColSampling
+                2,                                // mRowSampling
+                8,                                // allocatedDepth
+                8,                                // bitDepth
+                0,                                // rightShift
+                C2PlaneInfo::NATIVE,              // endianness
+                C2PlanarLayout::PLANE_U,          // rootIx
+                0,                                // offset
+            };
+            layout->planes[C2PlanarLayout::PLANE_V] = {
+                C2PlaneInfo::CHANNEL_CR,          // channel
+                (int32_t)ycbcrLayout.chromaStep,  // colInc
+                (int32_t)ycbcrLayout.cStride,     // rowInc
+                2,                                // mColSampling
+                2,                                // mRowSampling
+                8,                                // allocatedDepth
+                8,                                // bitDepth
+                0,                                // rightShift
+                C2PlaneInfo::NATIVE,              // endianness
+                C2PlanarLayout::PLANE_V,          // rootIx
+                0,                                // offset
+            };
+            // handle interleaved formats
+            intptr_t uvOffset = addr[C2PlanarLayout::PLANE_V] - addr[C2PlanarLayout::PLANE_U];
+            if (uvOffset > 0 && uvOffset < (intptr_t)ycbcrLayout.chromaStep) {
+                layout->rootPlanes = 2;
+                layout->planes[C2PlanarLayout::PLANE_V].rootIx = C2PlanarLayout::PLANE_U;
+                layout->planes[C2PlanarLayout::PLANE_V].offset = uvOffset;
+            } else if (uvOffset < 0 && uvOffset > -(intptr_t)ycbcrLayout.chromaStep) {
+                layout->rootPlanes = 2;
+                layout->planes[C2PlanarLayout::PLANE_U].rootIx = C2PlanarLayout::PLANE_V;
+                layout->planes[C2PlanarLayout::PLANE_U].offset = -uvOffset;
+            }
+            break;
+        }
+
+        case PixelFormat::RGBA_8888:
+            // TODO: alpha channel
+            // fall-through
+        case PixelFormat::RGBX_8888: {
+            void *pointer = nullptr;
+            mMapper->lock(
+                    const_cast<native_handle_t *>(mBuffer),
+                    grallocUsage,
+                    { (int32_t)rect.left, (int32_t)rect.top, (int32_t)rect.width, (int32_t)rect.height },
+                    // TODO: fence
+                    hidl_handle(),
+                    [&err, &pointer](const auto &maperr, const auto &mapPointer) {
+                        err = maperr2error(maperr);
+                        if (err == C2_OK) {
+                            pointer = mapPointer;
+                        }
+                    });
+            if (err != C2_OK) {
+                ALOGD("lock failed: %d", err);
+                return err;
+            }
+            addr[C2PlanarLayout::PLANE_R] = (uint8_t *)pointer;
+            addr[C2PlanarLayout::PLANE_G] = (uint8_t *)pointer + 1;
+            addr[C2PlanarLayout::PLANE_B] = (uint8_t *)pointer + 2;
+            layout->type = C2PlanarLayout::TYPE_RGB;
+            layout->numPlanes = 3;
+            layout->rootPlanes = 1;
+            layout->planes[C2PlanarLayout::PLANE_R] = {
+                C2PlaneInfo::CHANNEL_R,         // channel
+                4,                              // colInc
+                4 * (int32_t)mInfo.stride,      // rowInc
+                1,                              // mColSampling
+                1,                              // mRowSampling
+                8,                              // allocatedDepth
+                8,                              // bitDepth
+                0,                              // rightShift
+                C2PlaneInfo::NATIVE,            // endianness
+                C2PlanarLayout::PLANE_R,        // rootIx
+                0,                              // offset
+            };
+            layout->planes[C2PlanarLayout::PLANE_G] = {
+                C2PlaneInfo::CHANNEL_G,         // channel
+                4,                              // colInc
+                4 * (int32_t)mInfo.stride,      // rowInc
+                1,                              // mColSampling
+                1,                              // mRowSampling
+                8,                              // allocatedDepth
+                8,                              // bitDepth
+                0,                              // rightShift
+                C2PlaneInfo::NATIVE,            // endianness
+                C2PlanarLayout::PLANE_R,        // rootIx
+                1,                              // offset
+            };
+            layout->planes[C2PlanarLayout::PLANE_B] = {
+                C2PlaneInfo::CHANNEL_B,         // channel
+                4,                              // colInc
+                4 * (int32_t)mInfo.stride,      // rowInc
+                1,                              // mColSampling
+                1,                              // mRowSampling
+                8,                              // allocatedDepth
+                8,                              // bitDepth
+                0,                              // rightShift
+                C2PlaneInfo::NATIVE,            // endianness
+                C2PlanarLayout::PLANE_R,        // rootIx
+                2,                              // offset
+            };
+            break;
+        }
+        default: {
+            ALOGD("unsupported pixel format: %d", mInfo.mapperInfo.format);
+            return C2_OMITTED;
+        }
+    }
+    mLocked = true;
+
+    return C2_OK;
+}
+
+c2_status_t C2AllocationGralloc::unmap(
+        uint8_t **addr, C2Rect rect, C2Fence *fence /* nullable */) {
+    // TODO: check addr and size, use fence
+    (void)addr;
+    (void)rect;
+
+    std::lock_guard<std::mutex> lock(mMappedLock);
+    c2_status_t err = C2_OK;
+    mMapper->unlock(
+            const_cast<native_handle_t *>(mBuffer),
+            [&err, &fence](const auto &maperr, const auto &releaseFence) {
+                // TODO
+                (void) fence;
+                (void) releaseFence;
+                err = maperr2error(maperr);
+                if (err == C2_OK) {
+                    // TODO: fence
+                }
+            });
+    if (err == C2_OK) {
+        mLocked = false;
+    }
+    return err;
+}
+
+bool C2AllocationGralloc::equals(const std::shared_ptr<const C2GraphicAllocation> &other) const {
+    return other && other->handle() == handle();
+}
+
+/* ===================================== GRALLOC ALLOCATOR ==================================== */
+class C2AllocatorGralloc::Impl {
+public:
+    Impl(id_t id, bool bufferQueue);
+
+    id_t getId() const {
+        return mTraits->id;
+    }
+
+    C2String getName() const {
+        return mTraits->name;
+    }
+
+    std::shared_ptr<const C2Allocator::Traits> getTraits() const {
+        return mTraits;
+    }
+
+    c2_status_t newGraphicAllocation(
+            uint32_t width, uint32_t height, uint32_t format, const C2MemoryUsage &usage,
+            std::shared_ptr<C2GraphicAllocation> *allocation);
+
+    c2_status_t priorGraphicAllocation(
+            const C2Handle *handle,
+            std::shared_ptr<C2GraphicAllocation> *allocation);
+
+    c2_status_t status() const { return mInit; }
+
+private:
+    std::shared_ptr<C2Allocator::Traits> mTraits;
+    c2_status_t mInit;
+    sp<IAllocator> mAllocator;
+    sp<IMapper> mMapper;
+    const bool mBufferQueue;
+};
+
+void _UnwrapNativeCodec2GrallocMetadata(
+        const C2Handle *const handle,
+        uint32_t *width, uint32_t *height, uint32_t *format,uint64_t *usage, uint32_t *stride,
+        uint32_t *generation, uint64_t *igbp_id, uint32_t *igbp_slot) {
+    (void)C2HandleGralloc::Import(handle, width, height, format, usage, stride,
+                                  generation, igbp_id, igbp_slot);
+}
+
+C2AllocatorGralloc::Impl::Impl(id_t id, bool bufferQueue)
+    : mInit(C2_OK), mBufferQueue(bufferQueue) {
+    // TODO: get this from allocator
+    C2MemoryUsage minUsage = { 0, 0 }, maxUsage = { ~(uint64_t)0, ~(uint64_t)0 };
+    Traits traits = { "android.allocator.gralloc", id, C2Allocator::GRAPHIC, minUsage, maxUsage };
+    mTraits = std::make_shared<C2Allocator::Traits>(traits);
+
+    // gralloc allocator is a singleton, so all objects share a global service
+    mAllocator = IAllocator::getService();
+    mMapper = IMapper::getService();
+    if (mAllocator == nullptr || mMapper == nullptr) {
+        mInit = C2_CORRUPTED;
+    }
+}
+
+c2_status_t C2AllocatorGralloc::Impl::newGraphicAllocation(
+        uint32_t width, uint32_t height, uint32_t format, const C2MemoryUsage &usage,
+        std::shared_ptr<C2GraphicAllocation> *allocation) {
+    uint64_t grallocUsage = static_cast<C2AndroidMemoryUsage>(usage).asGrallocUsage();
+    ALOGV("allocating buffer with usage %#llx => %#llx",
+          (long long)usage.expected, (long long)grallocUsage);
+
+    BufferDescriptorInfo info = {
+        {
+            width,
+            height,
+            1u,  // layerCount
+            (PixelFormat)format,
+            grallocUsage,
+        },
+        0u,  // stride placeholder
+    };
+    c2_status_t err = C2_OK;
+    BufferDescriptor desc;
+    mMapper->createDescriptor(
+            info.mapperInfo, [&err, &desc](const auto &maperr, const auto &descriptor) {
+                err = maperr2error(maperr);
+                if (err == C2_OK) {
+                    desc = descriptor;
+                }
+            });
+    if (err != C2_OK) {
+        return err;
+    }
+
+    // IAllocator shares IMapper error codes.
+    hidl_handle buffer;
+    mAllocator->allocate(
+            desc,
+            1u,
+            [&err, &buffer, &info](const auto &maperr, const auto &stride, auto &buffers) {
+                err = maperr2error(maperr);
+                if (err != C2_OK) {
+                    return;
+                }
+                if (buffers.size() != 1u) {
+                    err = C2_CORRUPTED;
+                    return;
+                }
+                info.stride = stride;
+                buffer = std::move(buffers[0]);
+            });
+    if (err != C2_OK) {
+        return err;
+    }
+
+
+    allocation->reset(new C2AllocationGralloc(
+            info, mMapper, buffer,
+            C2HandleGralloc::WrapNativeHandle(
+                    buffer.getNativeHandle(),
+                    info.mapperInfo.width, info.mapperInfo.height,
+                    (uint32_t)info.mapperInfo.format, info.mapperInfo.usage, info.stride,
+                    0, 0, mBufferQueue ? ~0 : 0),
+            mTraits->id));
+    return C2_OK;
+}
+
+c2_status_t C2AllocatorGralloc::Impl::priorGraphicAllocation(
+        const C2Handle *handle,
+        std::shared_ptr<C2GraphicAllocation> *allocation) {
+    BufferDescriptorInfo info;
+    info.mapperInfo.layerCount = 1u;
+    uint32_t generation;
+    uint64_t igbp_id;
+    uint32_t igbp_slot;
+    const C2HandleGralloc *grallocHandle = C2HandleGralloc::Import(
+            handle,
+            &info.mapperInfo.width, &info.mapperInfo.height,
+            (uint32_t *)&info.mapperInfo.format, (uint64_t *)&info.mapperInfo.usage, &info.stride,
+            &generation, &igbp_id, &igbp_slot);
+    if (grallocHandle == nullptr) {
+        return C2_BAD_VALUE;
+    }
+
+    hidl_handle hidlHandle;
+    hidlHandle.setTo(C2HandleGralloc::UnwrapNativeHandle(grallocHandle), true);
+
+    allocation->reset(new C2AllocationGralloc(info, mMapper, hidlHandle, grallocHandle, mTraits->id));
+    return C2_OK;
+}
+
+C2AllocatorGralloc::C2AllocatorGralloc(id_t id, bool bufferQueue)
+        : mImpl(new Impl(id, bufferQueue)) {}
+
+C2AllocatorGralloc::~C2AllocatorGralloc() { delete mImpl; }
+
+C2Allocator::id_t C2AllocatorGralloc::getId() const {
+    return mImpl->getId();
+}
+
+C2String C2AllocatorGralloc::getName() const {
+    return mImpl->getName();
+}
+
+std::shared_ptr<const C2Allocator::Traits> C2AllocatorGralloc::getTraits() const {
+    return mImpl->getTraits();
+}
+
+c2_status_t C2AllocatorGralloc::newGraphicAllocation(
+        uint32_t width, uint32_t height, uint32_t format, C2MemoryUsage usage,
+        std::shared_ptr<C2GraphicAllocation> *allocation) {
+    return mImpl->newGraphicAllocation(width, height, format, usage, allocation);
+}
+
+c2_status_t C2AllocatorGralloc::priorGraphicAllocation(
+        const C2Handle *handle,
+        std::shared_ptr<C2GraphicAllocation> *allocation) {
+    return mImpl->priorGraphicAllocation(handle, allocation);
+}
+
+c2_status_t C2AllocatorGralloc::status() const {
+    return mImpl->status();
+}
+
+bool C2AllocatorGralloc::isValid(const C2Handle* const o) {
+    return C2HandleGralloc::isValid(o);
+}
+
+} // namespace android
diff --git a/media/codec2/vndk/C2AllocatorIon.cpp b/media/codec2/vndk/C2AllocatorIon.cpp
new file mode 100644
index 0000000..736aac5
--- /dev/null
+++ b/media/codec2/vndk/C2AllocatorIon.cpp
@@ -0,0 +1,561 @@
+/*
+ * Copyright (C) 2016 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_NDEBUG 0
+#define LOG_TAG "C2AllocatorIon"
+#include <utils/Log.h>
+
+#include <list>
+
+#include <ion/ion.h>
+#include <sys/mman.h>
+#include <unistd.h> // getpagesize, size_t, close, dup
+
+#include <C2AllocatorIon.h>
+#include <C2Buffer.h>
+#include <C2Debug.h>
+#include <C2ErrnoUtils.h>
+
+namespace android {
+
+namespace {
+    constexpr size_t USAGE_LRU_CACHE_SIZE = 1024;
+}
+
+/* size_t <=> int(lo), int(hi) conversions */
+constexpr inline int size2intLo(size_t s) {
+    return int(s & 0xFFFFFFFF);
+}
+
+constexpr inline int size2intHi(size_t s) {
+    // cast to uint64_t as size_t may be 32 bits wide
+    return int((uint64_t(s) >> 32) & 0xFFFFFFFF);
+}
+
+constexpr inline size_t ints2size(int intLo, int intHi) {
+    // convert in 2 stages to 64 bits as intHi may be negative
+    return size_t(unsigned(intLo)) | size_t(uint64_t(unsigned(intHi)) << 32);
+}
+
+/* ========================================= ION HANDLE ======================================== */
+/**
+ * ION handle
+ *
+ * There can be only a sole ion client per process, this is captured in the ion fd that is passed
+ * to the constructor, but this should be managed by the ion buffer allocator/mapper.
+ *
+ * ion uses ion_user_handle_t for buffers. We don't store this in the native handle as
+ * it requires an ion_free to decref. Instead, we share the buffer to get an fd that also holds
+ * a refcount.
+ *
+ * This handle will not capture mapped fd-s as updating that would require a global mutex.
+ */
+
+struct C2HandleIon : public C2Handle {
+    // ion handle owns ionFd(!) and bufferFd
+    C2HandleIon(int bufferFd, size_t size)
+        : C2Handle(cHeader),
+          mFds{ bufferFd },
+          mInts{ int(size & 0xFFFFFFFF), int((uint64_t(size) >> 32) & 0xFFFFFFFF), kMagic } { }
+
+    static bool isValid(const C2Handle * const o);
+
+    int bufferFd() const { return mFds.mBuffer; }
+    size_t size() const {
+        return size_t(unsigned(mInts.mSizeLo))
+                | size_t(uint64_t(unsigned(mInts.mSizeHi)) << 32);
+    }
+
+protected:
+    struct {
+        int mBuffer; // shared ion buffer
+    } mFds;
+    struct {
+        int mSizeLo; // low 32-bits of size
+        int mSizeHi; // high 32-bits of size
+        int mMagic;
+    } mInts;
+
+private:
+    typedef C2HandleIon _type;
+    enum {
+        kMagic = '\xc2io\x00',
+        numFds = sizeof(mFds) / sizeof(int),
+        numInts = sizeof(mInts) / sizeof(int),
+        version = sizeof(C2Handle)
+    };
+    //constexpr static C2Handle cHeader = { version, numFds, numInts, {} };
+    const static C2Handle cHeader;
+};
+
+const C2Handle C2HandleIon::cHeader = {
+    C2HandleIon::version,
+    C2HandleIon::numFds,
+    C2HandleIon::numInts,
+    {}
+};
+
+// static
+bool C2HandleIon::isValid(const C2Handle * const o) {
+    if (!o || memcmp(o, &cHeader, sizeof(cHeader))) {
+        return false;
+    }
+    const C2HandleIon *other = static_cast<const C2HandleIon*>(o);
+    return other->mInts.mMagic == kMagic;
+}
+
+// TODO: is the dup of an ion fd identical to ion_share?
+
+/* ======================================= ION ALLOCATION ====================================== */
+class C2AllocationIon : public C2LinearAllocation {
+public:
+    /* Interface methods */
+    virtual c2_status_t map(
+        size_t offset, size_t size, C2MemoryUsage usage, C2Fence *fence,
+        void **addr /* nonnull */) override;
+    virtual c2_status_t unmap(void *addr, size_t size, C2Fence *fenceFd) override;
+    virtual ~C2AllocationIon() override;
+    virtual const C2Handle *handle() const override;
+    virtual id_t getAllocatorId() const override;
+    virtual bool equals(const std::shared_ptr<C2LinearAllocation> &other) const override;
+
+    // internal methods
+    C2AllocationIon(int ionFd, size_t size, size_t align, unsigned heapMask, unsigned flags, C2Allocator::id_t id);
+    C2AllocationIon(int ionFd, size_t size, int shareFd, C2Allocator::id_t id);
+
+    c2_status_t status() const;
+
+protected:
+    class Impl;
+    Impl *mImpl;
+
+    // TODO: we could make this encapsulate shared_ptr and copiable
+    C2_DO_NOT_COPY(C2AllocationIon);
+};
+
+class C2AllocationIon::Impl {
+private:
+    /**
+     * Constructs an ion allocation.
+     *
+     * \note We always create an ion allocation, even if the allocation or import fails
+     * so that we can capture the error.
+     *
+     * \param ionFd     ion client (ownership transferred to created object)
+     * \param capacity  size of allocation
+     * \param bufferFd  buffer handle (ownership transferred to created object). Must be
+     *                  invalid if err is not 0.
+     * \param buffer    ion buffer user handle (ownership transferred to created object). Must be
+     *                  invalid if err is not 0.
+     * \param err       errno during buffer allocation or import
+     */
+    Impl(int ionFd, size_t capacity, int bufferFd, ion_user_handle_t buffer, C2Allocator::id_t id, int err)
+        : mIonFd(ionFd),
+          mHandle(bufferFd, capacity),
+          mBuffer(buffer),
+          mId(id),
+          mInit(c2_map_errno<ENOMEM, EACCES, EINVAL>(err)),
+          mMapFd(-1) {
+        if (mInit != C2_OK) {
+            // close ionFd now on error
+            if (mIonFd >= 0) {
+                close(mIonFd);
+                mIonFd = -1;
+            }
+            // C2_CHECK(bufferFd < 0);
+            // C2_CHECK(buffer < 0);
+        }
+    }
+
+public:
+    /**
+     * Constructs an ion allocation by importing a shared buffer fd.
+     *
+     * \param ionFd     ion client (ownership transferred to created object)
+     * \param capacity  size of allocation
+     * \param bufferFd  buffer handle (ownership transferred to created object)
+     *
+     * \return created ion allocation (implementation) which may be invalid if the
+     * import failed.
+     */
+    static Impl *Import(int ionFd, size_t capacity, int bufferFd, C2Allocator::id_t id) {
+        ion_user_handle_t buffer = -1;
+        int ret = ion_import(ionFd, bufferFd, &buffer);
+        return new Impl(ionFd, capacity, bufferFd, buffer, id, ret);
+    }
+
+    /**
+     * Constructs an ion allocation by allocating an ion buffer.
+     *
+     * \param ionFd     ion client (ownership transferred to created object)
+     * \param size      size of allocation
+     * \param align     desired alignment of allocation
+     * \param heapMask  mask of heaps considered
+     * \param flags     ion allocation flags
+     *
+     * \return created ion allocation (implementation) which may be invalid if the
+     * allocation failed.
+     */
+    static Impl *Alloc(int ionFd, size_t size, size_t align, unsigned heapMask, unsigned flags, C2Allocator::id_t id) {
+        int bufferFd = -1;
+        ion_user_handle_t buffer = -1;
+        size_t alignedSize = align == 0 ? size : (size + align - 1) & ~(align - 1);
+        int ret = ion_alloc(ionFd, alignedSize, align, heapMask, flags, &buffer);
+        ALOGV("ion_alloc(ionFd = %d, size = %zu, align = %zu, prot = %d, flags = %d) "
+              "returned (%d) ; buffer = %d",
+              ionFd, alignedSize, align, heapMask, flags, ret, buffer);
+        if (ret == 0) {
+            // get buffer fd for native handle constructor
+            ret = ion_share(ionFd, buffer, &bufferFd);
+            if (ret != 0) {
+                ion_free(ionFd, buffer);
+                buffer = -1;
+            }
+        }
+        return new Impl(ionFd, alignedSize, bufferFd, buffer, id, ret);
+    }
+
+    c2_status_t map(size_t offset, size_t size, C2MemoryUsage usage, C2Fence *fence, void **addr) {
+        (void)fence; // TODO: wait for fence
+        *addr = nullptr;
+        if (!mMappings.empty()) {
+            ALOGV("multiple map");
+            // TODO: technically we should return DUPLICATE here, but our block views don't
+            // actually unmap, so we end up remapping an ion buffer multiple times.
+            //
+            // return C2_DUPLICATE;
+        }
+        if (size == 0) {
+            return C2_BAD_VALUE;
+        }
+
+        int prot = PROT_NONE;
+        int flags = MAP_SHARED;
+        if (usage.expected & C2MemoryUsage::CPU_READ) {
+            prot |= PROT_READ;
+        }
+        if (usage.expected & C2MemoryUsage::CPU_WRITE) {
+            prot |= PROT_WRITE;
+        }
+
+        size_t alignmentBytes = offset % PAGE_SIZE;
+        size_t mapOffset = offset - alignmentBytes;
+        size_t mapSize = size + alignmentBytes;
+        Mapping map = { nullptr, alignmentBytes, mapSize };
+
+        c2_status_t err = C2_OK;
+        if (mMapFd == -1) {
+            int ret = ion_map(mIonFd, mBuffer, mapSize, prot,
+                              flags, mapOffset, (unsigned char**)&map.addr, &mMapFd);
+            ALOGV("ion_map(ionFd = %d, handle = %d, size = %zu, prot = %d, flags = %d, "
+                  "offset = %zu) returned (%d)",
+                  mIonFd, mBuffer, mapSize, prot, flags, mapOffset, ret);
+            if (ret) {
+                mMapFd = -1;
+                map.addr = *addr = nullptr;
+                err = c2_map_errno<EINVAL>(-ret);
+            } else {
+                *addr = (uint8_t *)map.addr + alignmentBytes;
+            }
+        } else {
+            map.addr = mmap(nullptr, mapSize, prot, flags, mMapFd, mapOffset);
+            ALOGV("mmap(size = %zu, prot = %d, flags = %d, mapFd = %d, offset = %zu) "
+                  "returned (%d)",
+                  mapSize, prot, flags, mMapFd, mapOffset, errno);
+            if (map.addr == MAP_FAILED) {
+                map.addr = *addr = nullptr;
+                err = c2_map_errno<EINVAL>(errno);
+            } else {
+                *addr = (uint8_t *)map.addr + alignmentBytes;
+            }
+        }
+        if (map.addr) {
+            mMappings.push_back(map);
+        }
+        return err;
+    }
+
+    c2_status_t unmap(void *addr, size_t size, C2Fence *fence) {
+        if (mMapFd < 0 || mMappings.empty()) {
+            ALOGD("tried to unmap unmapped buffer");
+            return C2_NOT_FOUND;
+        }
+        for (auto it = mMappings.begin(); it != mMappings.end(); ++it) {
+            if (addr != (uint8_t *)it->addr + it->alignmentBytes ||
+                    size + it->alignmentBytes != it->size) {
+                continue;
+            }
+            int err = munmap(it->addr, it->size);
+            if (err != 0) {
+                ALOGD("munmap failed");
+                return c2_map_errno<EINVAL>(errno);
+            }
+            if (fence) {
+                *fence = C2Fence(); // not using fences
+            }
+            (void)mMappings.erase(it);
+            ALOGV("successfully unmapped: %d", mBuffer);
+            return C2_OK;
+        }
+        ALOGD("unmap failed to find specified map");
+        return C2_BAD_VALUE;
+    }
+
+    ~Impl() {
+        if (!mMappings.empty()) {
+            ALOGD("Dangling mappings!");
+            for (const Mapping &map : mMappings) {
+                (void)munmap(map.addr, map.size);
+            }
+        }
+        if (mMapFd >= 0) {
+            close(mMapFd);
+            mMapFd = -1;
+        }
+        if (mInit == C2_OK) {
+            (void)ion_free(mIonFd, mBuffer);
+            native_handle_close(&mHandle);
+        }
+        if (mIonFd >= 0) {
+            close(mIonFd);
+        }
+    }
+
+    c2_status_t status() const {
+        return mInit;
+    }
+
+    const C2Handle *handle() const {
+        return &mHandle;
+    }
+
+    C2Allocator::id_t getAllocatorId() const {
+        return mId;
+    }
+
+    ion_user_handle_t ionHandle() const {
+        return mBuffer;
+    }
+
+private:
+    int mIonFd;
+    C2HandleIon mHandle;
+    ion_user_handle_t mBuffer;
+    C2Allocator::id_t mId;
+    c2_status_t mInit;
+    int mMapFd; // only one for now
+    struct Mapping {
+        void *addr;
+        size_t alignmentBytes;
+        size_t size;
+    };
+    std::list<Mapping> mMappings;
+};
+
+c2_status_t C2AllocationIon::map(
+    size_t offset, size_t size, C2MemoryUsage usage, C2Fence *fence, void **addr) {
+    return mImpl->map(offset, size, usage, fence, addr);
+}
+
+c2_status_t C2AllocationIon::unmap(void *addr, size_t size, C2Fence *fence) {
+    return mImpl->unmap(addr, size, fence);
+}
+
+c2_status_t C2AllocationIon::status() const {
+    return mImpl->status();
+}
+
+C2Allocator::id_t C2AllocationIon::getAllocatorId() const {
+    return mImpl->getAllocatorId();
+}
+
+bool C2AllocationIon::equals(const std::shared_ptr<C2LinearAllocation> &other) const {
+    if (!other || other->getAllocatorId() != getAllocatorId()) {
+        return false;
+    }
+    // get user handle to compare objects
+    std::shared_ptr<C2AllocationIon> otherAsIon = std::static_pointer_cast<C2AllocationIon>(other);
+    return mImpl->ionHandle() == otherAsIon->mImpl->ionHandle();
+}
+
+const C2Handle *C2AllocationIon::handle() const {
+    return mImpl->handle();
+}
+
+C2AllocationIon::~C2AllocationIon() {
+    delete mImpl;
+}
+
+C2AllocationIon::C2AllocationIon(int ionFd, size_t size, size_t align,
+                                 unsigned heapMask, unsigned flags, C2Allocator::id_t id)
+    : C2LinearAllocation(size),
+      mImpl(Impl::Alloc(ionFd, size, align, heapMask, flags, id)) { }
+
+C2AllocationIon::C2AllocationIon(int ionFd, size_t size, int shareFd, C2Allocator::id_t id)
+    : C2LinearAllocation(size),
+      mImpl(Impl::Import(ionFd, size, shareFd, id)) { }
+
+/* ======================================= ION ALLOCATOR ====================================== */
+C2AllocatorIon::C2AllocatorIon(id_t id)
+    : mInit(C2_OK),
+      mIonFd(ion_open()) {
+    if (mIonFd < 0) {
+        switch (errno) {
+        case ENOENT:    mInit = C2_OMITTED; break;
+        default:        mInit = c2_map_errno<EACCES>(errno); break;
+        }
+    } else {
+        C2MemoryUsage minUsage = { 0, 0 };
+        C2MemoryUsage maxUsage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE };
+        Traits traits = { "android.allocator.ion", id, LINEAR, minUsage, maxUsage };
+        mTraits = std::make_shared<Traits>(traits);
+        mBlockSize = ::getpagesize();
+    }
+}
+
+C2AllocatorIon::~C2AllocatorIon() {
+    if (mInit == C2_OK) {
+        ion_close(mIonFd);
+    }
+}
+
+C2Allocator::id_t C2AllocatorIon::getId() const {
+    std::lock_guard<std::mutex> lock(mUsageMapperLock);
+    return mTraits->id;
+}
+
+C2String C2AllocatorIon::getName() const {
+    std::lock_guard<std::mutex> lock(mUsageMapperLock);
+    return mTraits->name;
+}
+
+std::shared_ptr<const C2Allocator::Traits> C2AllocatorIon::getTraits() const {
+    std::lock_guard<std::mutex> lock(mUsageMapperLock);
+    return mTraits;
+}
+
+void C2AllocatorIon::setUsageMapper(
+        const UsageMapperFn &mapper, uint64_t minUsage, uint64_t maxUsage, uint64_t blockSize) {
+    std::lock_guard<std::mutex> lock(mUsageMapperLock);
+    mUsageMapperCache.clear();
+    mUsageMapperLru.clear();
+    mUsageMapper = mapper;
+    Traits traits = {
+        mTraits->name, mTraits->id, LINEAR,
+        C2MemoryUsage(minUsage), C2MemoryUsage(maxUsage)
+    };
+    mTraits = std::make_shared<Traits>(traits);
+    mBlockSize = blockSize;
+}
+
+std::size_t C2AllocatorIon::MapperKeyHash::operator()(const MapperKey &k) const {
+    return std::hash<uint64_t>{}(k.first) ^ std::hash<size_t>{}(k.second);
+}
+
+c2_status_t C2AllocatorIon::mapUsage(
+        C2MemoryUsage usage, size_t capacity, size_t *align, unsigned *heapMask, unsigned *flags) {
+    std::lock_guard<std::mutex> lock(mUsageMapperLock);
+    c2_status_t res = C2_OK;
+    // align capacity
+    capacity = (capacity + mBlockSize - 1) & ~(mBlockSize - 1);
+    MapperKey key = std::make_pair(usage.expected, capacity);
+    auto entry = mUsageMapperCache.find(key);
+    if (entry == mUsageMapperCache.end()) {
+        if (mUsageMapper) {
+            res = mUsageMapper(usage, capacity, align, heapMask, flags);
+        } else {
+            *align = 0; // TODO make this 1
+            *heapMask = ~0; // default mask
+            *flags = 0; // default flags
+            res = C2_NO_INIT;
+        }
+        // add usage to cache
+        MapperValue value = std::make_tuple(*align, *heapMask, *flags, res);
+        mUsageMapperLru.emplace_front(key, value);
+        mUsageMapperCache.emplace(std::make_pair(key, mUsageMapperLru.begin()));
+        if (mUsageMapperCache.size() > USAGE_LRU_CACHE_SIZE) {
+            // remove LRU entry
+            MapperKey lruKey = mUsageMapperLru.front().first;
+            mUsageMapperCache.erase(lruKey);
+            mUsageMapperLru.pop_back();
+        }
+    } else {
+        // move entry to MRU
+        mUsageMapperLru.splice(mUsageMapperLru.begin(), mUsageMapperLru, entry->second);
+        const MapperValue &value = entry->second->second;
+        std::tie(*align, *heapMask, *flags, res) = value;
+    }
+    return res;
+}
+
+c2_status_t C2AllocatorIon::newLinearAllocation(
+        uint32_t capacity, C2MemoryUsage usage, std::shared_ptr<C2LinearAllocation> *allocation) {
+    if (allocation == nullptr) {
+        return C2_BAD_VALUE;
+    }
+
+    allocation->reset();
+    if (mInit != C2_OK) {
+        return mInit;
+    }
+
+    size_t align = 0;
+    unsigned heapMask = ~0;
+    unsigned flags = 0;
+    c2_status_t ret = mapUsage(usage, capacity, &align, &heapMask, &flags);
+    if (ret && ret != C2_NO_INIT) {
+        return ret;
+    }
+
+    std::shared_ptr<C2AllocationIon> alloc
+        = std::make_shared<C2AllocationIon>(dup(mIonFd), capacity, align, heapMask, flags, mTraits->id);
+    ret = alloc->status();
+    if (ret == C2_OK) {
+        *allocation = alloc;
+    }
+    return ret;
+}
+
+c2_status_t C2AllocatorIon::priorLinearAllocation(
+        const C2Handle *handle, std::shared_ptr<C2LinearAllocation> *allocation) {
+    *allocation = nullptr;
+    if (mInit != C2_OK) {
+        return mInit;
+    }
+
+    if (!C2HandleIon::isValid(handle)) {
+        return C2_BAD_VALUE;
+    }
+
+    // TODO: get capacity and validate it
+    const C2HandleIon *h = static_cast<const C2HandleIon*>(handle);
+    std::shared_ptr<C2AllocationIon> alloc
+        = std::make_shared<C2AllocationIon>(dup(mIonFd), h->size(), h->bufferFd(), mTraits->id);
+    c2_status_t ret = alloc->status();
+    if (ret == C2_OK) {
+        *allocation = alloc;
+        native_handle_delete(const_cast<native_handle_t*>(
+                reinterpret_cast<const native_handle_t*>(handle)));
+    }
+    return ret;
+}
+
+bool C2AllocatorIon::isValid(const C2Handle* const o) {
+    return C2HandleIon::isValid(o);
+}
+
+} // namespace android
+
diff --git a/media/codec2/vndk/C2Buffer.cpp b/media/codec2/vndk/C2Buffer.cpp
new file mode 100644
index 0000000..47366ca
--- /dev/null
+++ b/media/codec2/vndk/C2Buffer.cpp
@@ -0,0 +1,1304 @@
+/*
+ * Copyright (C) 2016 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_NDEBUG 0
+#define LOG_TAG "C2Buffer"
+#include <utils/Log.h>
+
+#include <list>
+#include <map>
+#include <mutex>
+
+#include <C2AllocatorIon.h>
+#include <C2AllocatorGralloc.h>
+#include <C2BufferPriv.h>
+#include <C2BlockInternal.h>
+#include <bufferpool/ClientManager.h>
+
+namespace {
+
+using android::C2AllocatorGralloc;
+using android::C2AllocatorIon;
+using android::hardware::media::bufferpool::BufferPoolData;
+using android::hardware::media::bufferpool::V1_0::ResultStatus;
+using android::hardware::media::bufferpool::V1_0::implementation::BufferPoolAllocation;
+using android::hardware::media::bufferpool::V1_0::implementation::BufferPoolAllocator;
+using android::hardware::media::bufferpool::V1_0::implementation::ClientManager;
+using android::hardware::media::bufferpool::V1_0::implementation::ConnectionId;
+using android::hardware::media::bufferpool::V1_0::implementation::INVALID_CONNECTIONID;
+
+// This anonymous namespace contains the helper classes that allow our implementation to create
+// block/buffer objects.
+//
+// Inherit from the parent, share with the friend.
+class ReadViewBuddy : public C2ReadView {
+    using C2ReadView::C2ReadView;
+    friend class ::C2ConstLinearBlock;
+};
+
+class WriteViewBuddy : public C2WriteView {
+    using C2WriteView::C2WriteView;
+    friend class ::C2LinearBlock;
+};
+
+class ConstLinearBlockBuddy : public C2ConstLinearBlock {
+    using C2ConstLinearBlock::C2ConstLinearBlock;
+    friend class ::C2LinearBlock;
+};
+
+class LinearBlockBuddy : public C2LinearBlock {
+    using C2LinearBlock::C2LinearBlock;
+    friend class ::C2BasicLinearBlockPool;
+};
+
+class AcquirableReadViewBuddy : public C2Acquirable<C2ReadView> {
+    using C2Acquirable::C2Acquirable;
+    friend class ::C2ConstLinearBlock;
+};
+
+class AcquirableWriteViewBuddy : public C2Acquirable<C2WriteView> {
+    using C2Acquirable::C2Acquirable;
+    friend class ::C2LinearBlock;
+};
+
+class GraphicViewBuddy : public C2GraphicView {
+    using C2GraphicView::C2GraphicView;
+    friend class ::C2ConstGraphicBlock;
+    friend class ::C2GraphicBlock;
+};
+
+class AcquirableConstGraphicViewBuddy : public C2Acquirable<const C2GraphicView> {
+    using C2Acquirable::C2Acquirable;
+    friend class ::C2ConstGraphicBlock;
+};
+
+class AcquirableGraphicViewBuddy : public C2Acquirable<C2GraphicView> {
+    using C2Acquirable::C2Acquirable;
+    friend class ::C2GraphicBlock;
+};
+
+class ConstGraphicBlockBuddy : public C2ConstGraphicBlock {
+    using C2ConstGraphicBlock::C2ConstGraphicBlock;
+    friend class ::C2GraphicBlock;
+};
+
+class GraphicBlockBuddy : public C2GraphicBlock {
+    using C2GraphicBlock::C2GraphicBlock;
+    friend class ::C2BasicGraphicBlockPool;
+};
+
+class BufferDataBuddy : public C2BufferData {
+    using C2BufferData::C2BufferData;
+    friend class ::C2Buffer;
+};
+
+}  // namespace
+
+/* ========================================== 1D BLOCK ========================================= */
+
+/**
+ * This class is the base class for all 1D block and view implementations.
+ *
+ * This is basically just a placeholder for the underlying 1D allocation and the range of the
+ * alloted portion to this block. There is also a placeholder for a blockpool data.
+ */
+class C2_HIDE _C2Block1DImpl : public _C2LinearRangeAspect {
+public:
+    _C2Block1DImpl(const std::shared_ptr<C2LinearAllocation> &alloc,
+            const std::shared_ptr<_C2BlockPoolData> &poolData = nullptr,
+            size_t offset = 0, size_t size = ~(size_t)0)
+        : _C2LinearRangeAspect(alloc.get(), offset, size),
+          mAllocation(alloc),
+          mPoolData(poolData) { }
+
+    _C2Block1DImpl(const _C2Block1DImpl &other, size_t offset = 0, size_t size = ~(size_t)0)
+        : _C2LinearRangeAspect(&other, offset, size),
+          mAllocation(other.mAllocation),
+          mPoolData(other.mPoolData) { }
+
+    /** returns pool data  */
+    std::shared_ptr<_C2BlockPoolData> poolData() const {
+        return mPoolData;
+    }
+
+    /** returns native handle */
+    const C2Handle *handle() const {
+        return mAllocation ? mAllocation->handle() : nullptr;
+    }
+
+    /** returns the allocator's ID */
+    C2Allocator::id_t getAllocatorId() const {
+        // BAD_ID can only happen if this Impl class is initialized for a view - never for a block.
+        return mAllocation ? mAllocation->getAllocatorId() : C2Allocator::BAD_ID;
+    }
+
+    std::shared_ptr<C2LinearAllocation> getAllocation() const {
+        return mAllocation;
+    }
+
+private:
+    std::shared_ptr<C2LinearAllocation> mAllocation;
+    std::shared_ptr<_C2BlockPoolData> mPoolData;
+};
+
+/**
+ * This class contains the mapped data pointer, and the potential error.
+ *
+ * range is the mapped range of the underlying allocation (which is part of the allotted
+ * range).
+ */
+class C2_HIDE _C2MappedBlock1DImpl : public _C2Block1DImpl {
+public:
+    _C2MappedBlock1DImpl(const _C2Block1DImpl &block, uint8_t *data,
+                         size_t offset = 0, size_t size = ~(size_t)0)
+        : _C2Block1DImpl(block, offset, size), mData(data), mError(C2_OK) { }
+
+    _C2MappedBlock1DImpl(c2_status_t error)
+        : _C2Block1DImpl(nullptr), mData(nullptr), mError(error) {
+        // CHECK(error != C2_OK);
+    }
+
+    const uint8_t *data() const {
+        return mData;
+    }
+
+    uint8_t *data() {
+        return mData;
+    }
+
+    c2_status_t error() const {
+        return mError;
+    }
+
+private:
+    uint8_t *mData;
+    c2_status_t mError;
+};
+
+/**
+ * Block implementation.
+ */
+class C2Block1D::Impl : public _C2Block1DImpl {
+    using _C2Block1DImpl::_C2Block1DImpl;
+};
+
+const C2Handle *C2Block1D::handle() const {
+    return mImpl->handle();
+};
+
+C2Allocator::id_t C2Block1D::getAllocatorId() const {
+    return mImpl->getAllocatorId();
+};
+
+C2Block1D::C2Block1D(std::shared_ptr<Impl> impl, const _C2LinearRangeAspect &range)
+    // always clamp subrange to parent (impl) range for safety
+    : _C2LinearRangeAspect(impl.get(), range.offset(), range.size()), mImpl(impl) {
+}
+
+/**
+ * Read view implementation.
+ *
+ * range of Impl is the mapped range of the underlying allocation (which is part of the allotted
+ * range). range of View is 0 to capacity() (not represented as an actual range). This maps to a
+ * subrange of Impl range starting at mImpl->offset() + _mOffset.
+ */
+class C2ReadView::Impl : public _C2MappedBlock1DImpl {
+    using _C2MappedBlock1DImpl::_C2MappedBlock1DImpl;
+};
+
+C2ReadView::C2ReadView(std::shared_ptr<Impl> impl, uint32_t offset, uint32_t size)
+    : _C2LinearCapacityAspect(C2LinearCapacity(impl->size()).range(offset, size).size()),
+      mImpl(impl),
+      mOffset(C2LinearCapacity(impl->size()).range(offset, size).offset()) { }
+
+C2ReadView::C2ReadView(c2_status_t error)
+    : _C2LinearCapacityAspect(0u), mImpl(std::make_shared<Impl>(error)), mOffset(0u) {
+    // CHECK(error != C2_OK);
+}
+
+const uint8_t *C2ReadView::data() const {
+    return mImpl->error() ? nullptr : mImpl->data() + mOffset;
+}
+
+c2_status_t C2ReadView::error() const {
+    return mImpl->error();
+}
+
+C2ReadView C2ReadView::subView(size_t offset, size_t size) const {
+    C2LinearRange subRange(*this, offset, size);
+    return C2ReadView(mImpl, mOffset + subRange.offset(), subRange.size());
+}
+
+/**
+ * Write view implementation.
+ */
+class C2WriteView::Impl : public _C2MappedBlock1DImpl {
+    using _C2MappedBlock1DImpl::_C2MappedBlock1DImpl;
+};
+
+C2WriteView::C2WriteView(std::shared_ptr<Impl> impl)
+// UGLY: _C2LinearRangeAspect requires a bona-fide object for capacity to prevent spoofing, so
+// this is what we have to do.
+// TODO: use childRange
+    : _C2EditableLinearRangeAspect(std::make_unique<C2LinearCapacity>(impl->size()).get()), mImpl(impl) { }
+
+C2WriteView::C2WriteView(c2_status_t error)
+    : _C2EditableLinearRangeAspect(nullptr), mImpl(std::make_shared<Impl>(error)) {}
+
+uint8_t *C2WriteView::base() { return mImpl->data(); }
+
+uint8_t *C2WriteView::data() { return mImpl->data() + offset(); }
+
+c2_status_t C2WriteView::error() const { return mImpl->error(); }
+
+/**
+ * Const linear block implementation.
+ */
+C2ConstLinearBlock::C2ConstLinearBlock(std::shared_ptr<Impl> impl, const _C2LinearRangeAspect &range, C2Fence fence)
+    : C2Block1D(impl, range), mFence(fence) { }
+
+C2Acquirable<C2ReadView> C2ConstLinearBlock::map() const {
+    void *base = nullptr;
+    uint32_t len = size();
+    c2_status_t error = mImpl->getAllocation()->map(
+            offset(), len, { C2MemoryUsage::CPU_READ, 0 }, nullptr, &base);
+    // TODO: wait on fence
+    if (error == C2_OK) {
+        std::shared_ptr<ReadViewBuddy::Impl> rvi = std::shared_ptr<ReadViewBuddy::Impl>(
+                new ReadViewBuddy::Impl(*mImpl, (uint8_t *)base, offset(), len),
+                [base, len](ReadViewBuddy::Impl *i) {
+                    (void)i->getAllocation()->unmap(base, len, nullptr);
+                    delete i;
+        });
+        return AcquirableReadViewBuddy(error, C2Fence(), ReadViewBuddy(rvi, 0, len));
+    } else {
+        return AcquirableReadViewBuddy(error, C2Fence(), ReadViewBuddy(error));
+    }
+}
+
+C2ConstLinearBlock C2ConstLinearBlock::subBlock(size_t offset_, size_t size_) const {
+    C2LinearRange subRange(*mImpl, offset_, size_);
+    return C2ConstLinearBlock(mImpl, subRange, mFence);
+}
+
+/**
+ * Linear block implementation.
+ */
+C2LinearBlock::C2LinearBlock(std::shared_ptr<Impl> impl, const _C2LinearRangeAspect &range)
+    : C2Block1D(impl, range) { }
+
+C2Acquirable<C2WriteView> C2LinearBlock::map() {
+    void *base = nullptr;
+    uint32_t len = size();
+    c2_status_t error = mImpl->getAllocation()->map(
+            offset(), len, { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE }, nullptr, &base);
+    // TODO: wait on fence
+    if (error == C2_OK) {
+        std::shared_ptr<WriteViewBuddy::Impl> rvi = std::shared_ptr<WriteViewBuddy::Impl>(
+                new WriteViewBuddy::Impl(*mImpl, (uint8_t *)base, 0, len),
+                [base, len](WriteViewBuddy::Impl *i) {
+                    (void)i->getAllocation()->unmap(base, len, nullptr);
+                    delete i;
+        });
+        return AcquirableWriteViewBuddy(error, C2Fence(), WriteViewBuddy(rvi));
+    } else {
+        return AcquirableWriteViewBuddy(error, C2Fence(), WriteViewBuddy(error));
+    }
+}
+
+C2ConstLinearBlock C2LinearBlock::share(size_t offset_, size_t size_, C2Fence fence) {
+    return ConstLinearBlockBuddy(mImpl, C2LinearRange(*this, offset_, size_), fence);
+}
+
+C2BasicLinearBlockPool::C2BasicLinearBlockPool(
+        const std::shared_ptr<C2Allocator> &allocator)
+  : mAllocator(allocator) { }
+
+c2_status_t C2BasicLinearBlockPool::fetchLinearBlock(
+        uint32_t capacity,
+        C2MemoryUsage usage,
+        std::shared_ptr<C2LinearBlock> *block /* nonnull */) {
+    block->reset();
+
+    std::shared_ptr<C2LinearAllocation> alloc;
+    c2_status_t err = mAllocator->newLinearAllocation(capacity, usage, &alloc);
+    if (err != C2_OK) {
+        return err;
+    }
+
+    *block = _C2BlockFactory::CreateLinearBlock(alloc);
+
+    return C2_OK;
+}
+
+struct C2_HIDE C2PooledBlockPoolData : _C2BlockPoolData {
+
+    virtual type_t getType() const override {
+        return TYPE_BUFFERPOOL;
+    }
+
+    void getBufferPoolData(std::shared_ptr<BufferPoolData> *data) const {
+        *data = mData;
+    }
+
+    C2PooledBlockPoolData(const std::shared_ptr<BufferPoolData> &data) : mData(data) {}
+
+    virtual ~C2PooledBlockPoolData() override {}
+
+private:
+    std::shared_ptr<BufferPoolData> mData;
+};
+
+bool _C2BlockFactory::GetBufferPoolData(
+        const std::shared_ptr<const _C2BlockPoolData> &data,
+        std::shared_ptr<BufferPoolData> *bufferPoolData) {
+    if (data && data->getType() == _C2BlockPoolData::TYPE_BUFFERPOOL) {
+        const std::shared_ptr<const C2PooledBlockPoolData> poolData =
+                std::static_pointer_cast<const C2PooledBlockPoolData>(data);
+        poolData->getBufferPoolData(bufferPoolData);
+        return true;
+    }
+    return false;
+}
+
+std::shared_ptr<C2LinearBlock> _C2BlockFactory::CreateLinearBlock(
+        const std::shared_ptr<C2LinearAllocation> &alloc,
+        const std::shared_ptr<_C2BlockPoolData> &data, size_t offset, size_t size) {
+    std::shared_ptr<C2Block1D::Impl> impl =
+        std::make_shared<C2Block1D::Impl>(alloc, data, offset, size);
+    return std::shared_ptr<C2LinearBlock>(new C2LinearBlock(impl, *impl));
+}
+
+std::shared_ptr<_C2BlockPoolData> _C2BlockFactory::GetLinearBlockPoolData(
+        const C2Block1D &block) {
+    if (block.mImpl) {
+        return block.mImpl->poolData();
+    }
+    return nullptr;
+}
+
+std::shared_ptr<C2LinearBlock> _C2BlockFactory::CreateLinearBlock(
+        const C2Handle *handle) {
+    // TODO: get proper allocator? and mutex?
+    static std::unique_ptr<C2AllocatorIon> sAllocator = std::make_unique<C2AllocatorIon>(0);
+
+    std::shared_ptr<C2LinearAllocation> alloc;
+    if (C2AllocatorIon::isValid(handle)) {
+        c2_status_t err = sAllocator->priorLinearAllocation(handle, &alloc);
+        if (err == C2_OK) {
+            std::shared_ptr<C2LinearBlock> block = _C2BlockFactory::CreateLinearBlock(alloc);
+            return block;
+        }
+    }
+    return nullptr;
+}
+
+std::shared_ptr<C2LinearBlock> _C2BlockFactory::CreateLinearBlock(
+        const C2Handle *cHandle, const std::shared_ptr<BufferPoolData> &data) {
+    // TODO: get proper allocator? and mutex?
+    static std::unique_ptr<C2AllocatorIon> sAllocator = std::make_unique<C2AllocatorIon>(0);
+
+    std::shared_ptr<C2LinearAllocation> alloc;
+    if (C2AllocatorIon::isValid(cHandle)) {
+        native_handle_t *handle = native_handle_clone(cHandle);
+        if (handle) {
+            c2_status_t err = sAllocator->priorLinearAllocation(handle, &alloc);
+            const std::shared_ptr<C2PooledBlockPoolData> poolData =
+                    std::make_shared<C2PooledBlockPoolData>(data);
+            if (err == C2_OK && poolData) {
+                // TODO: config params?
+                std::shared_ptr<C2LinearBlock> block =
+                        _C2BlockFactory::CreateLinearBlock(alloc, poolData);
+                return block;
+            }
+        }
+    }
+    return nullptr;
+};
+
+/**
+ * Wrapped C2Allocator which is injected to buffer pool on behalf of
+ * C2BlockPool.
+ */
+class _C2BufferPoolAllocator : public BufferPoolAllocator {
+public:
+    _C2BufferPoolAllocator(const std::shared_ptr<C2Allocator> &allocator)
+        : mAllocator(allocator) {}
+
+    ~_C2BufferPoolAllocator() override {}
+
+    ResultStatus allocate(const std::vector<uint8_t> &params,
+                          std::shared_ptr<BufferPoolAllocation> *alloc,
+                          size_t *allocSize) override;
+
+    bool compatible(const std::vector<uint8_t> &newParams,
+                    const std::vector<uint8_t> &oldParams) override;
+
+    // Methods for codec2 component (C2BlockPool).
+    /**
+     * Transforms linear allocation parameters for C2Allocator to parameters
+     * for buffer pool.
+     *
+     * @param capacity      size of linear allocation
+     * @param usage         memory usage pattern for linear allocation
+     * @param params        allocation parameters for buffer pool
+     */
+    void getLinearParams(uint32_t capacity, C2MemoryUsage usage,
+                         std::vector<uint8_t> *params);
+
+    /**
+     * Transforms graphic allocation parameters for C2Allocator to parameters
+     * for buffer pool.
+     *
+     * @param width         width of graphic allocation
+     * @param height        height of graphic allocation
+     * @param format        color format of graphic allocation
+     * @param params        allocation parameter for buffer pool
+     */
+    void getGraphicParams(uint32_t width, uint32_t height,
+                          uint32_t format, C2MemoryUsage usage,
+                          std::vector<uint8_t> *params);
+
+    /**
+     * Transforms an existing native handle to an C2LinearAllcation.
+     * Wrapper to C2Allocator#priorLinearAllocation
+     */
+    c2_status_t priorLinearAllocation(
+            const C2Handle *handle,
+            std::shared_ptr<C2LinearAllocation> *c2Allocation);
+
+    /**
+     * Transforms an existing native handle to an C2GraphicAllcation.
+     * Wrapper to C2Allocator#priorGraphicAllocation
+     */
+    c2_status_t priorGraphicAllocation(
+            const C2Handle *handle,
+            std::shared_ptr<C2GraphicAllocation> *c2Allocation);
+
+private:
+    static constexpr int kMaxIntParams = 5; // large enough number;
+
+    enum AllocType : uint8_t {
+        ALLOC_NONE = 0,
+
+        ALLOC_LINEAR,
+        ALLOC_GRAPHIC,
+    };
+
+    union AllocParams {
+        struct {
+            AllocType allocType;
+            C2MemoryUsage usage;
+            uint32_t params[kMaxIntParams];
+        } data;
+        uint8_t array[0];
+
+        AllocParams() : data{ALLOC_NONE, {0, 0}, {0}} {}
+        AllocParams(C2MemoryUsage usage, uint32_t capacity)
+            : data{ALLOC_LINEAR, usage, {[0] = capacity}} {}
+        AllocParams(
+                C2MemoryUsage usage,
+                uint32_t width, uint32_t height, uint32_t format)
+                : data{ALLOC_GRAPHIC, usage, {width, height, format}} {}
+    };
+
+    const std::shared_ptr<C2Allocator> mAllocator;
+};
+
+struct LinearAllocationDtor {
+    LinearAllocationDtor(const std::shared_ptr<C2LinearAllocation> &alloc)
+        : mAllocation(alloc) {}
+
+    void operator()(BufferPoolAllocation *poolAlloc) { delete poolAlloc; }
+
+    const std::shared_ptr<C2LinearAllocation> mAllocation;
+};
+
+struct GraphicAllocationDtor {
+    GraphicAllocationDtor(const std::shared_ptr<C2GraphicAllocation> &alloc)
+        : mAllocation(alloc) {}
+
+    void operator()(BufferPoolAllocation *poolAlloc) { delete poolAlloc; }
+
+    const std::shared_ptr<C2GraphicAllocation> mAllocation;
+};
+
+ResultStatus _C2BufferPoolAllocator::allocate(
+        const std::vector<uint8_t>  &params,
+        std::shared_ptr<BufferPoolAllocation> *alloc,
+        size_t *allocSize) {
+    AllocParams c2Params;
+    memcpy(&c2Params, params.data(), std::min(sizeof(AllocParams), params.size()));
+    c2_status_t status = C2_BAD_VALUE;
+    switch(c2Params.data.allocType) {
+        case ALLOC_NONE:
+            break;
+        case ALLOC_LINEAR: {
+            std::shared_ptr<C2LinearAllocation> c2Linear;
+            status = mAllocator->newLinearAllocation(
+                    c2Params.data.params[0], c2Params.data.usage, &c2Linear);
+            if (status == C2_OK && c2Linear) {
+                BufferPoolAllocation *ptr = new BufferPoolAllocation(c2Linear->handle());
+                if (ptr) {
+                    *alloc = std::shared_ptr<BufferPoolAllocation>(
+                            ptr, LinearAllocationDtor(c2Linear));
+                    if (*alloc) {
+                        *allocSize = (size_t)c2Params.data.params[0];
+                        return ResultStatus::OK;
+                    }
+                    delete ptr;
+                }
+                return ResultStatus::NO_MEMORY;
+            }
+            break;
+        }
+        case ALLOC_GRAPHIC: {
+            std::shared_ptr<C2GraphicAllocation> c2Graphic;
+            status = mAllocator->newGraphicAllocation(
+                    c2Params.data.params[0],
+                    c2Params.data.params[1],
+                    c2Params.data.params[2],
+                    c2Params.data.usage, &c2Graphic);
+            if (status == C2_OK && c2Graphic) {
+                BufferPoolAllocation *ptr = new BufferPoolAllocation(c2Graphic->handle());
+                if (ptr) {
+                    *alloc = std::shared_ptr<BufferPoolAllocation>(
+                            ptr, GraphicAllocationDtor(c2Graphic));
+                    if (*alloc) {
+                        *allocSize = c2Params.data.params[0] * c2Params.data.params[1];
+                        return ResultStatus::OK;
+                    }
+                    delete ptr;
+                }
+                return ResultStatus::NO_MEMORY;
+            }
+            break;
+        }
+        default:
+            break;
+    }
+    return ResultStatus::CRITICAL_ERROR;
+}
+
+bool _C2BufferPoolAllocator::compatible(
+        const std::vector<uint8_t>  &newParams,
+        const std::vector<uint8_t>  &oldParams) {
+    AllocParams newAlloc;
+    AllocParams oldAlloc;
+    memcpy(&newAlloc, newParams.data(), std::min(sizeof(AllocParams), newParams.size()));
+    memcpy(&oldAlloc, oldParams.data(), std::min(sizeof(AllocParams), oldParams.size()));
+
+    // TODO: support not exact matching. e.g) newCapacity < oldCapacity
+    if (newAlloc.data.allocType == oldAlloc.data.allocType &&
+            newAlloc.data.usage.expected == oldAlloc.data.usage.expected) {
+        for (int i = 0; i < kMaxIntParams; ++i) {
+            if (newAlloc.data.params[i] != oldAlloc.data.params[i]) {
+                return false;
+            }
+        }
+        return true;
+    }
+    return false;
+}
+
+void _C2BufferPoolAllocator::getLinearParams(
+        uint32_t capacity, C2MemoryUsage usage, std::vector<uint8_t> *params) {
+    AllocParams c2Params(usage, capacity);
+    params->assign(c2Params.array, c2Params.array + sizeof(AllocParams));
+}
+
+void _C2BufferPoolAllocator::getGraphicParams(
+        uint32_t width, uint32_t height, uint32_t format, C2MemoryUsage usage,
+        std::vector<uint8_t> *params) {
+    AllocParams c2Params(usage, width, height, format);
+    params->assign(c2Params.array, c2Params.array + sizeof(AllocParams));
+}
+
+c2_status_t _C2BufferPoolAllocator::priorLinearAllocation(
+        const C2Handle *handle,
+        std::shared_ptr<C2LinearAllocation> *c2Allocation) {
+    return mAllocator->priorLinearAllocation(handle, c2Allocation);
+}
+
+c2_status_t _C2BufferPoolAllocator::priorGraphicAllocation(
+        const C2Handle *handle,
+        std::shared_ptr<C2GraphicAllocation> *c2Allocation) {
+    return mAllocator->priorGraphicAllocation(handle, c2Allocation);
+}
+
+class C2PooledBlockPool::Impl {
+public:
+    Impl(const std::shared_ptr<C2Allocator> &allocator)
+            : mInit(C2_OK),
+              mBufferPoolManager(ClientManager::getInstance()),
+              mAllocator(std::make_shared<_C2BufferPoolAllocator>(allocator)) {
+        if (mAllocator && mBufferPoolManager) {
+            if (mBufferPoolManager->create(
+                    mAllocator, &mConnectionId) == ResultStatus::OK) {
+                return;
+            }
+        }
+        mInit = C2_NO_INIT;
+    }
+
+    ~Impl() {
+        if (mInit == C2_OK) {
+            mBufferPoolManager->close(mConnectionId);
+        }
+    }
+
+    c2_status_t fetchLinearBlock(
+            uint32_t capacity, C2MemoryUsage usage,
+            std::shared_ptr<C2LinearBlock> *block /* nonnull */) {
+        block->reset();
+        if (mInit != C2_OK) {
+            return mInit;
+        }
+        std::vector<uint8_t> params;
+        mAllocator->getLinearParams(capacity, usage, &params);
+        std::shared_ptr<BufferPoolData> bufferPoolData;
+        native_handle_t *cHandle = nullptr;
+        ResultStatus status = mBufferPoolManager->allocate(
+                mConnectionId, params, &cHandle, &bufferPoolData);
+        if (status == ResultStatus::OK) {
+            native_handle_t *handle = native_handle_clone(cHandle);
+            if (handle) {
+                std::shared_ptr<C2LinearAllocation> alloc;
+                std::shared_ptr<C2PooledBlockPoolData> poolData =
+                        std::make_shared<C2PooledBlockPoolData>(bufferPoolData);
+                c2_status_t err = mAllocator->priorLinearAllocation(handle, &alloc);
+                if (err == C2_OK && poolData && alloc) {
+                    *block = _C2BlockFactory::CreateLinearBlock(alloc, poolData, 0, capacity);
+                    if (*block) {
+                        return C2_OK;
+                    }
+                }
+            }
+            return C2_NO_MEMORY;
+        }
+        if (status == ResultStatus::NO_MEMORY) {
+            return C2_NO_MEMORY;
+        }
+        return C2_CORRUPTED;
+    }
+
+    c2_status_t fetchGraphicBlock(
+            uint32_t width, uint32_t height, uint32_t format,
+            C2MemoryUsage usage,
+            std::shared_ptr<C2GraphicBlock> *block) {
+        block->reset();
+        if (mInit != C2_OK) {
+            return mInit;
+        }
+        std::vector<uint8_t> params;
+        mAllocator->getGraphicParams(width, height, format, usage, &params);
+        std::shared_ptr<BufferPoolData> bufferPoolData;
+        native_handle_t *cHandle = nullptr;
+        ResultStatus status = mBufferPoolManager->allocate(
+                mConnectionId, params, &cHandle, &bufferPoolData);
+        if (status == ResultStatus::OK) {
+            native_handle_t *handle = native_handle_clone(cHandle);
+            if (handle) {
+                std::shared_ptr<C2GraphicAllocation> alloc;
+                std::shared_ptr<C2PooledBlockPoolData> poolData =
+                    std::make_shared<C2PooledBlockPoolData>(bufferPoolData);
+                c2_status_t err = mAllocator->priorGraphicAllocation(
+                        handle, &alloc);
+                if (err == C2_OK && poolData && alloc) {
+                    *block = _C2BlockFactory::CreateGraphicBlock(
+                            alloc, poolData, C2Rect(width, height));
+                    if (*block) {
+                        return C2_OK;
+                    }
+                }
+            }
+            return C2_NO_MEMORY;
+        }
+        if (status == ResultStatus::NO_MEMORY) {
+            return C2_NO_MEMORY;
+        }
+        return C2_CORRUPTED;
+    }
+
+    ConnectionId getConnectionId() {
+        return mInit != C2_OK ? INVALID_CONNECTIONID : mConnectionId;
+    }
+
+private:
+    c2_status_t mInit;
+    const android::sp<ClientManager> mBufferPoolManager;
+    ConnectionId mConnectionId; // locally
+    const std::shared_ptr<_C2BufferPoolAllocator> mAllocator;
+};
+
+C2PooledBlockPool::C2PooledBlockPool(
+        const std::shared_ptr<C2Allocator> &allocator, const local_id_t localId)
+        : mAllocator(allocator), mLocalId(localId), mImpl(new Impl(allocator)) {}
+
+C2PooledBlockPool::~C2PooledBlockPool() {
+}
+
+c2_status_t C2PooledBlockPool::fetchLinearBlock(
+        uint32_t capacity,
+        C2MemoryUsage usage,
+        std::shared_ptr<C2LinearBlock> *block /* nonnull */) {
+    if (mImpl) {
+        return mImpl->fetchLinearBlock(capacity, usage, block);
+    }
+    return C2_CORRUPTED;
+}
+
+c2_status_t C2PooledBlockPool::fetchGraphicBlock(
+        uint32_t width,
+        uint32_t height,
+        uint32_t format,
+        C2MemoryUsage usage,
+        std::shared_ptr<C2GraphicBlock> *block) {
+    if (mImpl) {
+        return mImpl->fetchGraphicBlock(width, height, format, usage, block);
+    }
+    return C2_CORRUPTED;
+}
+
+int64_t C2PooledBlockPool::getConnectionId() {
+    if (mImpl) {
+        return mImpl->getConnectionId();
+    }
+    return 0;
+}
+
+/* ========================================== 2D BLOCK ========================================= */
+
+/**
+ * Implementation that is shared between all 2D blocks and views.
+ *
+ * For blocks' Impl's crop is always the allotted crop, even if it is a sub block.
+ *
+ * For views' Impl's crop is the mapped portion - which for now is always the
+ * allotted crop.
+ */
+class C2_HIDE _C2Block2DImpl : public _C2PlanarSectionAspect {
+public:
+    /**
+     * Impl's crop is always the or part of the allotted crop of the allocation.
+     */
+    _C2Block2DImpl(const std::shared_ptr<C2GraphicAllocation> &alloc,
+            const std::shared_ptr<_C2BlockPoolData> &poolData = nullptr,
+            const C2Rect &allottedCrop = C2Rect(~0u, ~0u))
+        : _C2PlanarSectionAspect(alloc.get(), allottedCrop),
+          mAllocation(alloc),
+          mPoolData(poolData) { }
+
+    virtual ~_C2Block2DImpl() = default;
+
+    /** returns pool data  */
+    std::shared_ptr<_C2BlockPoolData> poolData() const {
+        return mPoolData;
+    }
+
+    /** returns native handle */
+    const C2Handle *handle() const {
+        return mAllocation ? mAllocation->handle() : nullptr;
+    }
+
+    /** returns the allocator's ID */
+    C2Allocator::id_t getAllocatorId() const {
+        // BAD_ID can only happen if this Impl class is initialized for a view - never for a block.
+        return mAllocation ? mAllocation->getAllocatorId() : C2Allocator::BAD_ID;
+    }
+
+    std::shared_ptr<C2GraphicAllocation> getAllocation() const {
+        return mAllocation;
+    }
+
+private:
+    std::shared_ptr<C2GraphicAllocation> mAllocation;
+    std::shared_ptr<_C2BlockPoolData> mPoolData;
+};
+
+class C2_HIDE _C2MappingBlock2DImpl
+    : public _C2Block2DImpl, public std::enable_shared_from_this<_C2MappingBlock2DImpl> {
+public:
+    using _C2Block2DImpl::_C2Block2DImpl;
+
+    virtual ~_C2MappingBlock2DImpl() override = default;
+
+    /**
+     * This class contains the mapped data pointer, and the potential error.
+     */
+    struct Mapped {
+    private:
+        friend class _C2MappingBlock2DImpl;
+
+        Mapped(const std::shared_ptr<_C2Block2DImpl> &impl, bool writable, C2Fence *fence __unused)
+            : mImpl(impl), mWritable(writable) {
+            memset(mData, 0, sizeof(mData));
+            const C2Rect crop = mImpl->crop();
+            // gralloc requires mapping the whole region of interest as we cannot
+            // map multiple regions
+            mError = mImpl->getAllocation()->map(
+                    crop,
+                    { C2MemoryUsage::CPU_READ, writable ? C2MemoryUsage::CPU_WRITE : 0 },
+                    nullptr,
+                    &mLayout,
+                    mData);
+            if (mError != C2_OK) {
+                memset(&mLayout, 0, sizeof(mLayout));
+                memset(mData, 0, sizeof(mData));
+                memset(mOffsetData, 0, sizeof(mData));
+            } else {
+                // TODO: validate plane layout and
+                // adjust data pointers to the crop region's top left corner.
+                // fail if it is not on a subsampling boundary
+                for (size_t planeIx = 0; planeIx < mLayout.numPlanes; ++planeIx) {
+                    const uint32_t colSampling = mLayout.planes[planeIx].colSampling;
+                    const uint32_t rowSampling = mLayout.planes[planeIx].rowSampling;
+                    if (crop.left % colSampling || crop.right() % colSampling
+                            || crop.top % rowSampling || crop.bottom() % rowSampling) {
+                        // cannot calculate data pointer
+                        mImpl->getAllocation()->unmap(mData, crop, nullptr);
+                        memset(&mLayout, 0, sizeof(mLayout));
+                        memset(mData, 0, sizeof(mData));
+                        memset(mOffsetData, 0, sizeof(mData));
+                        mError = C2_BAD_VALUE;
+                        return;
+                    }
+                    mOffsetData[planeIx] =
+                        mData[planeIx] + (ssize_t)crop.left * mLayout.planes[planeIx].colInc
+                                + (ssize_t)crop.top * mLayout.planes[planeIx].rowInc;
+                }
+            }
+        }
+
+        explicit Mapped(c2_status_t error)
+            : mImpl(nullptr), mWritable(false), mError(error) {
+            // CHECK(error != C2_OK);
+            memset(&mLayout, 0, sizeof(mLayout));
+            memset(mData, 0, sizeof(mData));
+            memset(mOffsetData, 0, sizeof(mData));
+        }
+
+    public:
+        ~Mapped() {
+            if (mData[0] != nullptr) {
+                mImpl->getAllocation()->unmap(mData, mImpl->crop(), nullptr);
+            }
+        }
+
+        /** returns mapping status */
+        c2_status_t error() const { return mError; }
+
+        /** returns data pointer */
+        uint8_t *const *data() const { return mOffsetData; }
+
+        /** returns the plane layout */
+        C2PlanarLayout layout() const { return mLayout; }
+
+        /** returns whether the mapping is writable */
+        bool writable() const { return mWritable; }
+
+    private:
+        const std::shared_ptr<_C2Block2DImpl> mImpl;
+        bool mWritable;
+        c2_status_t mError;
+        uint8_t *mData[C2PlanarLayout::MAX_NUM_PLANES];
+        uint8_t *mOffsetData[C2PlanarLayout::MAX_NUM_PLANES];
+        C2PlanarLayout mLayout;
+    };
+
+    /**
+     * Maps the allotted region.
+     *
+     * If already mapped and it is currently in use, returns the existing mapping.
+     * If fence is provided, an acquire fence is stored there.
+     */
+    std::shared_ptr<Mapped> map(bool writable, C2Fence *fence) {
+        std::lock_guard<std::mutex> lock(mMappedLock);
+        std::shared_ptr<Mapped> existing = mMapped.lock();
+        if (!existing) {
+            existing = std::shared_ptr<Mapped>(new Mapped(shared_from_this(), writable, fence));
+            mMapped = existing;
+        } else {
+            // if we mapped the region read-only, we cannot remap it read-write
+            if (writable && !existing->writable()) {
+                existing = std::shared_ptr<Mapped>(new Mapped(C2_CANNOT_DO));
+            }
+            if (fence != nullptr) {
+                *fence = C2Fence();
+            }
+        }
+        return existing;
+    }
+
+private:
+    std::weak_ptr<Mapped> mMapped;
+    std::mutex mMappedLock;
+};
+
+class C2_HIDE _C2MappedBlock2DImpl : public _C2Block2DImpl {
+public:
+    _C2MappedBlock2DImpl(const _C2Block2DImpl &impl,
+                         std::shared_ptr<_C2MappingBlock2DImpl::Mapped> mapping)
+        : _C2Block2DImpl(impl), mMapping(mapping) {
+    }
+
+    virtual ~_C2MappedBlock2DImpl() override = default;
+
+    std::shared_ptr<_C2MappingBlock2DImpl::Mapped> mapping() const { return mMapping; }
+
+private:
+    std::shared_ptr<_C2MappingBlock2DImpl::Mapped> mMapping;
+};
+
+/**
+ * Block implementation.
+ */
+class C2Block2D::Impl : public _C2MappingBlock2DImpl {
+public:
+    using _C2MappingBlock2DImpl::_C2MappingBlock2DImpl;
+    virtual ~Impl() override = default;
+};
+
+const C2Handle *C2Block2D::handle() const {
+    return mImpl->handle();
+}
+
+C2Allocator::id_t C2Block2D::getAllocatorId() const {
+    return mImpl->getAllocatorId();
+}
+
+C2Block2D::C2Block2D(std::shared_ptr<Impl> impl, const _C2PlanarSectionAspect &section)
+    // always clamp subsection to parent (impl) crop for safety
+    : _C2PlanarSectionAspect(impl.get(), section.crop()), mImpl(impl) {
+}
+
+/**
+ * Graphic view implementation.
+ *
+ * range of Impl is the mapped range of the underlying allocation. range of View is the current
+ * crop.
+ */
+class C2GraphicView::Impl : public _C2MappedBlock2DImpl {
+public:
+    using _C2MappedBlock2DImpl::_C2MappedBlock2DImpl;
+    virtual ~Impl() override = default;
+};
+
+C2GraphicView::C2GraphicView(std::shared_ptr<Impl> impl, const _C2PlanarSectionAspect &section)
+    : _C2EditablePlanarSectionAspect(impl.get(), section.crop()), mImpl(impl) {
+}
+
+const uint8_t *const *C2GraphicView::data() const {
+    return mImpl->mapping()->data();
+}
+
+uint8_t *const *C2GraphicView::data() {
+    return mImpl->mapping()->data();
+}
+
+const C2PlanarLayout C2GraphicView::layout() const {
+    return mImpl->mapping()->layout();
+}
+
+const C2GraphicView C2GraphicView::subView(const C2Rect &rect) const {
+    return C2GraphicView(mImpl, C2PlanarSection(*mImpl, rect));
+}
+
+C2GraphicView C2GraphicView::subView(const C2Rect &rect) {
+    return C2GraphicView(mImpl, C2PlanarSection(*mImpl, rect));
+}
+
+c2_status_t C2GraphicView::error() const {
+    return mImpl->mapping()->error();
+}
+
+/**
+ * Const graphic block implementation.
+ */
+C2ConstGraphicBlock::C2ConstGraphicBlock(
+        std::shared_ptr<Impl> impl, const _C2PlanarSectionAspect &section, C2Fence fence)
+    : C2Block2D(impl, section), mFence(fence) { }
+
+C2Acquirable<const C2GraphicView> C2ConstGraphicBlock::map() const {
+    C2Fence fence;
+    std::shared_ptr<_C2MappingBlock2DImpl::Mapped> mapping =
+        mImpl->map(false /* writable */, &fence);
+    std::shared_ptr<GraphicViewBuddy::Impl> gvi =
+        std::shared_ptr<GraphicViewBuddy::Impl>(new GraphicViewBuddy::Impl(*mImpl, mapping));
+    return AcquirableConstGraphicViewBuddy(
+            mapping->error(), fence, GraphicViewBuddy(gvi, C2PlanarSection(*mImpl, crop())));
+}
+
+C2ConstGraphicBlock C2ConstGraphicBlock::subBlock(const C2Rect &rect) const {
+    return C2ConstGraphicBlock(mImpl, C2PlanarSection(*mImpl, crop().intersect(rect)), mFence);
+}
+
+/**
+ * Graphic block implementation.
+ */
+C2GraphicBlock::C2GraphicBlock(
+    std::shared_ptr<Impl> impl, const _C2PlanarSectionAspect &section)
+    : C2Block2D(impl, section) { }
+
+C2Acquirable<C2GraphicView> C2GraphicBlock::map() {
+    C2Fence fence;
+    std::shared_ptr<_C2MappingBlock2DImpl::Mapped> mapping =
+        mImpl->map(true /* writable */, &fence);
+    std::shared_ptr<GraphicViewBuddy::Impl> gvi =
+        std::shared_ptr<GraphicViewBuddy::Impl>(new GraphicViewBuddy::Impl(*mImpl, mapping));
+    return AcquirableGraphicViewBuddy(
+            mapping->error(), fence, GraphicViewBuddy(gvi, C2PlanarSection(*mImpl, crop())));
+}
+
+C2ConstGraphicBlock C2GraphicBlock::share(const C2Rect &crop, C2Fence fence) {
+    return ConstGraphicBlockBuddy(mImpl, C2PlanarSection(*mImpl, crop), fence);
+}
+
+/**
+ * Basic block pool implementations.
+ */
+C2BasicGraphicBlockPool::C2BasicGraphicBlockPool(
+        const std::shared_ptr<C2Allocator> &allocator)
+  : mAllocator(allocator) {}
+
+c2_status_t C2BasicGraphicBlockPool::fetchGraphicBlock(
+        uint32_t width,
+        uint32_t height,
+        uint32_t format,
+        C2MemoryUsage usage,
+        std::shared_ptr<C2GraphicBlock> *block /* nonnull */) {
+    block->reset();
+
+    std::shared_ptr<C2GraphicAllocation> alloc;
+    c2_status_t err = mAllocator->newGraphicAllocation(width, height, format, usage, &alloc);
+    if (err != C2_OK) {
+        return err;
+    }
+
+    *block = _C2BlockFactory::CreateGraphicBlock(alloc);
+
+    return C2_OK;
+}
+
+std::shared_ptr<C2GraphicBlock> _C2BlockFactory::CreateGraphicBlock(
+        const std::shared_ptr<C2GraphicAllocation> &alloc,
+        const std::shared_ptr<_C2BlockPoolData> &data, const C2Rect &allottedCrop) {
+    std::shared_ptr<C2Block2D::Impl> impl =
+        std::make_shared<C2Block2D::Impl>(alloc, data, allottedCrop);
+    return std::shared_ptr<C2GraphicBlock>(new C2GraphicBlock(impl, *impl));
+}
+
+std::shared_ptr<_C2BlockPoolData> _C2BlockFactory::GetGraphicBlockPoolData(
+        const C2Block2D &block) {
+    if (block.mImpl) {
+        return block.mImpl->poolData();
+    }
+    return nullptr;
+}
+
+std::shared_ptr<C2GraphicBlock> _C2BlockFactory::CreateGraphicBlock(
+        const C2Handle *cHandle,
+        const std::shared_ptr<BufferPoolData> &data) {
+    // TODO: get proper allocator? and mutex?
+    static std::unique_ptr<C2AllocatorGralloc> sAllocator = std::make_unique<C2AllocatorGralloc>(0);
+
+    std::shared_ptr<C2GraphicAllocation> alloc;
+    if (C2AllocatorGralloc::isValid(cHandle)) {
+        native_handle_t *handle = native_handle_clone(cHandle);
+        if (handle) {
+            c2_status_t err = sAllocator->priorGraphicAllocation(handle, &alloc);
+            const std::shared_ptr<C2PooledBlockPoolData> poolData =
+                    std::make_shared<C2PooledBlockPoolData>(data);
+            if (err == C2_OK && poolData) {
+                // TODO: config setup?
+                std::shared_ptr<C2GraphicBlock> block =
+                        _C2BlockFactory::CreateGraphicBlock(alloc, poolData);
+                return block;
+            }
+        }
+    }
+    return nullptr;
+};
+
+
+/* ========================================== BUFFER ========================================= */
+
+class C2BufferData::Impl {
+public:
+    explicit Impl(const std::vector<C2ConstLinearBlock> &blocks)
+        : mType(blocks.size() == 1 ? LINEAR : LINEAR_CHUNKS),
+          mLinearBlocks(blocks) {
+    }
+
+    explicit Impl(const std::vector<C2ConstGraphicBlock> &blocks)
+        : mType(blocks.size() == 1 ? GRAPHIC : GRAPHIC_CHUNKS),
+          mGraphicBlocks(blocks) {
+    }
+
+    type_t type() const { return mType; }
+    const std::vector<C2ConstLinearBlock> &linearBlocks() const { return mLinearBlocks; }
+    const std::vector<C2ConstGraphicBlock> &graphicBlocks() const { return mGraphicBlocks; }
+
+private:
+    type_t mType;
+    std::vector<C2ConstLinearBlock> mLinearBlocks;
+    std::vector<C2ConstGraphicBlock> mGraphicBlocks;
+};
+
+C2BufferData::C2BufferData(const std::vector<C2ConstLinearBlock> &blocks) : mImpl(new Impl(blocks)) {}
+C2BufferData::C2BufferData(const std::vector<C2ConstGraphicBlock> &blocks) : mImpl(new Impl(blocks)) {}
+
+C2BufferData::type_t C2BufferData::type() const { return mImpl->type(); }
+
+const std::vector<C2ConstLinearBlock> C2BufferData::linearBlocks() const {
+    return mImpl->linearBlocks();
+}
+
+const std::vector<C2ConstGraphicBlock> C2BufferData::graphicBlocks() const {
+    return mImpl->graphicBlocks();
+}
+
+class C2Buffer::Impl {
+public:
+    Impl(C2Buffer *thiz, const std::vector<C2ConstLinearBlock> &blocks)
+        : mThis(thiz), mData(blocks) {}
+    Impl(C2Buffer *thiz, const std::vector<C2ConstGraphicBlock> &blocks)
+        : mThis(thiz), mData(blocks) {}
+
+    ~Impl() {
+        for (const auto &pair : mNotify) {
+            pair.first(mThis, pair.second);
+        }
+    }
+
+    const C2BufferData &data() const { return mData; }
+
+    c2_status_t registerOnDestroyNotify(OnDestroyNotify onDestroyNotify, void *arg) {
+        auto it = std::find_if(
+                mNotify.begin(), mNotify.end(),
+                [onDestroyNotify, arg] (const auto &pair) {
+                    return pair.first == onDestroyNotify && pair.second == arg;
+                });
+        if (it != mNotify.end()) {
+            return C2_DUPLICATE;
+        }
+        mNotify.emplace_back(onDestroyNotify, arg);
+        return C2_OK;
+    }
+
+    c2_status_t unregisterOnDestroyNotify(OnDestroyNotify onDestroyNotify, void *arg) {
+        auto it = std::find_if(
+                mNotify.begin(), mNotify.end(),
+                [onDestroyNotify, arg] (const auto &pair) {
+                    return pair.first == onDestroyNotify && pair.second == arg;
+                });
+        if (it == mNotify.end()) {
+            return C2_NOT_FOUND;
+        }
+        mNotify.erase(it);
+        return C2_OK;
+    }
+
+    std::vector<std::shared_ptr<const C2Info>> info() const {
+        std::vector<std::shared_ptr<const C2Info>> result(mInfos.size());
+        std::transform(
+                mInfos.begin(), mInfos.end(), result.begin(),
+                [] (const auto &elem) { return elem.second; });
+        return result;
+    }
+
+    c2_status_t setInfo(const std::shared_ptr<C2Info> &info) {
+        // To "update" you need to erase the existing one if any, and then insert.
+        (void) mInfos.erase(info->coreIndex());
+        (void) mInfos.insert({ info->coreIndex(), info });
+        return C2_OK;
+    }
+
+    bool hasInfo(C2Param::Type index) const {
+        return mInfos.count(index.coreIndex()) > 0;
+    }
+
+    std::shared_ptr<const C2Info> getInfo(C2Param::Type index) const {
+        auto it = mInfos.find(index.coreIndex());
+        if (it == mInfos.end()) {
+            return nullptr;
+        }
+        return std::const_pointer_cast<const C2Info>(it->second);
+    }
+
+    std::shared_ptr<C2Info> removeInfo(C2Param::Type index) {
+        auto it = mInfos.find(index.coreIndex());
+        if (it == mInfos.end()) {
+            return nullptr;
+        }
+        std::shared_ptr<C2Info> ret = it->second;
+        (void) mInfos.erase(it);
+        return ret;
+    }
+
+private:
+    C2Buffer * const mThis;
+    BufferDataBuddy mData;
+    std::map<C2Param::CoreIndex, std::shared_ptr<C2Info>> mInfos;
+    std::list<std::pair<OnDestroyNotify, void *>> mNotify;
+};
+
+C2Buffer::C2Buffer(const std::vector<C2ConstLinearBlock> &blocks)
+    : mImpl(new Impl(this, blocks)) {}
+
+C2Buffer::C2Buffer(const std::vector<C2ConstGraphicBlock> &blocks)
+    : mImpl(new Impl(this, blocks)) {}
+
+const C2BufferData C2Buffer::data() const { return mImpl->data(); }
+
+c2_status_t C2Buffer::registerOnDestroyNotify(OnDestroyNotify onDestroyNotify, void *arg) {
+    return mImpl->registerOnDestroyNotify(onDestroyNotify, arg);
+}
+
+c2_status_t C2Buffer::unregisterOnDestroyNotify(OnDestroyNotify onDestroyNotify, void *arg) {
+    return mImpl->unregisterOnDestroyNotify(onDestroyNotify, arg);
+}
+
+const std::vector<std::shared_ptr<const C2Info>> C2Buffer::info() const {
+    return mImpl->info();
+}
+
+c2_status_t C2Buffer::setInfo(const std::shared_ptr<C2Info> &info) {
+    return mImpl->setInfo(info);
+}
+
+bool C2Buffer::hasInfo(C2Param::Type index) const {
+    return mImpl->hasInfo(index);
+}
+
+std::shared_ptr<const C2Info> C2Buffer::getInfo(C2Param::Type index) const {
+    return mImpl->getInfo(index);
+}
+
+std::shared_ptr<C2Info> C2Buffer::removeInfo(C2Param::Type index) {
+    return mImpl->removeInfo(index);
+}
+
+// static
+std::shared_ptr<C2Buffer> C2Buffer::CreateLinearBuffer(const C2ConstLinearBlock &block) {
+    return std::shared_ptr<C2Buffer>(new C2Buffer({ block }));
+}
+
+// static
+std::shared_ptr<C2Buffer> C2Buffer::CreateGraphicBuffer(const C2ConstGraphicBlock &block) {
+    return std::shared_ptr<C2Buffer>(new C2Buffer({ block }));
+}
+
diff --git a/media/codec2/vndk/C2Config.cpp b/media/codec2/vndk/C2Config.cpp
new file mode 100644
index 0000000..da12903
--- /dev/null
+++ b/media/codec2/vndk/C2Config.cpp
@@ -0,0 +1,231 @@
+/*
+ * Copyright (C) 2017 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_NDEBUG 0
+#define LOG_TAG "C2Config"
+#include <utils/Log.h>
+
+/**
+ * Define and initialize global config field descriptors in this cpp file
+ */
+#define __C2_GENERATE_GLOBAL_VARS__
+#include <C2Config.h>
+
+DEFINE_C2_ENUM_VALUE_CUSTOM_HELPER(C2Config::profile_t, ({
+        { "unused", C2Config::PROFILE_UNUSED },
+        { "aac-lc", C2Config::PROFILE_AAC_LC },
+        { "aac-main", C2Config::PROFILE_AAC_MAIN },
+        { "aac-ssr", C2Config::PROFILE_AAC_SSR },
+        { "aac-ltp", C2Config::PROFILE_AAC_LTP },
+        { "aac-he", C2Config::PROFILE_AAC_HE },
+        { "aac-scalable", C2Config::PROFILE_AAC_SCALABLE },
+        { "aac-er-lc", C2Config::PROFILE_AAC_ER_LC },
+        { "aac-er-scalable", C2Config::PROFILE_AAC_ER_SCALABLE },
+        { "aac-ld", C2Config::PROFILE_AAC_LD },
+        { "aac-he-ps", C2Config::PROFILE_AAC_HE_PS },
+        { "aac-eld", C2Config::PROFILE_AAC_ELD },
+        { "aac-xhe", C2Config::PROFILE_AAC_XHE },
+        { "mp2v-simple", C2Config::PROFILE_MP2V_SIMPLE },
+        { "mp2v-main", C2Config::PROFILE_MP2V_MAIN },
+        { "mp2v-snr-scalable", C2Config::PROFILE_MP2V_SNR_SCALABLE },
+        { "mp2v-spatially-scalable", C2Config::PROFILE_MP2V_SPATIALLY_SCALABLE },
+        { "mp2v-high", C2Config::PROFILE_MP2V_HIGH },
+        { "mp2v-422", C2Config::PROFILE_MP2V_422 },
+        { "mp2v-multiview", C2Config::PROFILE_MP2V_MULTIVIEW },
+        { "h263-baseline", C2Config::PROFILE_H263_BASELINE },
+        { "h263-h320", C2Config::PROFILE_H263_H320 },
+        { "h263-v1bc", C2Config::PROFILE_H263_V1BC },
+        { "h263-iswv2", C2Config::PROFILE_H263_ISWV2 },
+        { "h263-iswv3", C2Config::PROFILE_H263_ISWV3 },
+        { "h263-high-compression", C2Config::PROFILE_H263_HIGH_COMPRESSION },
+        { "h263-internet", C2Config::PROFILE_H263_INTERNET },
+        { "h263-interlace", C2Config::PROFILE_H263_INTERLACE },
+        { "h263-high-latency", C2Config::PROFILE_H263_HIGH_LATENCY },
+        { "mp4v-simple", C2Config::PROFILE_MP4V_SIMPLE },
+        { "mp4v-simple-scalable", C2Config::PROFILE_MP4V_SIMPLE_SCALABLE },
+        { "mp4v-core", C2Config::PROFILE_MP4V_CORE },
+        { "mp4v-main", C2Config::PROFILE_MP4V_MAIN },
+        { "mp4v-nbit", C2Config::PROFILE_MP4V_NBIT },
+        { "mp4v-arts", C2Config::PROFILE_MP4V_ARTS },
+        { "mp4v-core-scalable", C2Config::PROFILE_MP4V_CORE_SCALABLE },
+        { "mp4v-ace", C2Config::PROFILE_MP4V_ACE },
+        { "mp4v-advanced-core", C2Config::PROFILE_MP4V_ADVANCED_CORE },
+        { "mp4v-simple-studio", C2Config::PROFILE_MP4V_SIMPLE_STUDIO },
+        { "mp4v-core-studio", C2Config::PROFILE_MP4V_CORE_STUDIO },
+        { "mp4v-advanced-simple", C2Config::PROFILE_MP4V_ADVANCED_SIMPLE },
+        { "mp4v-fgs", C2Config::PROFILE_MP4V_FGS },
+        { "avc-baseline", C2Config::PROFILE_AVC_BASELINE },
+        { "avc-constrained-baseline", C2Config::PROFILE_AVC_CONSTRAINED_BASELINE },
+        { "avc-main", C2Config::PROFILE_AVC_MAIN },
+        { "avc-extended", C2Config::PROFILE_AVC_EXTENDED },
+        { "avc-high", C2Config::PROFILE_AVC_HIGH },
+        { "avc-progressive-high", C2Config::PROFILE_AVC_PROGRESSIVE_HIGH },
+        { "avc-constrained-high", C2Config::PROFILE_AVC_CONSTRAINED_HIGH },
+        { "avc-high-10", C2Config::PROFILE_AVC_HIGH_10 },
+        { "avc-progressive-high-10", C2Config::PROFILE_AVC_PROGRESSIVE_HIGH_10 },
+        { "avc-high-422", C2Config::PROFILE_AVC_HIGH_422 },
+        { "avc-high-444-predictive", C2Config::PROFILE_AVC_HIGH_444_PREDICTIVE },
+        { "avc-high-10-intra", C2Config::PROFILE_AVC_HIGH_10_INTRA },
+        { "avc-high-422-intra", C2Config::PROFILE_AVC_HIGH_422_INTRA },
+        { "avc-high-444-intra", C2Config::PROFILE_AVC_HIGH_444_INTRA },
+        { "avc-cavlc-444-intra", C2Config::PROFILE_AVC_CAVLC_444_INTRA },
+        { "avc-scalable-baseline", C2Config::PROFILE_AVC_SCALABLE_BASELINE },
+        { "avc-scalable-constrained-baseline", C2Config::PROFILE_AVC_SCALABLE_CONSTRAINED_BASELINE },
+        { "avc-scalable-high", C2Config::PROFILE_AVC_SCALABLE_HIGH },
+        { "avc-scalable-constrained-high", C2Config::PROFILE_AVC_SCALABLE_CONSTRAINED_HIGH },
+        { "avc-scalable-high-intra", C2Config::PROFILE_AVC_SCALABLE_HIGH_INTRA },
+        { "avc-multiview-high", C2Config::PROFILE_AVC_MULTIVIEW_HIGH },
+        { "avc-stereo-high", C2Config::PROFILE_AVC_STEREO_HIGH },
+        { "avc-mfc-high", C2Config::PROFILE_AVC_MFC_HIGH },
+        { "avc-multiview-depth-high", C2Config::PROFILE_AVC_MULTIVIEW_DEPTH_HIGH },
+        { "avc-mfc-depth-high", C2Config::PROFILE_AVC_MFC_DEPTH_HIGH },
+        { "avc-enhanced-multiview-depth-high", C2Config::PROFILE_AVC_ENHANCED_MULTIVIEW_DEPTH_HIGH },
+        { "hevc-main", C2Config::PROFILE_HEVC_MAIN },
+        { "hevc-main-10", C2Config::PROFILE_HEVC_MAIN_10 },
+        { "hevc-main-still", C2Config::PROFILE_HEVC_MAIN_STILL },
+        { "hevc-mono", C2Config::PROFILE_HEVC_MONO },
+        { "hevc-mono-12", C2Config::PROFILE_HEVC_MONO_12 },
+        { "hevc-mono-16", C2Config::PROFILE_HEVC_MONO_16 },
+        { "hevc-main-12", C2Config::PROFILE_HEVC_MAIN_12 },
+        { "hevc-main-422-10", C2Config::PROFILE_HEVC_MAIN_422_10 },
+        { "hevc-main-422-12", C2Config::PROFILE_HEVC_MAIN_422_12 },
+        { "hevc-main-444", C2Config::PROFILE_HEVC_MAIN_444 },
+        { "hevc-main-444-10", C2Config::PROFILE_HEVC_MAIN_444_10 },
+        { "hevc-main-444-12", C2Config::PROFILE_HEVC_MAIN_444_12 },
+        { "hevc-main-intra", C2Config::PROFILE_HEVC_MAIN_INTRA },
+        { "hevc-main-10-intra", C2Config::PROFILE_HEVC_MAIN_10_INTRA },
+        { "hevc-main-12-intra", C2Config::PROFILE_HEVC_MAIN_12_INTRA },
+        { "hevc-main-422-10-intra", C2Config::PROFILE_HEVC_MAIN_422_10_INTRA },
+        { "hevc-main-422-12-intra", C2Config::PROFILE_HEVC_MAIN_422_12_INTRA },
+        { "hevc-main-444-intra", C2Config::PROFILE_HEVC_MAIN_444_INTRA },
+        { "hevc-main-444-10-intra", C2Config::PROFILE_HEVC_MAIN_444_10_INTRA },
+        { "hevc-main-444-12-intra", C2Config::PROFILE_HEVC_MAIN_444_12_INTRA },
+        { "hevc-main-444-16-intra", C2Config::PROFILE_HEVC_MAIN_444_16_INTRA },
+        { "hevc-main-444-still", C2Config::PROFILE_HEVC_MAIN_444_STILL },
+        { "hevc-main-444-16-still", C2Config::PROFILE_HEVC_MAIN_444_16_STILL },
+        { "hevc-high-444", C2Config::PROFILE_HEVC_HIGH_444 },
+        { "hevc-high-444-10", C2Config::PROFILE_HEVC_HIGH_444_10 },
+        { "hevc-high-444-14", C2Config::PROFILE_HEVC_HIGH_444_14 },
+        { "hevc-high-444-16-intra", C2Config::PROFILE_HEVC_HIGH_444_16_INTRA },
+        { "hevc-sx-main", C2Config::PROFILE_HEVC_SX_MAIN },
+        { "hevc-sx-main-10", C2Config::PROFILE_HEVC_SX_MAIN_10 },
+        { "hevc-sx-main-444", C2Config::PROFILE_HEVC_SX_MAIN_444 },
+        { "hevc-sx-main-444-10", C2Config::PROFILE_HEVC_SX_MAIN_444_10 },
+        { "hevc-sx-high-444", C2Config::PROFILE_HEVC_SX_HIGH_444 },
+        { "hevc-sx-high-444-10", C2Config::PROFILE_HEVC_SX_HIGH_444_10 },
+        { "hevc-sx-high-444-14", C2Config::PROFILE_HEVC_SX_HIGH_444_14 },
+        { "hevc-multiview-main", C2Config::PROFILE_HEVC_MULTIVIEW_MAIN },
+        { "hevc-scalable-main", C2Config::PROFILE_HEVC_SCALABLE_MAIN },
+        { "hevc-scalable-main-10", C2Config::PROFILE_HEVC_SCALABLE_MAIN_10 },
+        { "hevc-scalable-mono", C2Config::PROFILE_HEVC_SCALABLE_MONO },
+        { "hevc-scalable-mono-12", C2Config::PROFILE_HEVC_SCALABLE_MONO_12 },
+        { "hevc-scalable-mono-16", C2Config::PROFILE_HEVC_SCALABLE_MONO_16 },
+        { "hevc-scalable-main-444", C2Config::PROFILE_HEVC_SCALABLE_MAIN_444 },
+        { "hevc-3d-main", C2Config::PROFILE_HEVC_3D_MAIN },
+        { "vp9-0", C2Config::PROFILE_VP9_0 },
+        { "vp9-1", C2Config::PROFILE_VP9_1 },
+        { "vp9-2", C2Config::PROFILE_VP9_2 },
+        { "vp9-3", C2Config::PROFILE_VP9_3 },
+}))
+
+DEFINE_C2_ENUM_VALUE_CUSTOM_HELPER(C2Config::level_t, ({
+        { "unused", C2Config::LEVEL_UNUSED },
+        { "mp2v-low", C2Config::LEVEL_MP2V_LOW },
+        { "mp2v-main", C2Config::LEVEL_MP2V_MAIN },
+        { "mp2v-high-1440", C2Config::LEVEL_MP2V_HIGH_1440 },
+        { "mp2v-high", C2Config::LEVEL_MP2V_HIGH },
+        { "mp2v-highp", C2Config::LEVEL_MP2V_HIGHP },
+        { "h263-10", C2Config::LEVEL_H263_10 },
+        { "h263-20", C2Config::LEVEL_H263_20 },
+        { "h263-30", C2Config::LEVEL_H263_30 },
+        { "h263-40", C2Config::LEVEL_H263_40 },
+        { "h263-45", C2Config::LEVEL_H263_45 },
+        { "h263-50", C2Config::LEVEL_H263_50 },
+        { "h263-60", C2Config::LEVEL_H263_60 },
+        { "h263-70", C2Config::LEVEL_H263_70 },
+        { "mp4v-0", C2Config::LEVEL_MP4V_0 },
+        { "mp4v-0b", C2Config::LEVEL_MP4V_0B },
+        { "mp4v-1", C2Config::LEVEL_MP4V_1 },
+        { "mp4v-2", C2Config::LEVEL_MP4V_2 },
+        { "mp4v-3", C2Config::LEVEL_MP4V_3 },
+        { "mp4v-3b", C2Config::LEVEL_MP4V_3B },
+        { "mp4v-4", C2Config::LEVEL_MP4V_4 },
+        { "mp4v-4a", C2Config::LEVEL_MP4V_4A },
+        { "mp4v-5", C2Config::LEVEL_MP4V_5 },
+        { "mp4v-6", C2Config::LEVEL_MP4V_6 },
+        { "avc-1", C2Config::LEVEL_AVC_1 },
+        { "avc-1b", C2Config::LEVEL_AVC_1B },
+        { "avc-1.1", C2Config::LEVEL_AVC_1_1 },
+        { "avc-1.2", C2Config::LEVEL_AVC_1_2 },
+        { "avc-1.3", C2Config::LEVEL_AVC_1_3 },
+        { "avc-2", C2Config::LEVEL_AVC_2 },
+        { "avc-2.1", C2Config::LEVEL_AVC_2_1 },
+        { "avc-2.2", C2Config::LEVEL_AVC_2_2 },
+        { "avc-3", C2Config::LEVEL_AVC_3 },
+        { "avc-3.1", C2Config::LEVEL_AVC_3_1 },
+        { "avc-3.2", C2Config::LEVEL_AVC_3_2 },
+        { "avc-4", C2Config::LEVEL_AVC_4 },
+        { "avc-4.1", C2Config::LEVEL_AVC_4_1 },
+        { "avc-4.2", C2Config::LEVEL_AVC_4_2 },
+        { "avc-5", C2Config::LEVEL_AVC_5 },
+        { "avc-5.1", C2Config::LEVEL_AVC_5_1 },
+        { "avc-5.2", C2Config::LEVEL_AVC_5_2 },
+        { "hevc-main-1", C2Config::LEVEL_HEVC_MAIN_1 },
+        { "hevc-main-2", C2Config::LEVEL_HEVC_MAIN_2 },
+        { "hevc-main-2.1", C2Config::LEVEL_HEVC_MAIN_2_1 },
+        { "hevc-main-3", C2Config::LEVEL_HEVC_MAIN_3 },
+        { "hevc-main-3.1", C2Config::LEVEL_HEVC_MAIN_3_1 },
+        { "hevc-main-4", C2Config::LEVEL_HEVC_MAIN_4 },
+        { "hevc-main-4.1", C2Config::LEVEL_HEVC_MAIN_4_1 },
+        { "hevc-main-5", C2Config::LEVEL_HEVC_MAIN_5 },
+        { "hevc-main-5.1", C2Config::LEVEL_HEVC_MAIN_5_1 },
+        { "hevc-main-5.2", C2Config::LEVEL_HEVC_MAIN_5_2 },
+        { "hevc-main-6", C2Config::LEVEL_HEVC_MAIN_6 },
+        { "hevc-main-6.1", C2Config::LEVEL_HEVC_MAIN_6_1 },
+        { "hevc-main-6.2", C2Config::LEVEL_HEVC_MAIN_6_2 },
+        { "hevc-high-4", C2Config::LEVEL_HEVC_HIGH_4 },
+        { "hevc-high-4.1", C2Config::LEVEL_HEVC_HIGH_4_1 },
+        { "hevc-high-5", C2Config::LEVEL_HEVC_HIGH_5 },
+        { "hevc-high-5.1", C2Config::LEVEL_HEVC_HIGH_5_1 },
+        { "hevc-high-5.2", C2Config::LEVEL_HEVC_HIGH_5_2 },
+        { "hevc-high-6", C2Config::LEVEL_HEVC_HIGH_6 },
+        { "hevc-high-6.1", C2Config::LEVEL_HEVC_HIGH_6_1 },
+        { "hevc-high-6.2", C2Config::LEVEL_HEVC_HIGH_6_2 },
+        { "vp9-1", C2Config::LEVEL_VP9_1 },
+        { "vp9-1.1", C2Config::LEVEL_VP9_1_1 },
+        { "vp9-2", C2Config::LEVEL_VP9_2 },
+        { "vp9-2.1", C2Config::LEVEL_VP9_2_1 },
+        { "vp9-3", C2Config::LEVEL_VP9_3 },
+        { "vp9-3.1", C2Config::LEVEL_VP9_3_1 },
+        { "vp9-4", C2Config::LEVEL_VP9_4 },
+        { "vp9-4.1", C2Config::LEVEL_VP9_4_1 },
+        { "vp9-5", C2Config::LEVEL_VP9_5 },
+        { "vp9-5.1", C2Config::LEVEL_VP9_5_1 },
+        { "vp9-5.2", C2Config::LEVEL_VP9_5_2 },
+        { "vp9-6", C2Config::LEVEL_VP9_6 },
+        { "vp9-6.1", C2Config::LEVEL_VP9_6_1 },
+        { "vp9-6.2", C2Config::LEVEL_VP9_6_2 },
+}))
+
+DEFINE_C2_ENUM_VALUE_CUSTOM_HELPER(C2BufferData::type_t, ({
+        { "invalid",        C2BufferData::INVALID },
+        { "linear",         C2BufferData::LINEAR },
+        { "linear-chunks",  C2BufferData::LINEAR_CHUNKS },
+        { "graphic",        C2BufferData::GRAPHIC },
+        { "graphic-chunks", C2BufferData::GRAPHIC_CHUNKS },
+}))
+
+
diff --git a/media/codec2/vndk/C2PlatformStorePluginLoader.cpp b/media/codec2/vndk/C2PlatformStorePluginLoader.cpp
new file mode 100644
index 0000000..7a460f4
--- /dev/null
+++ b/media/codec2/vndk/C2PlatformStorePluginLoader.cpp
@@ -0,0 +1,95 @@
+/*
+ * Copyright 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_NDEBUG 0
+#define LOG_TAG "C2PlatformStorePluginLoader"
+
+#include <dlfcn.h>
+
+#include <utils/Log.h>
+
+#include "C2PlatformStorePluginLoader.h"
+
+/* static */ android::Mutex C2PlatformStorePluginLoader::sMutex;
+/* static */ std::unique_ptr<C2PlatformStorePluginLoader> C2PlatformStorePluginLoader::sInstance;
+
+C2PlatformStorePluginLoader::C2PlatformStorePluginLoader(const char *libPath)
+    : mCreateBlockPool(nullptr) {
+    mLibHandle = dlopen(libPath, RTLD_NOW | RTLD_NODELETE);
+    if (mLibHandle == nullptr) {
+        ALOGD("Failed to load library: %s (%s)", libPath, dlerror());
+        return;
+    }
+    mCreateBlockPool = (CreateBlockPoolFunc)dlsym(mLibHandle, "CreateBlockPool");
+    if (mCreateBlockPool == nullptr) {
+        ALOGD("Failed to find symbol: CreateBlockPool (%s)", dlerror());
+    }
+    mCreateAllocator = (CreateAllocatorFunc)dlsym(mLibHandle, "CreateAllocator");
+    if (mCreateAllocator == nullptr) {
+        ALOGD("Failed to find symbol: CreateAllocator (%s)", dlerror());
+    }
+}
+
+C2PlatformStorePluginLoader::~C2PlatformStorePluginLoader() {
+    if (mLibHandle != nullptr) {
+        ALOGV("Closing handle");
+        dlclose(mLibHandle);
+    }
+}
+
+c2_status_t C2PlatformStorePluginLoader::createBlockPool(
+        ::C2Allocator::id_t allocatorId, ::C2BlockPool::local_id_t blockPoolId,
+        std::shared_ptr<C2BlockPool>* pool) {
+    if (mCreateBlockPool == nullptr) {
+        ALOGD("Handle or CreateBlockPool symbol is null");
+        return C2_NOT_FOUND;
+    }
+
+    std::shared_ptr<::C2BlockPool> ptr(mCreateBlockPool(allocatorId, blockPoolId));
+    if (ptr) {
+        *pool = ptr;
+        return C2_OK;
+    }
+    ALOGD("Failed to CreateBlockPool by allocator id: %u", allocatorId);
+    return C2_BAD_INDEX;
+}
+
+c2_status_t C2PlatformStorePluginLoader::createAllocator(
+        ::C2Allocator::id_t allocatorId, std::shared_ptr<C2Allocator>* const allocator) {
+    if (mCreateAllocator == nullptr) {
+        ALOGD("Handle or CreateAllocator symbol is null");
+        return C2_NOT_FOUND;
+    }
+
+    c2_status_t res = C2_CORRUPTED;
+    allocator->reset(mCreateAllocator(allocatorId, &res));
+    if (res != C2_OK) {
+        ALOGD("Failed to CreateAllocator by id: %u, res: %d", allocatorId, res);
+        allocator->reset();
+        return res;
+    }
+    return C2_OK;
+}
+
+// static
+const std::unique_ptr<C2PlatformStorePluginLoader>& C2PlatformStorePluginLoader::GetInstance() {
+    android::Mutex::Autolock _l(sMutex);
+    if (!sInstance) {
+        ALOGV("Loading library");
+        sInstance.reset(new C2PlatformStorePluginLoader("libstagefright_ccodec_ext.so"));
+    }
+    return sInstance;
+}
diff --git a/media/codec2/vndk/C2Store.cpp b/media/codec2/vndk/C2Store.cpp
new file mode 100644
index 0000000..33019ed
--- /dev/null
+++ b/media/codec2/vndk/C2Store.cpp
@@ -0,0 +1,974 @@
+/*
+ * Copyright (C) 2017 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 "C2Store"
+#define LOG_NDEBUG 0
+#include <utils/Log.h>
+
+#include <C2AllocatorGralloc.h>
+#include <C2AllocatorIon.h>
+#include <C2BufferPriv.h>
+#include <C2BqBufferPriv.h>
+#include <C2Component.h>
+#include <C2Config.h>
+#include <C2PlatformStorePluginLoader.h>
+#include <C2PlatformSupport.h>
+#include <util/C2InterfaceHelper.h>
+
+#include <dlfcn.h>
+#include <unistd.h> // getpagesize
+
+#include <map>
+#include <memory>
+#include <mutex>
+
+namespace android {
+
+/**
+ * Returns the preferred component store in this process to access its interface.
+ */
+std::shared_ptr<C2ComponentStore> GetPreferredCodec2ComponentStore();
+
+/**
+ * The platform allocator store provides basic allocator-types for the framework based on ion and
+ * gralloc. Allocators are not meant to be updatable.
+ *
+ * \todo Provide allocator based on ashmem
+ * \todo Move ion allocation into its HIDL or provide some mapping from memory usage to ion flags
+ * \todo Make this allocator store extendable
+ */
+class C2PlatformAllocatorStoreImpl : public C2PlatformAllocatorStore {
+public:
+    C2PlatformAllocatorStoreImpl();
+
+    virtual c2_status_t fetchAllocator(
+            id_t id, std::shared_ptr<C2Allocator> *const allocator) override;
+
+    virtual std::vector<std::shared_ptr<const C2Allocator::Traits>> listAllocators_nb()
+            const override {
+        return std::vector<std::shared_ptr<const C2Allocator::Traits>>(); /// \todo
+    }
+
+    virtual C2String getName() const override {
+        return "android.allocator-store";
+    }
+
+    void setComponentStore(std::shared_ptr<C2ComponentStore> store);
+
+    ~C2PlatformAllocatorStoreImpl() override = default;
+
+private:
+    /// returns a shared-singleton ion allocator
+    std::shared_ptr<C2Allocator> fetchIonAllocator();
+
+    /// returns a shared-singleton gralloc allocator
+    std::shared_ptr<C2Allocator> fetchGrallocAllocator();
+
+    /// returns a shared-singleton bufferqueue supporting gralloc allocator
+    std::shared_ptr<C2Allocator> fetchBufferQueueAllocator();
+
+    /// component store to use
+    std::mutex _mComponentStoreSetLock; // protects the entire updating _mComponentStore and its
+                                        // dependencies
+    std::mutex _mComponentStoreReadLock; // must protect only read/write of _mComponentStore
+    std::shared_ptr<C2ComponentStore> _mComponentStore;
+};
+
+C2PlatformAllocatorStoreImpl::C2PlatformAllocatorStoreImpl() {
+}
+
+c2_status_t C2PlatformAllocatorStoreImpl::fetchAllocator(
+        id_t id, std::shared_ptr<C2Allocator> *const allocator) {
+    allocator->reset();
+    switch (id) {
+    // TODO: should we implement a generic registry for all, and use that?
+    case C2PlatformAllocatorStore::ION:
+    case C2AllocatorStore::DEFAULT_LINEAR:
+        *allocator = fetchIonAllocator();
+        break;
+
+    case C2PlatformAllocatorStore::GRALLOC:
+    case C2AllocatorStore::DEFAULT_GRAPHIC:
+        *allocator = fetchGrallocAllocator();
+        break;
+
+    case C2PlatformAllocatorStore::BUFFERQUEUE:
+        *allocator = fetchBufferQueueAllocator();
+        break;
+
+    default:
+        // Try to create allocator from platform store plugins.
+        c2_status_t res =
+                C2PlatformStorePluginLoader::GetInstance()->createAllocator(id, allocator);
+        if (res != C2_OK) {
+            return res;
+        }
+        break;
+    }
+    if (*allocator == nullptr) {
+        return C2_NO_MEMORY;
+    }
+    return C2_OK;
+}
+
+namespace {
+
+std::mutex gIonAllocatorMutex;
+std::weak_ptr<C2AllocatorIon> gIonAllocator;
+
+void UseComponentStoreForIonAllocator(
+        const std::shared_ptr<C2AllocatorIon> allocator,
+        std::shared_ptr<C2ComponentStore> store) {
+    C2AllocatorIon::UsageMapperFn mapper;
+    uint64_t minUsage = 0;
+    uint64_t maxUsage = C2MemoryUsage(C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE).expected;
+    size_t blockSize = getpagesize();
+
+    // query min and max usage as well as block size via supported values
+    C2StoreIonUsageInfo usageInfo;
+    std::vector<C2FieldSupportedValuesQuery> query = {
+        C2FieldSupportedValuesQuery::Possible(C2ParamField::Make(usageInfo, usageInfo.usage)),
+        C2FieldSupportedValuesQuery::Possible(C2ParamField::Make(usageInfo, usageInfo.capacity)),
+    };
+    c2_status_t res = store->querySupportedValues_sm(query);
+    if (res == C2_OK) {
+        if (query[0].status == C2_OK) {
+            const C2FieldSupportedValues &fsv = query[0].values;
+            if (fsv.type == C2FieldSupportedValues::FLAGS && !fsv.values.empty()) {
+                minUsage = fsv.values[0].u64;
+                maxUsage = 0;
+                for (C2Value::Primitive v : fsv.values) {
+                    maxUsage |= v.u64;
+                }
+            }
+        }
+        if (query[1].status == C2_OK) {
+            const C2FieldSupportedValues &fsv = query[1].values;
+            if (fsv.type == C2FieldSupportedValues::RANGE && fsv.range.step.u32 > 0) {
+                blockSize = fsv.range.step.u32;
+            }
+        }
+
+        mapper = [store](C2MemoryUsage usage, size_t capacity,
+                         size_t *align, unsigned *heapMask, unsigned *flags) -> c2_status_t {
+            if (capacity > UINT32_MAX) {
+                return C2_BAD_VALUE;
+            }
+            C2StoreIonUsageInfo usageInfo = { usage.expected, capacity };
+            std::vector<std::unique_ptr<C2SettingResult>> failures; // TODO: remove
+            c2_status_t res = store->config_sm({&usageInfo}, &failures);
+            if (res == C2_OK) {
+                *align = usageInfo.minAlignment;
+                *heapMask = usageInfo.heapMask;
+                *flags = usageInfo.allocFlags;
+            }
+            return res;
+        };
+    }
+
+    allocator->setUsageMapper(mapper, minUsage, maxUsage, blockSize);
+}
+
+}
+
+void C2PlatformAllocatorStoreImpl::setComponentStore(std::shared_ptr<C2ComponentStore> store) {
+    // technically this set lock is not needed, but is here for safety in case we add more
+    // getter orders
+    std::lock_guard<std::mutex> lock(_mComponentStoreSetLock);
+    {
+        std::lock_guard<std::mutex> lock(_mComponentStoreReadLock);
+        _mComponentStore = store;
+    }
+    std::shared_ptr<C2AllocatorIon> allocator;
+    {
+        std::lock_guard<std::mutex> lock(gIonAllocatorMutex);
+        allocator = gIonAllocator.lock();
+    }
+    if (allocator) {
+        UseComponentStoreForIonAllocator(allocator, store);
+    }
+}
+
+std::shared_ptr<C2Allocator> C2PlatformAllocatorStoreImpl::fetchIonAllocator() {
+    std::lock_guard<std::mutex> lock(gIonAllocatorMutex);
+    std::shared_ptr<C2AllocatorIon> allocator = gIonAllocator.lock();
+    if (allocator == nullptr) {
+        std::shared_ptr<C2ComponentStore> componentStore;
+        {
+            std::lock_guard<std::mutex> lock(_mComponentStoreReadLock);
+            componentStore = _mComponentStore;
+        }
+        allocator = std::make_shared<C2AllocatorIon>(C2PlatformAllocatorStore::ION);
+        UseComponentStoreForIonAllocator(allocator, componentStore);
+        gIonAllocator = allocator;
+    }
+    return allocator;
+}
+
+std::shared_ptr<C2Allocator> C2PlatformAllocatorStoreImpl::fetchGrallocAllocator() {
+    static std::mutex mutex;
+    static std::weak_ptr<C2Allocator> grallocAllocator;
+    std::lock_guard<std::mutex> lock(mutex);
+    std::shared_ptr<C2Allocator> allocator = grallocAllocator.lock();
+    if (allocator == nullptr) {
+        allocator = std::make_shared<C2AllocatorGralloc>(C2PlatformAllocatorStore::GRALLOC);
+        grallocAllocator = allocator;
+    }
+    return allocator;
+}
+
+std::shared_ptr<C2Allocator> C2PlatformAllocatorStoreImpl::fetchBufferQueueAllocator() {
+    static std::mutex mutex;
+    static std::weak_ptr<C2Allocator> grallocAllocator;
+    std::lock_guard<std::mutex> lock(mutex);
+    std::shared_ptr<C2Allocator> allocator = grallocAllocator.lock();
+    if (allocator == nullptr) {
+        allocator = std::make_shared<C2AllocatorGralloc>(
+                C2PlatformAllocatorStore::BUFFERQUEUE, true);
+        grallocAllocator = allocator;
+    }
+    return allocator;
+}
+
+namespace {
+    std::mutex gPreferredComponentStoreMutex;
+    std::shared_ptr<C2ComponentStore> gPreferredComponentStore;
+
+    std::mutex gPlatformAllocatorStoreMutex;
+    std::weak_ptr<C2PlatformAllocatorStoreImpl> gPlatformAllocatorStore;
+}
+
+std::shared_ptr<C2AllocatorStore> GetCodec2PlatformAllocatorStore() {
+    std::lock_guard<std::mutex> lock(gPlatformAllocatorStoreMutex);
+    std::shared_ptr<C2PlatformAllocatorStoreImpl> store = gPlatformAllocatorStore.lock();
+    if (store == nullptr) {
+        store = std::make_shared<C2PlatformAllocatorStoreImpl>();
+        store->setComponentStore(GetPreferredCodec2ComponentStore());
+        gPlatformAllocatorStore = store;
+    }
+    return store;
+}
+
+void SetPreferredCodec2ComponentStore(std::shared_ptr<C2ComponentStore> componentStore) {
+    static std::mutex mutex;
+    std::lock_guard<std::mutex> lock(mutex); // don't interleve set-s
+
+    // update preferred store
+    {
+        std::lock_guard<std::mutex> lock(gPreferredComponentStoreMutex);
+        gPreferredComponentStore = componentStore;
+    }
+
+    // update platform allocator's store as well if it is alive
+    std::shared_ptr<C2PlatformAllocatorStoreImpl> allocatorStore;
+    {
+        std::lock_guard<std::mutex> lock(gPlatformAllocatorStoreMutex);
+        allocatorStore = gPlatformAllocatorStore.lock();
+    }
+    if (allocatorStore) {
+        allocatorStore->setComponentStore(componentStore);
+    }
+}
+
+std::shared_ptr<C2ComponentStore> GetPreferredCodec2ComponentStore() {
+    std::lock_guard<std::mutex> lock(gPreferredComponentStoreMutex);
+    return gPreferredComponentStore ? gPreferredComponentStore : GetCodec2PlatformComponentStore();
+}
+
+namespace {
+
+class _C2BlockPoolCache {
+public:
+    _C2BlockPoolCache() : mBlockPoolSeqId(C2BlockPool::PLATFORM_START + 1) {}
+
+    c2_status_t _createBlockPool(
+            C2PlatformAllocatorStore::id_t allocatorId,
+            std::shared_ptr<const C2Component> component,
+            C2BlockPool::local_id_t poolId,
+            std::shared_ptr<C2BlockPool> *pool) {
+        std::shared_ptr<C2AllocatorStore> allocatorStore =
+                GetCodec2PlatformAllocatorStore();
+        std::shared_ptr<C2Allocator> allocator;
+        c2_status_t res = C2_NOT_FOUND;
+
+        switch(allocatorId) {
+            case C2PlatformAllocatorStore::ION:
+            case C2AllocatorStore::DEFAULT_LINEAR:
+                res = allocatorStore->fetchAllocator(
+                        C2AllocatorStore::DEFAULT_LINEAR, &allocator);
+                if (res == C2_OK) {
+                    std::shared_ptr<C2BlockPool> ptr =
+                            std::make_shared<C2PooledBlockPool>(
+                                    allocator, poolId);
+                    *pool = ptr;
+                    mBlockPools[poolId] = ptr;
+                    mComponents[poolId] = component;
+                }
+                break;
+            case C2PlatformAllocatorStore::GRALLOC:
+            case C2AllocatorStore::DEFAULT_GRAPHIC:
+                res = allocatorStore->fetchAllocator(
+                        C2AllocatorStore::DEFAULT_GRAPHIC, &allocator);
+                if (res == C2_OK) {
+                    std::shared_ptr<C2BlockPool> ptr =
+                        std::make_shared<C2PooledBlockPool>(allocator, poolId);
+                    *pool = ptr;
+                    mBlockPools[poolId] = ptr;
+                    mComponents[poolId] = component;
+                }
+                break;
+            case C2PlatformAllocatorStore::BUFFERQUEUE:
+                res = allocatorStore->fetchAllocator(
+                        C2PlatformAllocatorStore::BUFFERQUEUE, &allocator);
+                if (res == C2_OK) {
+                    std::shared_ptr<C2BlockPool> ptr =
+                            std::make_shared<C2BufferQueueBlockPool>(
+                                    allocator, poolId);
+                    *pool = ptr;
+                    mBlockPools[poolId] = ptr;
+                    mComponents[poolId] = component;
+                }
+                break;
+            default:
+                // Try to create block pool from platform store plugins.
+                std::shared_ptr<C2BlockPool> ptr;
+                res = C2PlatformStorePluginLoader::GetInstance()->createBlockPool(
+                        allocatorId, poolId, &ptr);
+                if (res == C2_OK) {
+                    *pool = ptr;
+                    mBlockPools[poolId] = ptr;
+                    mComponents[poolId] = component;
+                }
+                break;
+        }
+        return res;
+    }
+
+    c2_status_t createBlockPool(
+            C2PlatformAllocatorStore::id_t allocatorId,
+            std::shared_ptr<const C2Component> component,
+            std::shared_ptr<C2BlockPool> *pool) {
+        return _createBlockPool(allocatorId, component, mBlockPoolSeqId++, pool);
+    }
+
+    bool getBlockPool(
+            C2BlockPool::local_id_t blockPoolId,
+            std::shared_ptr<const C2Component> component,
+            std::shared_ptr<C2BlockPool> *pool) {
+        // TODO: use one iterator for multiple blockpool type scalability.
+        std::shared_ptr<C2BlockPool> ptr;
+        auto it = mBlockPools.find(blockPoolId);
+        if (it != mBlockPools.end()) {
+            ptr = it->second.lock();
+            if (!ptr) {
+                mBlockPools.erase(it);
+                mComponents.erase(blockPoolId);
+            } else {
+                auto found = mComponents.find(blockPoolId);
+                if (component == found->second.lock()) {
+                    *pool = ptr;
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+private:
+    C2BlockPool::local_id_t mBlockPoolSeqId;
+
+    std::map<C2BlockPool::local_id_t, std::weak_ptr<C2BlockPool>> mBlockPools;
+    std::map<C2BlockPool::local_id_t, std::weak_ptr<const C2Component>> mComponents;
+};
+
+static std::unique_ptr<_C2BlockPoolCache> sBlockPoolCache =
+    std::make_unique<_C2BlockPoolCache>();
+static std::mutex sBlockPoolCacheMutex;
+
+} // anynymous namespace
+
+c2_status_t GetCodec2BlockPool(
+        C2BlockPool::local_id_t id, std::shared_ptr<const C2Component> component,
+        std::shared_ptr<C2BlockPool> *pool) {
+    pool->reset();
+    std::lock_guard<std::mutex> lock(sBlockPoolCacheMutex);
+    std::shared_ptr<C2AllocatorStore> allocatorStore = GetCodec2PlatformAllocatorStore();
+    std::shared_ptr<C2Allocator> allocator;
+    c2_status_t res = C2_NOT_FOUND;
+
+    if (id >= C2BlockPool::PLATFORM_START) {
+        if (sBlockPoolCache->getBlockPool(id, component, pool)) {
+            return C2_OK;
+        }
+    }
+
+    switch (id) {
+    case C2BlockPool::BASIC_LINEAR:
+        res = allocatorStore->fetchAllocator(C2AllocatorStore::DEFAULT_LINEAR, &allocator);
+        if (res == C2_OK) {
+            *pool = std::make_shared<C2BasicLinearBlockPool>(allocator);
+        }
+        break;
+    case C2BlockPool::BASIC_GRAPHIC:
+        res = allocatorStore->fetchAllocator(C2AllocatorStore::DEFAULT_GRAPHIC, &allocator);
+        if (res == C2_OK) {
+            *pool = std::make_shared<C2BasicGraphicBlockPool>(allocator);
+        }
+        break;
+    // TODO: remove this. this is temporary
+    case C2BlockPool::PLATFORM_START:
+        res = sBlockPoolCache->_createBlockPool(
+                C2PlatformAllocatorStore::BUFFERQUEUE, component, id, pool);
+        break;
+    default:
+        break;
+    }
+    return res;
+}
+
+c2_status_t CreateCodec2BlockPool(
+        C2PlatformAllocatorStore::id_t allocatorId,
+        std::shared_ptr<const C2Component> component,
+        std::shared_ptr<C2BlockPool> *pool) {
+    pool->reset();
+
+    std::lock_guard<std::mutex> lock(sBlockPoolCacheMutex);
+    return sBlockPoolCache->createBlockPool(allocatorId, component, pool);
+}
+
+class C2PlatformComponentStore : public C2ComponentStore {
+public:
+    virtual std::vector<std::shared_ptr<const C2Component::Traits>> listComponents() override;
+    virtual std::shared_ptr<C2ParamReflector> getParamReflector() const override;
+    virtual C2String getName() const override;
+    virtual c2_status_t querySupportedValues_sm(
+            std::vector<C2FieldSupportedValuesQuery> &fields) const override;
+    virtual c2_status_t querySupportedParams_nb(
+            std::vector<std::shared_ptr<C2ParamDescriptor>> *const params) const override;
+    virtual c2_status_t query_sm(
+            const std::vector<C2Param*> &stackParams,
+            const std::vector<C2Param::Index> &heapParamIndices,
+            std::vector<std::unique_ptr<C2Param>> *const heapParams) const override;
+    virtual c2_status_t createInterface(
+            C2String name, std::shared_ptr<C2ComponentInterface> *const interface) override;
+    virtual c2_status_t createComponent(
+            C2String name, std::shared_ptr<C2Component> *const component) override;
+    virtual c2_status_t copyBuffer(
+            std::shared_ptr<C2GraphicBuffer> src, std::shared_ptr<C2GraphicBuffer> dst) override;
+    virtual c2_status_t config_sm(
+            const std::vector<C2Param*> &params,
+            std::vector<std::unique_ptr<C2SettingResult>> *const failures) override;
+    C2PlatformComponentStore();
+
+    virtual ~C2PlatformComponentStore() override = default;
+
+private:
+
+    /**
+     * An object encapsulating a loaded component module.
+     *
+     * \todo provide a way to add traits to known components here to avoid loading the .so-s
+     * for listComponents
+     */
+    struct ComponentModule : public C2ComponentFactory,
+            public std::enable_shared_from_this<ComponentModule> {
+        virtual c2_status_t createComponent(
+                c2_node_id_t id, std::shared_ptr<C2Component> *component,
+                ComponentDeleter deleter = std::default_delete<C2Component>()) override;
+        virtual c2_status_t createInterface(
+                c2_node_id_t id, std::shared_ptr<C2ComponentInterface> *interface,
+                InterfaceDeleter deleter = std::default_delete<C2ComponentInterface>()) override;
+
+        /**
+         * \returns the traits of the component in this module.
+         */
+        std::shared_ptr<const C2Component::Traits> getTraits();
+
+        /**
+         * Creates an uninitialized component module.
+         *
+         * \param name[in]  component name.
+         *
+         * \note Only used by ComponentLoader.
+         */
+        ComponentModule()
+            : mInit(C2_NO_INIT),
+              mLibHandle(nullptr),
+              createFactory(nullptr),
+              destroyFactory(nullptr),
+              mComponentFactory(nullptr) {
+        }
+
+        /**
+         * Initializes a component module with a given library path. Must be called exactly once.
+         *
+         * \note Only used by ComponentLoader.
+         *
+         * \param alias[in]   module alias
+         * \param libPath[in] library path
+         *
+         * \retval C2_OK        the component module has been successfully loaded
+         * \retval C2_NO_MEMORY not enough memory to loading the component module
+         * \retval C2_NOT_FOUND could not locate the component module
+         * \retval C2_CORRUPTED the component module could not be loaded (unexpected)
+         * \retval C2_REFUSED   permission denied to load the component module (unexpected)
+         * \retval C2_TIMED_OUT could not load the module within the time limit (unexpected)
+         */
+        c2_status_t init(std::string alias, std::string libPath);
+
+        virtual ~ComponentModule() override;
+
+    protected:
+        std::recursive_mutex mLock; ///< lock protecting mTraits
+        std::shared_ptr<C2Component::Traits> mTraits; ///< cached component traits
+
+        c2_status_t mInit; ///< initialization result
+
+        void *mLibHandle; ///< loaded library handle
+        C2ComponentFactory::CreateCodec2FactoryFunc createFactory; ///< loaded create function
+        C2ComponentFactory::DestroyCodec2FactoryFunc destroyFactory; ///< loaded destroy function
+        C2ComponentFactory *mComponentFactory; ///< loaded/created component factory
+    };
+
+    /**
+     * An object encapsulating a loadable component module.
+     *
+     * \todo make this also work for enumerations
+     */
+    struct ComponentLoader {
+        /**
+         * Load the component module.
+         *
+         * This method simply returns the component module if it is already currently loaded, or
+         * attempts to load it if it is not.
+         *
+         * \param module[out] pointer to the shared pointer where the loaded module shall be stored.
+         *                    This will be nullptr on error.
+         *
+         * \retval C2_OK        the component module has been successfully loaded
+         * \retval C2_NO_MEMORY not enough memory to loading the component module
+         * \retval C2_NOT_FOUND could not locate the component module
+         * \retval C2_CORRUPTED the component module could not be loaded
+         * \retval C2_REFUSED   permission denied to load the component module
+         */
+        c2_status_t fetchModule(std::shared_ptr<ComponentModule> *module) {
+            c2_status_t res = C2_OK;
+            std::lock_guard<std::mutex> lock(mMutex);
+            std::shared_ptr<ComponentModule> localModule = mModule.lock();
+            if (localModule == nullptr) {
+                localModule = std::make_shared<ComponentModule>();
+                res = localModule->init(mAlias, mLibPath);
+                if (res == C2_OK) {
+                    mModule = localModule;
+                }
+            }
+            *module = localModule;
+            return res;
+        }
+
+        /**
+         * Creates a component loader for a specific library path (or name).
+         */
+        ComponentLoader(std::string alias, std::string libPath)
+            : mAlias(alias), mLibPath(libPath) {}
+
+    private:
+        std::mutex mMutex; ///< mutex guarding the module
+        std::weak_ptr<ComponentModule> mModule; ///< weak reference to the loaded module
+        std::string mAlias; ///< component alias
+        std::string mLibPath; ///< library path
+    };
+
+    struct Interface : public C2InterfaceHelper {
+        std::shared_ptr<C2StoreIonUsageInfo> mIonUsageInfo;
+
+        Interface(std::shared_ptr<C2ReflectorHelper> reflector)
+            : C2InterfaceHelper(reflector) {
+            setDerivedInstance(this);
+
+            struct Setter {
+                static C2R setIonUsage(bool /* mayBlock */, C2P<C2StoreIonUsageInfo> &me) {
+                    me.set().heapMask = ~0;
+                    me.set().allocFlags = 0;
+                    me.set().minAlignment = 0;
+                    return C2R::Ok();
+                }
+            };
+
+            addParameter(
+                DefineParam(mIonUsageInfo, "ion-usage")
+                .withDefault(new C2StoreIonUsageInfo())
+                .withFields({
+                    C2F(mIonUsageInfo, usage).flags({C2MemoryUsage::CPU_READ | C2MemoryUsage::CPU_WRITE}),
+                    C2F(mIonUsageInfo, capacity).inRange(0, UINT32_MAX, 1024),
+                    C2F(mIonUsageInfo, heapMask).any(),
+                    C2F(mIonUsageInfo, allocFlags).flags({}),
+                    C2F(mIonUsageInfo, minAlignment).equalTo(0)
+                })
+                .withSetter(Setter::setIonUsage)
+                .build());
+        }
+    };
+
+    /**
+     * Retrieves the component loader for a component.
+     *
+     * \return a non-ref-holding pointer to the component loader.
+     *
+     * \retval C2_OK        the component loader has been successfully retrieved
+     * \retval C2_NO_MEMORY not enough memory to locate the component loader
+     * \retval C2_NOT_FOUND could not locate the component to be loaded
+     * \retval C2_CORRUPTED the component loader could not be identified due to some modules being
+     *                      corrupted (this can happen if the name does not refer to an already
+     *                      identified component but some components could not be loaded due to
+     *                      bad library)
+     * \retval C2_REFUSED   permission denied to find the component loader for the named component
+     *                      (this can happen if the name does not refer to an already identified
+     *                      component but some components could not be loaded due to lack of
+     *                      permissions)
+     */
+    c2_status_t findComponent(C2String name, ComponentLoader **loader);
+
+    std::map<C2String, ComponentLoader> mComponents; ///< map of name -> components
+    std::vector<C2String> mComponentsList; ///< list of components
+    std::shared_ptr<C2ReflectorHelper> mReflector;
+    Interface mInterface;
+};
+
+c2_status_t C2PlatformComponentStore::ComponentModule::init(
+        std::string alias, std::string libPath) {
+    ALOGV("in %s", __func__);
+    ALOGV("loading dll");
+    mLibHandle = dlopen(libPath.c_str(), RTLD_NOW|RTLD_NODELETE);
+    if (mLibHandle == nullptr) {
+        // could be access/symbol or simply not being there
+        ALOGD("could not dlopen %s: %s", libPath.c_str(), dlerror());
+        mInit = C2_CORRUPTED;
+    } else {
+        createFactory =
+            (C2ComponentFactory::CreateCodec2FactoryFunc)dlsym(mLibHandle, "CreateCodec2Factory");
+        destroyFactory =
+            (C2ComponentFactory::DestroyCodec2FactoryFunc)dlsym(mLibHandle, "DestroyCodec2Factory");
+
+        mComponentFactory = createFactory();
+        if (mComponentFactory == nullptr) {
+            ALOGD("could not create factory in %s", libPath.c_str());
+            mInit = C2_NO_MEMORY;
+        } else {
+            mInit = C2_OK;
+        }
+    }
+    if (mInit != C2_OK) {
+        return mInit;
+    }
+
+    std::shared_ptr<C2ComponentInterface> intf;
+    c2_status_t res = createInterface(0, &intf);
+    if (res != C2_OK) {
+        ALOGD("failed to create interface: %d", res);
+        return mInit;
+    }
+
+    std::shared_ptr<C2Component::Traits> traits(new (std::nothrow) C2Component::Traits);
+    if (traits) {
+        if (alias != intf->getName()) {
+            ALOGV("%s is alias to %s", alias.c_str(), intf->getName().c_str());
+        }
+        traits->name = alias;
+        // TODO: get this from interface properly.
+        bool encoder = (traits->name.find("encoder") != std::string::npos);
+        uint32_t mediaTypeIndex = encoder ? C2PortMimeConfig::output::PARAM_TYPE
+                : C2PortMimeConfig::input::PARAM_TYPE;
+        std::vector<std::unique_ptr<C2Param>> params;
+        res = intf->query_vb({}, { mediaTypeIndex }, C2_MAY_BLOCK, &params);
+        if (res != C2_OK) {
+            ALOGD("failed to query interface: %d", res);
+            return mInit;
+        }
+        if (params.size() != 1u) {
+            ALOGD("failed to query interface: unexpected vector size: %zu", params.size());
+            return mInit;
+        }
+        C2PortMimeConfig *mediaTypeConfig = (C2PortMimeConfig *)(params[0].get());
+        if (mediaTypeConfig == nullptr) {
+            ALOGD("failed to query media type");
+            return mInit;
+        }
+        traits->mediaType = mediaTypeConfig->m.value;
+        // TODO: get this properly.
+        traits->rank = 0x200;
+
+        // TODO: define these values properly
+        bool decoder = (traits->name.find("decoder") != std::string::npos);
+        traits->kind =
+                decoder ? C2Component::KIND_DECODER :
+                encoder ? C2Component::KIND_ENCODER :
+                C2Component::KIND_OTHER;
+        if (strncmp(traits->mediaType.c_str(), "audio/", 6) == 0) {
+            traits->domain = C2Component::DOMAIN_AUDIO;
+        } else if (strncmp(traits->mediaType.c_str(), "video/", 6) == 0) {
+            traits->domain = C2Component::DOMAIN_VIDEO;
+        } else if (strncmp(traits->mediaType.c_str(), "image/", 6) == 0) {
+            traits->domain = C2Component::DOMAIN_IMAGE;
+        } else {
+            traits->domain = C2Component::DOMAIN_OTHER;
+        }
+    }
+    mTraits = traits;
+
+    return mInit;
+}
+
+C2PlatformComponentStore::ComponentModule::~ComponentModule() {
+    ALOGV("in %s", __func__);
+    if (destroyFactory && mComponentFactory) {
+        destroyFactory(mComponentFactory);
+    }
+    if (mLibHandle) {
+        ALOGV("unloading dll");
+        dlclose(mLibHandle);
+    }
+}
+
+c2_status_t C2PlatformComponentStore::ComponentModule::createInterface(
+        c2_node_id_t id, std::shared_ptr<C2ComponentInterface> *interface,
+        std::function<void(::C2ComponentInterface*)> deleter) {
+    interface->reset();
+    if (mInit != C2_OK) {
+        return mInit;
+    }
+    std::shared_ptr<ComponentModule> module = shared_from_this();
+    c2_status_t res = mComponentFactory->createInterface(
+            id, interface, [module, deleter](C2ComponentInterface *p) mutable {
+                // capture module so that we ensure we still have it while deleting interface
+                deleter(p); // delete interface first
+                module.reset(); // remove module ref (not technically needed)
+    });
+    return res;
+}
+
+c2_status_t C2PlatformComponentStore::ComponentModule::createComponent(
+        c2_node_id_t id, std::shared_ptr<C2Component> *component,
+        std::function<void(::C2Component*)> deleter) {
+    component->reset();
+    if (mInit != C2_OK) {
+        return mInit;
+    }
+    std::shared_ptr<ComponentModule> module = shared_from_this();
+    c2_status_t res = mComponentFactory->createComponent(
+            id, component, [module, deleter](C2Component *p) mutable {
+                // capture module so that we ensure we still have it while deleting component
+                deleter(p); // delete component first
+                module.reset(); // remove module ref (not technically needed)
+    });
+    return res;
+}
+
+std::shared_ptr<const C2Component::Traits> C2PlatformComponentStore::ComponentModule::getTraits() {
+    std::unique_lock<std::recursive_mutex> lock(mLock);
+    return mTraits;
+}
+
+C2PlatformComponentStore::C2PlatformComponentStore()
+    : mReflector(std::make_shared<C2ReflectorHelper>()),
+      mInterface(mReflector) {
+
+    auto emplace = [this](const char *alias, const char *libPath) {
+        // ComponentLoader is neither copiable nor movable, so it must be
+        // constructed in-place. Now ComponentLoader takes two arguments in
+        // constructor, so we need to use piecewise_construct to achieve this
+        // behavior.
+        mComponents.emplace(
+                std::piecewise_construct,
+                std::forward_as_tuple(alias),
+                std::forward_as_tuple(alias, libPath));
+        mComponentsList.emplace_back(alias);
+    };
+    // TODO: move this also into a .so so it can be updated
+    emplace("c2.android.avc.decoder", "libstagefright_soft_c2avcdec.so");
+    emplace("c2.android.avc.encoder", "libstagefright_soft_c2avcenc.so");
+    emplace("c2.android.aac.decoder", "libstagefright_soft_c2aacdec.so");
+    emplace("c2.android.aac.encoder", "libstagefright_soft_c2aacenc.so");
+    emplace("c2.android.amrnb.decoder", "libstagefright_soft_c2amrnbdec.so");
+    emplace("c2.android.amrnb.encoder", "libstagefright_soft_c2amrnbenc.so");
+    emplace("c2.android.amrwb.decoder", "libstagefright_soft_c2amrwbdec.so");
+    emplace("c2.android.amrwb.encoder", "libstagefright_soft_c2amrwbenc.so");
+    emplace("c2.android.hevc.decoder", "libstagefright_soft_c2hevcdec.so");
+    emplace("c2.android.g711.alaw.decoder", "libstagefright_soft_c2g711alawdec.so");
+    emplace("c2.android.g711.mlaw.decoder", "libstagefright_soft_c2g711mlawdec.so");
+    emplace("c2.android.mpeg2.decoder", "libstagefright_soft_c2mpeg2dec.so");
+    emplace("c2.android.h263.decoder", "libstagefright_soft_c2h263dec.so");
+    emplace("c2.android.h263.encoder", "libstagefright_soft_c2h263enc.so");
+    emplace("c2.android.mpeg4.decoder", "libstagefright_soft_c2mpeg4dec.so");
+    emplace("c2.android.mpeg4.encoder", "libstagefright_soft_c2mpeg4enc.so");
+    emplace("c2.android.mp3.decoder", "libstagefright_soft_c2mp3dec.so");
+    emplace("c2.android.vorbis.decoder", "libstagefright_soft_c2vorbisdec.so");
+    emplace("c2.android.opus.decoder", "libstagefright_soft_c2opusdec.so");
+    emplace("c2.android.vp8.decoder", "libstagefright_soft_c2vp8dec.so");
+    emplace("c2.android.vp9.decoder", "libstagefright_soft_c2vp9dec.so");
+    emplace("c2.android.vp8.encoder", "libstagefright_soft_c2vp8enc.so");
+    emplace("c2.android.vp9.encoder", "libstagefright_soft_c2vp9enc.so");
+    emplace("c2.android.raw.decoder", "libstagefright_soft_c2rawdec.so");
+    emplace("c2.android.flac.decoder", "libstagefright_soft_c2flacdec.so");
+    emplace("c2.android.flac.encoder", "libstagefright_soft_c2flacenc.so");
+    emplace("c2.android.gsm.decoder", "libstagefright_soft_c2gsmdec.so");
+    emplace("c2.android.xaac.decoder", "libstagefright_soft_c2xaacdec.so");
+
+    // "Aliases"
+    // TODO: use aliases proper from C2Component::Traits
+    emplace("OMX.google.h264.decoder", "libstagefright_soft_c2avcdec.so");
+    emplace("OMX.google.h264.encoder", "libstagefright_soft_c2avcenc.so");
+    emplace("OMX.google.aac.decoder", "libstagefright_soft_c2aacdec.so");
+    emplace("OMX.google.aac.encoder", "libstagefright_soft_c2aacenc.so");
+    emplace("OMX.google.amrnb.decoder", "libstagefright_soft_c2amrnbdec.so");
+    emplace("OMX.google.amrnb.encoder", "libstagefright_soft_c2amrnbenc.so");
+    emplace("OMX.google.amrwb.decoder", "libstagefright_soft_c2amrwbdec.so");
+    emplace("OMX.google.amrwb.encoder", "libstagefright_soft_c2amrwbenc.so");
+    emplace("OMX.google.hevc.decoder", "libstagefright_soft_c2hevcdec.so");
+    emplace("OMX.google.g711.alaw.decoder", "libstagefright_soft_c2g711alawdec.so");
+    emplace("OMX.google.g711.mlaw.decoder", "libstagefright_soft_c2g711mlawdec.so");
+    emplace("OMX.google.mpeg2.decoder", "libstagefright_soft_c2mpeg2dec.so");
+    emplace("OMX.google.h263.decoder", "libstagefright_soft_c2h263dec.so");
+    emplace("OMX.google.h263.encoder", "libstagefright_soft_c2h263enc.so");
+    emplace("OMX.google.mpeg4.decoder", "libstagefright_soft_c2mpeg4dec.so");
+    emplace("OMX.google.mpeg4.encoder", "libstagefright_soft_c2mpeg4enc.so");
+    emplace("OMX.google.mp3.decoder", "libstagefright_soft_c2mp3dec.so");
+    emplace("OMX.google.vorbis.decoder", "libstagefright_soft_c2vorbisdec.so");
+    emplace("OMX.google.opus.decoder", "libstagefright_soft_c2opusdec.so");
+    emplace("OMX.google.vp8.decoder", "libstagefright_soft_c2vp8dec.so");
+    emplace("OMX.google.vp9.decoder", "libstagefright_soft_c2vp9dec.so");
+    emplace("OMX.google.vp8.encoder", "libstagefright_soft_c2vp8enc.so");
+    emplace("OMX.google.vp9.encoder", "libstagefright_soft_c2vp9enc.so");
+    emplace("OMX.google.raw.decoder", "libstagefright_soft_c2rawdec.so");
+    emplace("OMX.google.flac.decoder", "libstagefright_soft_c2flacdec.so");
+    emplace("OMX.google.flac.encoder", "libstagefright_soft_c2flacenc.so");
+    emplace("OMX.google.gsm.decoder", "libstagefright_soft_c2gsmdec.so");
+    emplace("OMX.google.xaac.decoder", "libstagefright_soft_c2xaacdec.so");
+}
+
+c2_status_t C2PlatformComponentStore::copyBuffer(
+        std::shared_ptr<C2GraphicBuffer> src, std::shared_ptr<C2GraphicBuffer> dst) {
+    (void)src;
+    (void)dst;
+    return C2_OMITTED;
+}
+
+c2_status_t C2PlatformComponentStore::query_sm(
+        const std::vector<C2Param*> &stackParams,
+        const std::vector<C2Param::Index> &heapParamIndices,
+        std::vector<std::unique_ptr<C2Param>> *const heapParams) const {
+    return mInterface.query(stackParams, heapParamIndices, C2_MAY_BLOCK, heapParams);
+}
+
+c2_status_t C2PlatformComponentStore::config_sm(
+        const std::vector<C2Param*> &params,
+        std::vector<std::unique_ptr<C2SettingResult>> *const failures) {
+    return mInterface.config(params, C2_MAY_BLOCK, failures);
+}
+
+std::vector<std::shared_ptr<const C2Component::Traits>> C2PlatformComponentStore::listComponents() {
+    // This method SHALL return within 500ms.
+    std::vector<std::shared_ptr<const C2Component::Traits>> list;
+    for (const C2String &alias : mComponentsList) {
+        ComponentLoader &loader = mComponents.at(alias);
+        std::shared_ptr<ComponentModule> module;
+        c2_status_t res = loader.fetchModule(&module);
+        if (res == C2_OK) {
+            std::shared_ptr<const C2Component::Traits> traits = module->getTraits();
+            if (traits) {
+                list.push_back(traits);
+            }
+        }
+    }
+    return list;
+}
+
+c2_status_t C2PlatformComponentStore::findComponent(C2String name, ComponentLoader **loader) {
+    *loader = nullptr;
+    auto pos = mComponents.find(name);
+    // TODO: check aliases
+    if (pos == mComponents.end()) {
+        return C2_NOT_FOUND;
+    }
+    *loader = &pos->second;
+    return C2_OK;
+}
+
+c2_status_t C2PlatformComponentStore::createComponent(
+        C2String name, std::shared_ptr<C2Component> *const component) {
+    // This method SHALL return within 100ms.
+    component->reset();
+    ComponentLoader *loader;
+    c2_status_t res = findComponent(name, &loader);
+    if (res == C2_OK) {
+        std::shared_ptr<ComponentModule> module;
+        res = loader->fetchModule(&module);
+        if (res == C2_OK) {
+            // TODO: get a unique node ID
+            res = module->createComponent(0, component);
+        }
+    }
+    return res;
+}
+
+c2_status_t C2PlatformComponentStore::createInterface(
+        C2String name, std::shared_ptr<C2ComponentInterface> *const interface) {
+    // This method SHALL return within 100ms.
+    interface->reset();
+    ComponentLoader *loader;
+    c2_status_t res = findComponent(name, &loader);
+    if (res == C2_OK) {
+        std::shared_ptr<ComponentModule> module;
+        res = loader->fetchModule(&module);
+        if (res == C2_OK) {
+            // TODO: get a unique node ID
+            res = module->createInterface(0, interface);
+        }
+    }
+    return res;
+}
+
+c2_status_t C2PlatformComponentStore::querySupportedParams_nb(
+        std::vector<std::shared_ptr<C2ParamDescriptor>> *const params) const {
+    return mInterface.querySupportedParams(params);
+}
+
+c2_status_t C2PlatformComponentStore::querySupportedValues_sm(
+        std::vector<C2FieldSupportedValuesQuery> &fields) const {
+    return mInterface.querySupportedValues(fields, C2_MAY_BLOCK);
+}
+
+C2String C2PlatformComponentStore::getName() const {
+    return "android.componentStore.platform";
+}
+
+std::shared_ptr<C2ParamReflector> C2PlatformComponentStore::getParamReflector() const {
+    return mReflector;
+}
+
+std::shared_ptr<C2ComponentStore> GetCodec2PlatformComponentStore() {
+    static std::mutex mutex;
+    static std::weak_ptr<C2ComponentStore> platformStore;
+    std::lock_guard<std::mutex> lock(mutex);
+    std::shared_ptr<C2ComponentStore> store = platformStore.lock();
+    if (store == nullptr) {
+        store = std::make_shared<C2PlatformComponentStore>();
+        platformStore = store;
+    }
+    return store;
+}
+
+} // namespace android
diff --git a/media/codec2/vndk/include/C2AllocatorGralloc.h b/media/codec2/vndk/include/C2AllocatorGralloc.h
new file mode 100644
index 0000000..05d989e
--- /dev/null
+++ b/media/codec2/vndk/include/C2AllocatorGralloc.h
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#ifndef STAGEFRIGHT_CODEC2_ALLOCATOR_GRALLOC_H_
+#define STAGEFRIGHT_CODEC2_ALLOCATOR_GRALLOC_H_
+
+#include <functional>
+
+#include <C2Buffer.h>
+
+namespace android {
+
+/**
+ * Unwrap the native handle from a Codec2 handle allocated by C2AllocatorGralloc.
+ *
+ * @param handle a handle allocated by C2AllocatorGralloc. This includes handles returned for a
+ * graphic block allocation handle returned.
+ *
+ * @return a new NON-OWNING native handle that must be deleted using native_handle_delete.
+ */
+native_handle_t *UnwrapNativeCodec2GrallocHandle(const C2Handle *const handle);
+
+/**
+ * Wrap the gralloc handle and metadata into Codec2 handle recognized by
+ * C2AllocatorGralloc.
+ *
+ * @return a new NON-OWNING C2Handle that must be deleted using native_handle_delete.
+ */
+C2Handle *WrapNativeCodec2GrallocHandle(
+        const native_handle_t *const handle,
+        uint32_t width, uint32_t height, uint32_t format, uint64_t usage, uint32_t stride,
+        uint32_t generation = 0, uint64_t igbp_id = 0, uint32_t igbp_slot = 0);
+
+/**
+ * \todo Get this from the buffer
+ */
+void _UnwrapNativeCodec2GrallocMetadata(
+        const C2Handle *const handle,
+        uint32_t *width, uint32_t *height, uint32_t *format, uint64_t *usage, uint32_t *stride,
+        uint32_t *generation, uint64_t *igbp_id, uint32_t *igbp_slot);
+
+class C2AllocatorGralloc : public C2Allocator {
+public:
+    virtual id_t getId() const override;
+
+    virtual C2String getName() const override;
+
+    virtual std::shared_ptr<const Traits> getTraits() const override;
+
+    virtual c2_status_t newGraphicAllocation(
+            uint32_t width, uint32_t height, uint32_t format, C2MemoryUsage usage,
+            std::shared_ptr<C2GraphicAllocation> *allocation) override;
+
+    virtual c2_status_t priorGraphicAllocation(
+            const C2Handle *handle,
+            std::shared_ptr<C2GraphicAllocation> *allocation) override;
+
+    C2AllocatorGralloc(id_t id, bool bufferQueue = false);
+
+    c2_status_t status() const;
+
+    virtual ~C2AllocatorGralloc() override;
+
+    static bool isValid(const C2Handle* const o);
+
+private:
+    class Impl;
+    Impl *mImpl;
+};
+
+} // namespace android
+
+#endif // STAGEFRIGHT_CODEC2_ALLOCATOR_GRALLOC_H_
diff --git a/media/codec2/vndk/include/C2AllocatorIon.h b/media/codec2/vndk/include/C2AllocatorIon.h
new file mode 100644
index 0000000..1b2051f
--- /dev/null
+++ b/media/codec2/vndk/include/C2AllocatorIon.h
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#ifndef STAGEFRIGHT_CODEC2_ALLOCATOR_ION_H_
+#define STAGEFRIGHT_CODEC2_ALLOCATOR_ION_H_
+
+#include <functional>
+#include <list>
+#include <mutex>
+#include <tuple>
+#include <unordered_map>
+
+#include <C2Buffer.h>
+
+namespace android {
+
+class C2AllocatorIon : public C2Allocator {
+public:
+    // Usage mapper function used by the allocator
+    //   (usage, capacity) => (align, heapMask, flags)
+    //
+    // capacity is aligned to the default block-size (defaults to page size) to reduce caching
+    // overhead
+    typedef std::function<c2_status_t(C2MemoryUsage, size_t,
+                      /* => */ size_t*, unsigned*, unsigned*)> UsageMapperFn;
+
+    virtual id_t getId() const override;
+
+    virtual C2String getName() const override;
+
+    virtual std::shared_ptr<const Traits> getTraits() const override;
+
+    virtual c2_status_t newLinearAllocation(
+            uint32_t capacity, C2MemoryUsage usage,
+            std::shared_ptr<C2LinearAllocation> *allocation) override;
+
+    virtual c2_status_t priorLinearAllocation(
+            const C2Handle *handle,
+            std::shared_ptr<C2LinearAllocation> *allocation) override;
+
+    C2AllocatorIon(id_t id);
+
+    virtual c2_status_t status() const { return mInit; }
+
+    virtual ~C2AllocatorIon() override;
+
+    static bool isValid(const C2Handle* const o);
+
+    /**
+     * Updates the usage mapper for subsequent new allocations, as well as the supported
+     * minimum and maximum usage masks and default block-size to use for the mapper.
+     *
+     * \param mapper this method is called to map Codec 2.0 buffer usage to ion flags
+     *        required by the ion device
+     * \param minUsage minimum buffer usage required for supported allocations (defaults to 0)
+     * \param maxUsage maximum buffer usage supported by the ion allocator (defaults to SW_READ
+     *        | SW_WRITE)
+     * \param blockSize alignment used prior to calling |mapper| for the buffer capacity.
+     *        This also helps reduce the size of cache required for caching mapper results.
+     *        (defaults to the page size)
+     */
+    void setUsageMapper(
+            const UsageMapperFn &mapper, uint64_t minUsage, uint64_t maxUsage, uint64_t blockSize);
+
+private:
+    c2_status_t mapUsage(C2MemoryUsage usage, size_t size,
+                     /* => */ size_t *align, unsigned *heapMask, unsigned *flags);
+
+    c2_status_t mInit;
+    int mIonFd;
+
+    // this locks mTraits, mBlockSize, mUsageMapper, mUsageMapperLru and mUsageMapperCache
+    mutable std::mutex mUsageMapperLock;
+    std::shared_ptr<const Traits> mTraits;
+    size_t mBlockSize;
+    UsageMapperFn mUsageMapper;
+    typedef std::pair<uint64_t, size_t> MapperKey;
+    struct MapperKeyHash {
+        std::size_t operator()(const MapperKey &) const;
+    };
+    typedef std::tuple<size_t, unsigned, unsigned, c2_status_t> MapperValue;
+    typedef std::pair<MapperKey, MapperValue> MapperKeyValue;
+    typedef std::list<MapperKeyValue>::iterator MapperKeyValuePointer;
+    std::list<MapperKeyValue> mUsageMapperLru;
+    std::unordered_map<MapperKey, MapperKeyValuePointer, MapperKeyHash> mUsageMapperCache;
+};
+
+} // namespace android
+
+#endif // STAGEFRIGHT_CODEC2_ALLOCATOR_ION_H_
diff --git a/media/codec2/vndk/include/C2BqBufferPriv.h b/media/codec2/vndk/include/C2BqBufferPriv.h
new file mode 100644
index 0000000..9271a71
--- /dev/null
+++ b/media/codec2/vndk/include/C2BqBufferPriv.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#ifndef STAGEFRIGHT_CODEC2_BQ_BUFFER_PRIV_H_
+#define STAGEFRIGHT_CODEC2_BQ_BUFFER_PRIV_H_
+
+#include <functional>
+
+#include <C2Buffer.h>
+#include <media/stagefright/bqhelper/WGraphicBufferProducer.h>
+
+class C2BufferQueueBlockPool : public C2BlockPool {
+public:
+    C2BufferQueueBlockPool(const std::shared_ptr<C2Allocator> &allocator, const local_id_t localId);
+
+    virtual ~C2BufferQueueBlockPool() override;
+
+    virtual C2Allocator::id_t getAllocatorId() const override {
+        return mAllocator->getId();
+    };
+
+    virtual local_id_t getLocalId() const override {
+        return mLocalId;
+    };
+
+    virtual c2_status_t fetchGraphicBlock(
+            uint32_t width,
+            uint32_t height,
+            uint32_t format,
+            C2MemoryUsage usage,
+            std::shared_ptr<C2GraphicBlock> *block /* nonnull */) override;
+
+    typedef std::function<void(uint64_t producer, int32_t slot, int64_t nsecs)> OnRenderCallback;
+
+    /**
+     * Sets render callback.
+     *
+     * \param renderCallbak callback to call for all dequeue buffer.
+     */
+    virtual void setRenderCallback(const OnRenderCallback &renderCallback = OnRenderCallback());
+
+    /**
+     * Configures an IGBP in order to create blocks. A newly created block is
+     * dequeued from the configured IGBP. Unique Id of IGBP and the slot number of
+     * blocks are passed via native_handle. Managing IGBP is responsibility of caller.
+     * When IGBP is not configured, block will be created via allocator.
+     * Since zero is not used for Unique Id of IGBP, if IGBP is not configured or producer
+     * is configured as nullptr, unique id which is bundled in native_handle is zero.
+     *
+     * \param producer      the IGBP, which will be used to fetch blocks
+     */
+    virtual void configureProducer(const android::sp<android::HGraphicBufferProducer> &producer);
+
+private:
+    const std::shared_ptr<C2Allocator> mAllocator;
+    const local_id_t mLocalId;
+
+    class Impl;
+    std::shared_ptr<Impl> mImpl;
+
+    friend struct C2BufferQueueBlockPoolData;
+};
+
+#endif // STAGEFRIGHT_CODEC2_BUFFER_PRIV_H_
diff --git a/media/codec2/vndk/include/C2BufferPriv.h b/media/codec2/vndk/include/C2BufferPriv.h
new file mode 100644
index 0000000..d0b9152
--- /dev/null
+++ b/media/codec2/vndk/include/C2BufferPriv.h
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#ifndef STAGEFRIGHT_CODEC2_BUFFER_PRIV_H_
+#define STAGEFRIGHT_CODEC2_BUFFER_PRIV_H_
+
+#include <functional>
+
+#include <C2Buffer.h>
+#include <android/hardware/media/bufferpool/1.0/IAccessor.h>
+
+class C2BasicLinearBlockPool : public C2BlockPool {
+public:
+    explicit C2BasicLinearBlockPool(const std::shared_ptr<C2Allocator> &allocator);
+
+    virtual ~C2BasicLinearBlockPool() override = default;
+
+    virtual C2Allocator::id_t getAllocatorId() const override {
+        return mAllocator->getId();
+    }
+
+    virtual local_id_t getLocalId() const override {
+        return BASIC_LINEAR;
+    }
+
+    virtual c2_status_t fetchLinearBlock(
+            uint32_t capacity,
+            C2MemoryUsage usage,
+            std::shared_ptr<C2LinearBlock> *block /* nonnull */) override;
+
+    // TODO: fetchCircularBlock
+
+private:
+    const std::shared_ptr<C2Allocator> mAllocator;
+};
+
+class C2BasicGraphicBlockPool : public C2BlockPool {
+public:
+    explicit C2BasicGraphicBlockPool(const std::shared_ptr<C2Allocator> &allocator);
+
+    virtual ~C2BasicGraphicBlockPool() override = default;
+
+    virtual C2Allocator::id_t getAllocatorId() const override {
+        return mAllocator->getId();
+    }
+
+    virtual local_id_t getLocalId() const override {
+        return BASIC_GRAPHIC;
+    }
+
+    virtual c2_status_t fetchGraphicBlock(
+            uint32_t width,
+            uint32_t height,
+            uint32_t format,
+            C2MemoryUsage usage,
+            std::shared_ptr<C2GraphicBlock> *block /* nonnull */) override;
+
+private:
+    const std::shared_ptr<C2Allocator> mAllocator;
+};
+
+class C2PooledBlockPool : public C2BlockPool {
+public:
+    C2PooledBlockPool(const std::shared_ptr<C2Allocator> &allocator, const local_id_t localId);
+
+    virtual ~C2PooledBlockPool() override;
+
+    virtual C2Allocator::id_t getAllocatorId() const override {
+        return mAllocator->getId();
+    }
+
+    virtual local_id_t getLocalId() const override {
+        return mLocalId;
+    }
+
+    virtual c2_status_t fetchLinearBlock(
+            uint32_t capacity,
+            C2MemoryUsage usage,
+            std::shared_ptr<C2LinearBlock> *block /* nonnull */) override;
+
+    virtual c2_status_t fetchGraphicBlock(
+            uint32_t width,
+            uint32_t height,
+            uint32_t format,
+            C2MemoryUsage usage,
+            std::shared_ptr<C2GraphicBlock> *block) override;
+
+    /**
+     * Retrieves the connection Id for underlying bufferpool
+     */
+    int64_t getConnectionId();
+
+    /**
+     * Retrieves the accessor which is used by underlying bufferpool. (It can be
+     * passed to receiving process.)
+     *
+     * \param accessor          IAccessor will be written to this out parameter.
+     *
+     * \return true             IAcessor is writen successfully.
+     * \return false            IAccessor is not written.
+     */
+    bool getAccessor(android::sp<android::hardware::media::bufferpool::V1_0::IAccessor> *accessor);
+
+private:
+    const std::shared_ptr<C2Allocator> mAllocator;
+    const local_id_t mLocalId;
+
+    class Impl;
+    std::unique_ptr<Impl> mImpl;
+};
+
+#endif // STAGEFRIGHT_CODEC2_BUFFER_PRIV_H_
diff --git a/media/codec2/vndk/include/C2ComponentFactory.h b/media/codec2/vndk/include/C2ComponentFactory.h
new file mode 100644
index 0000000..f6d8b98
--- /dev/null
+++ b/media/codec2/vndk/include/C2ComponentFactory.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#ifndef STAGEFRIGHT_CODEC2_COMPONENT_FACTORY_H_
+#define STAGEFRIGHT_CODEC2_COMPONENT_FACTORY_H_
+
+#include <C2Component.h>
+
+#include <functional>
+#include <memory>
+
+/**
+ * Component factory object that enables to create a component and/or interface from a dynamically
+ * linked library. This is needed because the component/interfaces are managed objects, but we
+ * cannot safely create a managed object and pass it in C.
+ *
+ * Components/interfaces typically inherit from std::enable_shared_from_this, but C requires
+ * passing simple pointer, and shared_ptr constructor needs to know the class to be constructed
+ * derives from enable_shared_from_this.
+ *
+ */
+class C2ComponentFactory {
+public:
+    typedef std::function<void(::C2Component*)> ComponentDeleter;
+    typedef std::function<void(::C2ComponentInterface*)> InterfaceDeleter;
+
+    /**
+     * Creates a component.
+     *
+     * This method SHALL return within 100ms.
+     *
+     * \param id        component ID for the created component
+     * \param component shared pointer where the created component is stored. Cleared on
+     *                  failure and updated on success.
+     *
+     * \retval C2_OK        the component was created successfully
+     * \retval C2_TIMED_OUT could not create the component within the time limit (unexpected)
+     * \retval C2_CORRUPTED some unknown error prevented the creation of the component (unexpected)
+     *
+     * \retval C2_NO_MEMORY not enough memory to create the component
+     */
+    virtual c2_status_t createComponent(
+            c2_node_id_t id, std::shared_ptr<C2Component>* const component,
+            ComponentDeleter deleter = std::default_delete<C2Component>()) = 0;
+
+    /**
+     * Creates a component interface.
+     *
+     * This method SHALL return within 100ms.
+     *
+     * \param id        component interface ID for the created interface
+     * \param interface shared pointer where the created interface is stored. Cleared on
+     *                  failure and updated on success.
+     *
+     * \retval C2_OK        the component interface was created successfully
+     * \retval C2_TIMED_OUT could not create the component interface within the time limit
+     *                      (unexpected)
+     * \retval C2_CORRUPTED some unknown error prevented the creation of the component interface
+     *                      (unexpected)
+     *
+     * \retval C2_NO_MEMORY not enough memory to create the component interface
+     */
+    virtual c2_status_t createInterface(
+            c2_node_id_t id, std::shared_ptr<C2ComponentInterface>* const interface,
+            InterfaceDeleter deleter = std::default_delete<C2ComponentInterface>()) = 0;
+
+    virtual ~C2ComponentFactory() = default;
+
+    typedef ::C2ComponentFactory* (*CreateCodec2FactoryFunc)(void);
+    typedef void (*DestroyCodec2FactoryFunc)(::C2ComponentFactory*);
+};
+
+
+#endif // STAGEFRIGHT_CODEC2_COMPONENT_FACTORY_H_
diff --git a/media/codec2/vndk/include/C2Debug.h b/media/codec2/vndk/include/C2Debug.h
new file mode 100644
index 0000000..af1cdab
--- /dev/null
+++ b/media/codec2/vndk/include/C2Debug.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#ifndef C2UTILS_DEBUG_H_
+#define C2UTILS_DEBUG_H_
+
+#include <util/C2Debug-log.h>
+
+// To allow arbitrary ordering of C2Debug.h and API headers and to keep debug utilities separately
+// from the API definitions, we provide API header specific debug headers.
+#ifdef C2PARAM_H_
+#include <util/C2Debug-param.h>
+#endif
+
+#endif  // C2UTILS_DEBUG_H_
diff --git a/media/codec2/vndk/include/C2ErrnoUtils.h b/media/codec2/vndk/include/C2ErrnoUtils.h
new file mode 100644
index 0000000..5b995f6
--- /dev/null
+++ b/media/codec2/vndk/include/C2ErrnoUtils.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2016 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.
+ */
+
+#ifndef STAGEFRIGHT_CODEC2_ERRNO_UTILS_H_
+#define STAGEFRIGHT_CODEC2_ERRNO_UTILS_H_
+
+#include <errno.h>
+#include <C2.h>
+
+// standard ERRNO mappings
+template<int N> constexpr c2_status_t _c2_errno2status_impl();
+template<> constexpr c2_status_t _c2_errno2status_impl<0>()       { return C2_OK; }
+template<> constexpr c2_status_t _c2_errno2status_impl<EINVAL>()  { return C2_BAD_VALUE; }
+template<> constexpr c2_status_t _c2_errno2status_impl<EACCES>()  { return C2_REFUSED; }
+template<> constexpr c2_status_t _c2_errno2status_impl<EPERM>()   { return C2_REFUSED; }
+template<> constexpr c2_status_t _c2_errno2status_impl<ENOMEM>()  { return C2_NO_MEMORY; }
+
+// map standard errno-s to the equivalent c2_status_t
+template<int... N> struct _c2_map_errno_impl;
+template<int E, int ... N> struct _c2_map_errno_impl<E, N...> {
+    static c2_status_t map(int result) {
+        if (result == E) {
+            return _c2_errno2status_impl <E>();
+        } else {
+            return _c2_map_errno_impl<N...>::map(result);
+        }
+    }
+};
+template<> struct _c2_map_errno_impl<> {
+    static c2_status_t map(int result) {
+        return result == 0 ? C2_OK : C2_CORRUPTED;
+    }
+};
+
+template<int... N>
+c2_status_t c2_map_errno(int result) {
+    return _c2_map_errno_impl<N...>::map(result);
+}
+
+#endif // STAGEFRIGHT_CODEC2_ERRNO_UTILS_H_
+
diff --git a/media/codec2/vndk/include/C2PlatformStorePluginLoader.h b/media/codec2/vndk/include/C2PlatformStorePluginLoader.h
new file mode 100644
index 0000000..4c10643
--- /dev/null
+++ b/media/codec2/vndk/include/C2PlatformStorePluginLoader.h
@@ -0,0 +1,98 @@
+/*
+ * Copyright 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.
+ */
+
+#ifndef STAGEFRIGHT_C2_PLATFORM_STORE_PLUGIN_LOADER_H_
+
+#define STAGEFRIGHT_C2_PLATFORM_STORE_PLUGIN_LOADER_H_
+
+#include <memory>
+
+#include <utils/Mutex.h>
+
+#include <C2.h>
+#include <C2Buffer.h>
+
+/**
+ * Extern C interface for creating block pool from platform store extension.
+ *
+ * \param alloctorId  the ID of the backing allocator type.
+ * \parem blockPoolId the ID of created block pool.
+ *
+ * \return pointer of created C2BlockPool, nullptr on error.
+ */
+typedef ::C2BlockPool* (*CreateBlockPoolFunc)(::C2Allocator::id_t, ::C2BlockPool::local_id_t);
+
+/**
+ * Extern C interface for creating allocator from platform store extension.
+ *
+ * \param alloctorId  the ID of the allocator to create.
+ * \param status      the returned status from creating allocator.
+ *
+ * \return pointer of created C2Allocator, nullptr on error.
+ */
+typedef ::C2Allocator* (*CreateAllocatorFunc)(::C2Allocator::id_t, ::c2_status_t*);
+
+class C2PlatformStorePluginLoader {
+public:
+    static const std::unique_ptr<C2PlatformStorePluginLoader>& GetInstance();
+    ~C2PlatformStorePluginLoader();
+
+    /**
+     * Creates block pool from platform store extension.
+     *
+     * \param alloctorId  the ID of the backing allocator type.
+     * \param blockPoolId the ID of created block pool.
+     * \param pool        shared pointer where the created block pool is stored.
+     *
+     * \retval C2_OK        the block pool was created successfully.
+     * \retval C2_NOT_FOUND the extension symbol was not found.
+     * \retval C2_BAD_INDEX the input allocatorId is not defined in platform store extension.
+     */
+    c2_status_t createBlockPool(::C2Allocator::id_t allocatorId,
+                                ::C2BlockPool::local_id_t blockPoolId,
+                                std::shared_ptr<C2BlockPool>* pool);
+
+    /**
+     * Creates allocator from platform store extension.
+     *
+     * Note that this allocator is not created as shared singleton as C2AllocatorStore does, because
+     * C interface only allows raw pointer transmission for extension.
+     *
+     * \param alloctorId  the ID of the allocator to create.
+     * \param allocator   shared pointer where the created allocator is stored.
+     *
+     * \retval C2_OK        the allocator was created successfully.
+     * \retval C2_TIMED_OUT could not create the allocator within the time limit (unexpected)
+     * \retval C2_CORRUPTED some unknown error prevented the creation of the allocator (unexpected)
+     * \retval C2_NOT_FOUND the extension symbol was not found.
+     * \retval C2_BAD_INDEX the input allocatorId is not defined in platform store extension.
+     * \retval C2_NO_MEMORY not enough memory to create the allocator
+     */
+    c2_status_t createAllocator(::C2Allocator::id_t allocatorId,
+                                std::shared_ptr<C2Allocator>* const allocator);
+
+private:
+    explicit C2PlatformStorePluginLoader(const char *libPath);
+
+    static android::Mutex sMutex;
+    static std::unique_ptr<C2PlatformStorePluginLoader> sInstance;
+
+    void *mLibHandle;
+    CreateBlockPoolFunc mCreateBlockPool;
+    CreateAllocatorFunc mCreateAllocator;
+};
+
+#endif  // STAGEFRIGHT_C2_PLATFORM_STORE_PLUGIN_LOADER_H_
diff --git a/media/codec2/vndk/include/C2PlatformSupport.h b/media/codec2/vndk/include/C2PlatformSupport.h
new file mode 100644
index 0000000..f31282c
--- /dev/null
+++ b/media/codec2/vndk/include/C2PlatformSupport.h
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#ifndef STAGEFRIGHT_CODEC2_PLATFORM_SUPPORT_H_
+#define STAGEFRIGHT_CODEC2_PLATFORM_SUPPORT_H_
+
+#include <C2Component.h>
+#include <C2ComponentFactory.h>
+
+#include <memory>
+
+namespace android {
+
+/**
+ * Returns the platform allocator store.
+ * \retval nullptr if the platform allocator store could not be obtained
+ */
+std::shared_ptr<C2AllocatorStore> GetCodec2PlatformAllocatorStore();
+
+/**
+ * Platform allocator store IDs
+ */
+class C2PlatformAllocatorStore : public C2AllocatorStore {
+public:
+    enum : id_t {
+        /**
+         * ID of the ion backed platform allocator.
+         *
+         * C2Handle consists of:
+         *   fd  shared ion buffer handle
+         *   int size (lo 32 bits)
+         *   int size (hi 32 bits)
+         *   int magic '\xc2io\x00'
+         */
+        ION = PLATFORM_START,
+
+        /**
+         * ID of the gralloc backed platform allocator.
+         *
+         * C2Handle layout is not public. Use C2AllocatorGralloc::UnwrapNativeCodec2GrallocHandle
+         * to get the underlying gralloc handle from a C2Handle, and WrapNativeCodec2GrallocHandle
+         * to create a C2Handle from a gralloc handle - for C2Allocator::priorAllocation.
+         */
+        GRALLOC,
+
+        /**
+         * ID of the bufferqueue backed platform allocator.
+         *
+         * C2Handle layout is not public. Use C2AllocatorGralloc::UnwrapNativeCodec2GrallocHandle
+         * to get the underlying handle from a C2Handle, and WrapNativeCodec2GrallocHandle
+         * to create a C2Handle from a handle - for C2Allocator::priorAllocation.
+         */
+        BUFFERQUEUE,
+
+        /**
+         * ID of indicating the end of platform allocator definition.
+         *
+         * \note always put this macro in the last place.
+         *
+         * Extended platform store plugin should use this macro as the start ID of its own allocator
+         * types.
+         */
+        PLATFORM_END,
+    };
+};
+
+/**
+ * Retrieves a block pool for a component.
+ *
+ * \param id        the local ID of the block pool
+ * \param component the component using the block pool (must be non-null)
+ * \param pool      pointer to where the obtained block pool shall be stored on success. nullptr
+ *                  will be stored here on failure
+ *
+ * \retval C2_OK        the operation was successful
+ * \retval C2_BAD_VALUE the component is null
+ * \retval C2_NOT_FOUND if the block pool does not exist
+ * \retval C2_NO_MEMORY not enough memory to fetch the block pool (this return value is only
+ *                      possible for basic pools)
+ * \retval C2_TIMED_OUT the operation timed out (this return value is only possible for basic pools)
+ * \retval C2_REFUSED   no permission to complete any required allocation (this return value is only
+ *                      possible for basic pools)
+ * \retval C2_CORRUPTED some unknown, unrecoverable error occured during operation (unexpected,
+ *                      this return value is only possible for basic pools)
+ */
+c2_status_t GetCodec2BlockPool(
+        C2BlockPool::local_id_t id, std::shared_ptr<const C2Component> component,
+        std::shared_ptr<C2BlockPool> *pool);
+
+/**
+ * Creates a block pool.
+ * \param allocatorId  the allocator ID which is used to allocate blocks
+ * \param component     the component using the block pool (must be non-null)
+ * \param pool          pointer to where the created block pool shall be store on success.
+ *                      nullptr will be stored here on failure
+ *
+ * \retval C2_OK        the operation was successful
+ * \retval C2_BAD_VALUE the component is null
+ * \retval C2_NOT_FOUND if the allocator does not exist
+ * \retval C2_NO_MEMORY not enough memory to create a block pool
+ */
+c2_status_t CreateCodec2BlockPool(
+        C2PlatformAllocatorStore::id_t allocatorId,
+        std::shared_ptr<const C2Component> component,
+        std::shared_ptr<C2BlockPool> *pool);
+
+/**
+ * Returns the platform component store.
+ * \retval nullptr if the platform component store could not be obtained
+ */
+std::shared_ptr<C2ComponentStore> GetCodec2PlatformComponentStore();
+
+/**
+ * Sets the preferred component store in this process for the sole purpose of accessing its
+ * interface. If this is not called, the default IComponentStore HAL (if exists) is the preferred
+ * store for this purpose. If the default IComponentStore HAL is not present, the platform
+ * component store is used.
+ */
+void SetPreferredCodec2ComponentStore(std::shared_ptr<C2ComponentStore> store);
+
+} // namespace android
+
+#endif // STAGEFRIGHT_CODEC2_PLATFORM_SUPPORT_H_
diff --git a/media/codec2/vndk/include/android-C2Debug-log.h b/media/codec2/vndk/include/android-C2Debug-log.h
new file mode 100644
index 0000000..5910228
--- /dev/null
+++ b/media/codec2/vndk/include/android-C2Debug-log.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#ifndef C2UTILS_ANDROID_DEBUG_LOG_H_
+#define C2UTILS_ANDROID_DEBUG_LOG_H_
+
+/*
+ * Android provides logging and debug macros. Redefine them with C2 prefix and add support for
+ * opting out of verbose logs.
+ */
+
+#ifdef C2_LOG_TAG
+#define LOG_TAG C2_LOG_TAG
+#endif
+
+#include <android-base/logging.h>
+
+#ifdef C2_LOG_VERBOSE
+#define C2_LOG(LEVEL) LOG(::android::base::LEVEL)
+#else
+/**
+ * Use as follows:
+ *
+ * #define C2_LOG_TAG "tag"
+ * //#define C2_LOG_VERBOSE  // enable verbose logs in this file
+ * #include <C2Debug.h>
+ *
+ * C2_LOG(DEBUG) << expr ...;
+ *
+ * Log levels are: VERBOSE, DEBUG, INFO, WARNING, ERROR, FATAL (aborts).
+ *
+ * No endl is required.
+ */
+#define C2_LOG(LEVEL) \
+        (::android::base::LEVEL != ::android::base::VERBOSE) && LOG(::android::base::LEVEL)
+#endif
+
+#define C2_CHECK    CHECK
+#define C2_CHECK_LT CHECK_LT
+#define C2_CHECK_LE CHECK_LE
+#define C2_CHECK_EQ CHECK_EQ
+#define C2_CHECK_GE CHECK_GE
+#define C2_CHECK_GT CHECK_GT
+#define C2_CHECK_NE CHECK_NE
+
+#define C2_DCHECK    DCHECK
+#define C2_DCHECK_LT DCHECK_LT
+#define C2_DCHECK_LE DCHECK_LE
+#define C2_DCHECK_EQ DCHECK_EQ
+#define C2_DCHECK_GE DCHECK_GE
+#define C2_DCHECK_GT DCHECK_GT
+#define C2_DCHECK_NE DCHECK_NE
+
+#endif  // C2UTILS_ANDROID_DEBUG_LOG_H_
diff --git a/media/codec2/vndk/include/util/C2Debug-base.h b/media/codec2/vndk/include/util/C2Debug-base.h
new file mode 100644
index 0000000..a040e42
--- /dev/null
+++ b/media/codec2/vndk/include/util/C2Debug-base.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#ifndef C2UTILS_DEBUG_BASE_H_
+#define C2UTILS_DEBUG_BASE_H_
+
+#include <iostream>
+
+/**
+ * Debug print utilities for Codec 2.0 base objects.
+ */
+
+#define C2_DECLARE_AS_STRING_AND_DEFINE_STREAM_OUT(type) \
+const char *asString(type i, const char *def = "??"); \
+inline std::ostream& operator<<(std::ostream& os, const type &i) { \
+    return os << asString(i); \
+}
+
+C2_DECLARE_AS_STRING_AND_DEFINE_STREAM_OUT(c2_status_t)
+
+
+#endif  // C2UTILS_DEBUG_BASE_H_
+
diff --git a/media/codec2/vndk/include/util/C2Debug-interface.h b/media/codec2/vndk/include/util/C2Debug-interface.h
new file mode 100644
index 0000000..3f31875
--- /dev/null
+++ b/media/codec2/vndk/include/util/C2Debug-interface.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#ifndef C2UTILS_DEBUG_INTERFACE_H_
+#define C2UTILS_DEBUG_INTERFACE_H_
+
+#include <util/C2Debug-base.h>
+#include <util/C2InterfaceUtils.h>
+
+#include <iostream>
+
+template<typename T>
+std::ostream& operator<<(std::ostream& os, const C2SupportedRange<T> &i);
+
+template<typename T>
+std::ostream& operator<<(std::ostream& os, const C2SupportedValueSet<T> &i);
+
+template<typename T>
+std::ostream& operator<<(std::ostream& os, const C2FieldSupportedValuesHelper<T> &i);
+
+#endif  // C2UTILS_DEBUG_INTERFACE_H_
diff --git a/media/codec2/vndk/include/util/C2Debug-log.h b/media/codec2/vndk/include/util/C2Debug-log.h
new file mode 100644
index 0000000..195c4c1
--- /dev/null
+++ b/media/codec2/vndk/include/util/C2Debug-log.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#ifndef C2UTILS_DEBUG_LOG_H_
+#define C2UTILS_DEBUG_LOG_H_
+
+// Platform specific debug utilities
+#ifdef __ANDROID__
+#include <android-C2Debug-log.h>
+#else
+// TODO: implement base debug utils
+#endif
+
+#endif  // C2UTILS_DEBUG_LOG_H_
diff --git a/media/codec2/vndk/include/util/C2Debug-param.h b/media/codec2/vndk/include/util/C2Debug-param.h
new file mode 100644
index 0000000..e1c1de0
--- /dev/null
+++ b/media/codec2/vndk/include/util/C2Debug-param.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#ifndef C2UTILS_DEBUG_PARAM_H_
+#define C2UTILS_DEBUG_PARAM_H_
+
+#include <C2Param.h>
+#include <util/C2Debug-base.h>
+
+#include <iostream>
+
+/**
+ * Debug/print declarations for objects defined in C2Param.h
+ */
+C2_DECLARE_AS_STRING_AND_DEFINE_STREAM_OUT(C2FieldDescriptor::type_t)
+
+std::ostream& operator<<(std::ostream& os, const C2Param::CoreIndex &i);
+std::ostream& operator<<(std::ostream& os, const C2Param::Type &i);
+std::ostream& operator<<(std::ostream& os, const C2Param::Index &i);
+std::ostream& operator<<(std::ostream& os, const _C2FieldId &i);
+std::ostream& operator<<(std::ostream& os, const C2FieldDescriptor &i);
+std::ostream& operator<<(std::ostream& os, const C2ParamField &i);
+
+#endif  // C2UTILS_DEBUG_PARAM_H_
diff --git a/media/codec2/vndk/include/util/C2InterfaceHelper.h b/media/codec2/vndk/include/util/C2InterfaceHelper.h
new file mode 100644
index 0000000..bfb43af
--- /dev/null
+++ b/media/codec2/vndk/include/util/C2InterfaceHelper.h
@@ -0,0 +1,752 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#ifndef C2UTILS_INTERFACE_HELPER_H_
+#define C2UTILS_INTERFACE_HELPER_H_
+
+#include <C2Component.h>
+#include <util/C2InterfaceUtils.h>
+
+#include <map>
+#include <mutex>
+#include <vector>
+
+#include <stddef.h>
+
+/**
+ * Interface Helper
+ */
+using C2R = C2SettingResultsBuilder;
+
+template<typename T, bool E=std::is_enum<T>::value>
+struct _c2_reduce_enum_to_underlying_type {
+    typedef T type;
+};
+
+template<typename T>
+struct _c2_reduce_enum_to_underlying_type<T, true> {
+    typedef typename std::underlying_type<T>::type type;
+};
+
+/**
+ * Helper class to implement parameter reflectors. This class is dynamic and is designed to be
+ * shared by multiple interfaces. This allows interfaces to add structure descriptors as needed.
+ */
+class C2ReflectorHelper : public C2ParamReflector {
+public:
+    C2ReflectorHelper() = default;
+    virtual ~C2ReflectorHelper() = default;
+    virtual std::unique_ptr<C2StructDescriptor> describe(
+            C2Param::CoreIndex paramIndex) const override;
+
+    /**
+     * Adds support for describing the given parameters.
+     *
+     * \param Params types of codec 2.0 structs (or parameters) to describe
+     */
+    template<typename... Params>
+    C2_INLINE void addStructDescriptors() {
+        std::vector<C2StructDescriptor> structs;
+        addStructDescriptors(structs, (_Tuple<Params...> *)nullptr);
+    }
+
+    /**
+     * Adds support for describing a specific struct.
+     *
+     * \param strukt descriptor for the struct that will be moved out.
+     */
+    void addStructDescriptor(C2StructDescriptor &&strukt);
+
+private:
+    template<typename... Params>
+    class C2_HIDE _Tuple { };
+
+    /**
+     * Adds support for describing the given descriptors.
+     *
+     * \param structs List of structure descriptors to add support for
+     */
+    void addStructDescriptors(
+            std::vector<C2StructDescriptor> &structs, _Tuple<> *);
+
+    /**
+     * Utility method that adds support for describing the given descriptors in a recursive manner
+     * one structure at a time using a list of structure descriptors temporary.
+     *
+     * \param T the type of codec 2.0 struct to describe
+     * \param Params rest of the structs
+     * \param structs Temporary list of structure descriptors used to optimize the operation.
+     */
+    template<typename T, typename... Params>
+    C2_INLINE void addStructDescriptors(
+            std::vector<C2StructDescriptor> &structs, _Tuple<T, Params...> *) {
+        structs.emplace_back((T*)nullptr);
+        addStructDescriptors(structs, (_Tuple<Params...> *)nullptr);
+    }
+
+    mutable std::mutex _mMutex;
+    std::map<C2Param::CoreIndex, const C2StructDescriptor> _mStructs; ///< descriptors
+};
+
+/**
+ * Utility class that implements the codec 2.0 interface API-s for some parameters.
+ *
+ * This class must be subclassed.
+ */
+class C2InterfaceHelper {
+public:
+    /**
+     * Returns the base offset of a field at |offset| that could be part of an array or part of a
+     * sub-structure.
+     *
+     * This method does not do field size verification, e.g. if offset if obtained from a structure,
+     * it will not stop at the structure boundary - this is okay, as we just want the base offset
+     * here, which is the same.
+     */
+    static
+    size_t GetBaseOffset(const std::shared_ptr<C2ParamReflector> &reflector,
+                                C2Param::CoreIndex index, size_t offset);
+
+    /**
+     * The interface helper class uses references to std::shared_ptr<T> config parameters.
+     * Internally, these need to be generalized to std::shared_ptr<C2Param> refs, but the cast is
+     * not allowed (as these are references). As such, this class never returns pointer to the
+     * shared_ptrs.
+     */
+    struct ParamRef {
+        template<typename T, typename enable=
+                typename std::enable_if<std::is_convertible<T, C2Param>::value>::type>
+        inline C2_HIDE ParamRef(std::shared_ptr<T> &param)
+            : _mRef(reinterpret_cast<std::shared_ptr<C2Param>*>(&param)) { }
+
+        // used by comparison operator for containers
+        operator std::shared_ptr<C2Param> *() const { return _mRef; }
+
+        /**
+         * Returns a shared pointer to the parameter.
+         */
+        std::shared_ptr<C2Param> get() const { return *_mRef; }
+
+    private:
+        std::shared_ptr<C2Param> *_mRef;
+    };
+
+    /**
+     * Field helper.
+     *
+     * Contains additional information for the field: possible values, and currently supported
+     * values.
+     */
+    class FieldHelper {
+    public:
+        /**
+         * Creates helper for a field with given possible values.
+         *
+         * \param param parameter reference. The parameter does not have to be initialized at this
+         *        point.
+         * \param field field identifier
+         * \param values possible values for the field
+         */
+        FieldHelper(const ParamRef &param, const _C2FieldId &field,
+                    std::unique_ptr<C2FieldSupportedValues> &&values);
+
+        /**
+         * Creates a param-field identifier for this field. This method is called after the
+         * underlying parameter has been initialized.
+         *
+         * \aram index
+         *
+         * @return C2ParamField
+         */
+        C2ParamField makeParamField(C2Param::Index index) const;
+
+        /**
+         * Sets the currently supported values for this field.
+         *
+         * \param values currently supported values that will be moved out
+         */
+        void setSupportedValues(std::unique_ptr<C2FieldSupportedValues> &&values);
+
+        /**
+         * Gets the currently supported values for this field. This defaults to the possible values
+         * if currently supported values were never set.
+         */
+        const C2FieldSupportedValues *getSupportedValues() const;
+
+        /**
+         * Gets the possible values for this field.
+         */
+        const C2FieldSupportedValues *getPossibleValues() const;
+
+    protected:
+        // TODO: move to impl for safety
+        ParamRef mParam;
+        _C2FieldId mFieldId;
+        std::unique_ptr<C2FieldSupportedValues> mPossible;
+        std::unique_ptr<C2FieldSupportedValues> mSupported; ///< if different from possible
+    };
+
+    template<typename T>
+    struct C2_HIDE Param;
+    class ParamHelper;
+
+    /**
+     * Factory is an interface to get the parameter helpers from a std::shared_ptr<T> &.
+     */
+    class Factory {
+        // \todo this may be already in ParamHelper
+        virtual std::shared_ptr<C2ParamReflector> getReflector() const = 0;
+
+        virtual std::shared_ptr<ParamHelper> getParamHelper(const ParamRef &param) const = 0;
+
+    public:
+        virtual ~Factory() = default;
+
+        template<typename T>
+        Param<T> get(std::shared_ptr<T> &param, std::shared_ptr<T> altValue = nullptr) const {
+            return Param<T>(getParamHelper(ParamRef(param)),
+                            altValue == nullptr ? param : altValue,
+                            getReflector());
+        }
+    };
+
+    /**
+     * Typed field helper.
+     */
+    template<typename T>
+    struct Field {
+        /**
+         * Constructor.
+         *
+         * \param helper helper for this field
+         * \param index  parameter index (this is needed as it is not available during parameter
+         *        construction) \todo remove
+         */
+        Field(std::shared_ptr<FieldHelper> helper, C2Param::Index index);
+
+        bool supportsAtAll(T value) const {
+            return C2FieldSupportedValuesHelper<T>(*_mHelper->getPossibleValues()).supports(value);
+        }
+
+        bool supportsNow(T value) const {
+            return C2FieldSupportedValuesHelper<T>(*_mHelper->getSupportedValues()).supports(value);
+        }
+
+        /**
+         * Creates a conflict resolution suggestion builder for this field.
+         */
+        C2ParamFieldValuesBuilder<T> shouldBe() const;
+
+        /**
+         * Creates a currently supported values builder for this field. This is only supported
+         * for non-const fields to disallow setting supported values for dependencies.
+         */
+        C2ParamFieldValuesBuilder<T> mustBe();
+
+        operator C2ParamField() const {
+            return _mField;
+        }
+
+        // TODO
+        C2R validatePossible(const T &value __unused) const {
+            /// TODO
+            return C2R::Ok();
+        }
+
+    private:
+        std::shared_ptr<FieldHelper> _mHelper;
+        C2ParamField _mField;
+    };
+
+    class ParamHelper {
+    public:
+        ParamHelper(ParamRef param, C2StringLiteral name, C2StructDescriptor &&);
+        ParamHelper(ParamHelper &&);
+        ~ParamHelper();
+
+        /**
+         * Finds a field descriptor.
+         */
+        std::shared_ptr<FieldHelper> findField(size_t baseOffs, size_t baseSize) const;
+
+        /// returns the parameter ref for this parameter
+        const ParamRef ref() const;
+
+        /// returns the current value of this parameter as modifiable. The constness of this
+        /// object determines the constness of the returned value.
+        std::shared_ptr<C2Param> value();
+
+        /// returns the current value of this parameter as const
+        std::shared_ptr<const C2Param> value() const;
+
+        /**
+         * Performs a configuration change request for this parameter.
+         *
+         * \param value    the value that is being assigned to this parameter.
+         *                 This could be pointing to the current value of the
+         *                 parameter. This must not change.
+         * \param mayBlock whether blocking is allowed
+         * \param endValue the resulting value
+         * \param factory  parameter factory (to access dependencies)
+         * \param failures vector of failures to append any failures from this
+         *                 operation
+         *
+         * \retval C2_OK        configuration was successful
+         * \retval C2_BAD_VALUE value is incorrect (TBD)
+         * \retval C2_NO_MEMORY not enough memory to perform the assignment
+         * \retval C2_TIMED_OUT configuration timed out
+         * \retval C2_BLOCKING  configuration requires blocking to be allowed
+         * \retval C2_CORRUPTED interface is corrupted
+         */
+        c2_status_t trySet(
+                const C2Param *value, bool mayBlock,
+                bool *changed,
+                Factory &factory,
+                std::vector<std::unique_ptr<C2SettingResult>>* const failures);
+
+        /// returns parameter indices that depend on this parameter
+        const std::vector<C2Param::Index> getDownDependencies() const;
+
+        /// adds a dependent parameter
+        void addDownDependency(C2Param::Index index);
+
+        /// returns that parameter refs for parameters that depend on this
+        const std::vector<ParamRef> getDependenciesAsRefs() const;
+
+        /// returns and moves out stored struct descriptor
+        C2StructDescriptor retrieveStructDescriptor();
+
+        /// returns the name of this parameter
+        C2String name() const;
+
+        /// returns the index of this parameter
+        C2Param::Index index() const;
+
+        /// returns the parameter descriptor
+        std::shared_ptr<const C2ParamDescriptor> getDescriptor() const;
+
+        /**
+         * Validates param helper.
+         *
+         * For now, this fills field info for const params.
+         *
+         * \retval C2_CORRUPTED the parameter cannot be added as such
+         */
+        c2_status_t validate(const std::shared_ptr<C2ParamReflector> &reflector);
+
+    protected:
+        typedef C2ParamDescriptor::attrib_t attrib_t;
+        attrib_t& attrib();
+
+        /// sets the default value of this parameter
+        void setDefaultValue(std::shared_ptr<C2Param> default_);
+
+        /// sets the setter method
+        void setSetter(std::function<C2R(const C2Param *, bool, bool *, Factory &)> setter);
+
+        /// sets the getter method
+        void setGetter(std::function<std::shared_ptr<C2Param>(bool)> getter);
+
+        /// sets the dependencies
+        void setDependencies(std::vector<C2Param::Index> indices, std::vector<ParamRef> refs);
+
+        /// sets the fields and their supported values
+        void setFields(std::vector<C2ParamFieldValues> &&fields);
+
+        /// build this into a final ParamHelper object
+        std::shared_ptr<ParamHelper> build();
+
+        class Impl;
+        std::unique_ptr<Impl> mImpl;
+    };
+
+    /**
+     * Typed parameter helper. This provides access to members as well as field helpers.
+     */
+    template<typename T>
+    struct C2_HIDE Param {
+        Param(
+                std::shared_ptr<ParamHelper> helper, std::shared_ptr<T> &param,
+                std::shared_ptr<C2ParamReflector> reflector)
+            : v(*param.get()),
+              _mTypedParam(param),
+              _mHelper(helper),
+              _mReflector(reflector) { }
+
+        template<typename S>
+        using FieldType = Field<
+                typename _c2_reduce_enum_to_underlying_type<
+                        typename std::remove_const<
+                                typename std::remove_extent<S>::type>::type>::type>;
+
+        template<typename S>
+        FieldType<S> F(S &field) {
+            size_t offs = (uintptr_t)&field - (uintptr_t)&get();
+            // this must fall either within sizeof(T) + FLEX_SIZE or param->size()
+            // size_t size = sizeof(field);
+            // mParam may be null
+            size_t baseSize = sizeof(typename std::remove_extent<S>::type);
+            size_t baseOffs = GetBaseOffset(
+                    _mReflector, T::CORE_INDEX, offs - sizeof(C2Param));
+            if (~baseOffs == 0) {
+                // C2_LOG(FATAL) << "unknown field at offset " << offs << " size " << sizeof(S)
+                //       << " base-size " << baseSize;
+                // __builtin_trap();
+            } else {
+                baseOffs += sizeof(C2Param);
+            }
+
+            std::shared_ptr<FieldHelper> helper = _mHelper->findField(baseOffs, baseSize);
+            return FieldType<S>(helper, _mTypedParam->index());
+        }
+
+        // const Param have const Fields; however, remove const from S
+        template<typename S>
+        const FieldType<S> F(S &field) const {
+            return const_cast<const FieldType<S>>(const_cast<Param *>(this)->F(field));
+        }
+
+        /// Returns a const ref value of this const param.
+        const T &get() const {
+            return *_mTypedParam.get();
+        }
+
+        /// Returns a modifiable ref value of this non-const param.
+        T &set() {
+            return *_mTypedParam.get();
+        }
+
+        /// Const-reference to the value.s
+        T const &v;
+
+    private:
+        std::shared_ptr<T> _mTypedParam;
+        std::shared_ptr<ParamHelper> _mHelper;
+        std::shared_ptr<C2ParamReflector> _mReflector;
+    };
+
+    template<typename T>
+    using C2P = Param<T>;
+
+    /**
+     * Templated move builder class for a parameter helper.
+     */
+    template<typename T>
+    class C2_HIDE ParamBuilder : private ParamHelper {
+    public:
+        /** Construct the parameter builder from minimal info required. */
+        ParamBuilder(std::shared_ptr<T> &param, C2StringLiteral name)
+            : ParamHelper(param, name, C2StructDescriptor((T*)nullptr)),
+              mTypedParam(&param) {
+            attrib() = attrib_t::IS_PERSISTENT;
+        }
+
+        /** Makes this parameter required. */
+        inline ParamBuilder &required() {
+            attrib() |= attrib_t::IS_REQUIRED;
+            return *this;
+        }
+
+        /** Makes this parameter transient (not persistent). */
+        inline ParamBuilder &transient() {
+            attrib() &= ~attrib_t::IS_PERSISTENT;
+            return *this;
+        }
+
+        /** Makes this parameter hidden (not exposed in JAVA API). */
+        inline ParamBuilder &hidden() {
+            attrib() |= attrib_t::IS_HIDDEN;
+            return *this;
+        }
+
+        /** Makes this parameter internal (not exposed to query/settings). */
+        inline ParamBuilder &internal() {
+            attrib() |= attrib_t::IS_INTERNAL;
+            return *this;
+        }
+
+        /** Adds default value. Must be added exactly once. */
+        inline ParamBuilder &withDefault(std::shared_ptr<T> default_) {
+            // CHECK(!mDefaultValue);
+            // WARN_IF(!default_); // could be nullptr if OOM
+            // technically, this could be in the parent
+            *mTypedParam = std::shared_ptr<T>(T::From(C2Param::Copy(*default_).release()));
+            setDefaultValue(default_);
+            std::shared_ptr<T> *typedParam = mTypedParam;
+            setGetter([typedParam](bool) -> std::shared_ptr<C2Param> {
+                return std::static_pointer_cast<C2Param>(*typedParam);
+            });
+            return *this;
+        }
+
+        /** Adds default value. Must be added exactly once. */
+        inline ParamBuilder &withDefault(T *default_) {
+            return withDefault(std::shared_ptr<T>(default_));
+        }
+
+        /** Adds all fields to this parameter with their possible values. */
+        inline ParamBuilder &withFields(std::vector<C2ParamFieldValues> &&fields_) {
+            setFields(std::move(fields_));
+            return *this;
+        }
+
+        /**
+         * Adds a constant value (also as default). Must be added exactly once.
+         *
+         * Const parameters by definition have no dependencies.
+         */
+        inline ParamBuilder &withConstValue(std::shared_ptr<T> default_) {
+            attrib() |= attrib_t::IS_CONST;
+            setSetter([default_](
+                    const C2Param *value, bool mayBlock __unused, bool *changed, Factory &) -> C2R {
+                *changed = false;
+                const T *typedValue = T::From(value);
+                if (typedValue == nullptr) {
+                    return C2R::Corrupted(); // TODO BadValue/Invalid. This should not happen here.
+                }
+                if (*typedValue != *default_) {
+                    return C2R::Corrupted(); // TODO ReadOnly(*default_);
+                }
+                *changed = false;
+                return C2R::Ok();
+            });
+            return withDefault(default_);
+        }
+
+        /** Adds constant value (also as default). Must be added exactly once. */
+        inline ParamBuilder &withConstValue(T *default_) {
+            return withConstValue(std::shared_ptr<T>(default_));
+        }
+
+        /**
+         * Use a strict setter.
+         *
+         * \param fn   strict setter
+         * \param deps dependencies (references)
+         */
+        template<typename ... Deps>
+        inline ParamBuilder &withSetter(
+                C2R (*fn)(bool, const C2P<T> &, C2P<T> &, const C2P<Deps> &...),
+                std::shared_ptr<Deps>& ... deps) {
+            attrib() |= attrib_t::IS_STRICT;
+            std::shared_ptr<T> *typedParam = mTypedParam;
+            setSetter([typedParam, fn, &deps...](
+                    const C2Param *value, bool mayBlock, bool *changed, Factory &factory) -> C2R {
+                *changed = false;
+                const T *typedValue = T::From(value);
+                if (typedValue == nullptr) {
+                    return C2R::Corrupted(); // TODO BadValue/Invalid. This should not happen here.
+                }
+                // Do copy-on-change for parameters in this helper so change can be detected by
+                // a change of the pointer. Do this by working on a proposed value.
+                std::shared_ptr<T> proposedValue =
+                    std::shared_ptr<T>(T::From(C2Param::Copy(*value).release()));
+                if (proposedValue == nullptr) {
+                    return C2R::NoMemory(value->index());
+                }
+                C2P<T> oldValue = factory.get(*typedParam);
+                // Get a parameter helper with value pointing to proposedValue
+                C2P<T> helper = factory.get(*typedParam, proposedValue);
+                C2R result = fn(mayBlock, oldValue, helper, factory.get(deps)...);
+
+                // If value changed, copy result to current value
+                if (helper.get() != *typedParam->get()) {
+                    *typedParam = proposedValue;
+                    *changed = true;
+                }
+                return result;
+            });
+            setDependencies(std::vector<C2Param::Index>{ deps->index()... },
+                            std::vector<ParamRef>{ ParamRef(deps)... });
+            return *this;
+        }
+
+        /**
+         * Use a non-strict setter.
+         *
+         * \param fn   non-strict setter
+         * \param deps dependencies (references)
+         */
+        template<typename ... Deps>
+        inline ParamBuilder &withSetter(
+                C2R (*fn)(bool, C2P<T> &, const C2P<Deps> &...), std::shared_ptr<Deps>& ... deps) {
+            std::shared_ptr<T> *typedParam = mTypedParam;
+            setSetter([typedParam, fn, &deps...](
+                    const C2Param *value, bool mayBlock, bool *changed, Factory &factory) -> C2R {
+                *changed = false;
+                const T *typedValue = T::From(value);
+                if (typedValue == nullptr) {
+                    return C2R::Corrupted(); // TODO BadValue/Invalid. This should not happen here.
+                }
+                // Do copy-on-change for parameters in this helper so change can be detected by
+                // a change of the pointer. Do this by working on a proposed value.
+                std::shared_ptr<T> proposedValue =
+                    std::shared_ptr<T>(T::From(C2Param::Copy(*value).release()));
+                if (proposedValue == nullptr) {
+                    return C2R::NoMemory(value->index());
+                }
+                // Get a parameter helper with value pointing to proposedValue
+                C2P<T> helper = factory.get(*typedParam, proposedValue);
+                C2R result = fn(mayBlock, helper, factory.get(deps)...);
+
+                // If value changed, copy result to current value
+                if (helper.get() != *typedParam->get()) {
+                    *typedParam = proposedValue;
+                    *changed = true;
+                }
+                return result;
+            });
+            setDependencies(std::vector<C2Param::Index>{ deps->index()... },
+                            std::vector<ParamRef>{ ParamRef(deps)... });
+            return *this;
+        }
+
+        /**
+         * Marks this a calculated (read-only) field.
+         *
+         * \param fn   non-strict setter (calculator)
+         * \param deps dependencies (references)
+         */
+        template<typename ... Deps>
+        inline ParamBuilder &calculatedAs(
+                C2R (*fn)(bool, C2P<T> &, const C2P<Deps> &...), std::shared_ptr<Deps>& ... deps) {
+            attrib() |= attrib_t::IS_READ_ONLY;
+            return withSetter(fn, std::forward<decltype(deps)>(deps)...);
+        }
+
+        inline std::shared_ptr<ParamHelper> build() {
+            return ParamHelper::build();
+        }
+
+    protected:
+        std::shared_ptr<T> *mTypedParam;
+    };
+
+    template<typename T>
+    static ParamBuilder<T> DefineParam(std::shared_ptr<T> &param, C2StringLiteral name) {
+        return ParamBuilder<T>(param, name);
+    }
+
+public:
+    c2_status_t query(
+            const std::vector<C2Param*> &stackParams,
+            const std::vector<C2Param::Index> &heapParamIndices,
+            c2_blocking_t mayBlock,
+            std::vector<std::unique_ptr<C2Param>>* const heapParams) const;
+
+    /**
+     * Helper implementing config calls as well as other configuration updates.
+     *
+     * This method is virtual, so implementations may provide wrappers around it (and perform
+     * actions just before and after a configuration).
+     *
+     * \param params
+     * \param mayBlock
+     * \param failures
+     * \param updateParams if true, the updated parameter values are copied back into the arguments
+     *                     passed in |params|
+     * \param changes      pointed to a vector to receive settings with their values changed. If not
+     *                     null, settings with their values changed are added to this.
+     * \return result from config
+     */
+    virtual c2_status_t config(
+            const std::vector<C2Param*> &params, c2_blocking_t mayBlock,
+            std::vector<std::unique_ptr<C2SettingResult>>* const failures,
+            bool updateParams = true,
+            std::vector<std::shared_ptr<C2Param>> *changes = nullptr);
+
+    c2_status_t querySupportedParams(
+            std::vector<std::shared_ptr<C2ParamDescriptor>> *const params) const;
+
+    c2_status_t querySupportedValues(
+            std::vector<C2FieldSupportedValuesQuery> &fields, c2_blocking_t mayBlock) const;
+
+    std::shared_ptr<C2ReflectorHelper> getReflector() {
+        return mReflector;
+    }
+
+    typedef std::unique_lock<std::mutex> Lock;
+
+    /**
+     * Locks the interface and returns a lock. This lock must be unlocked or released without
+     * calling any other blocking call.
+     */
+    Lock lock() const;
+
+private:
+    void setInterfaceAddressBounds(uintptr_t start, uintptr_t end) {
+        // TODO: exclude this helper
+        (void)start;
+        (void)end;
+    }
+
+protected:
+    mutable std::mutex mMutex;
+    std::shared_ptr<C2ReflectorHelper> mReflector;
+    struct FactoryImpl;
+    std::shared_ptr<FactoryImpl> _mFactory;
+
+    C2InterfaceHelper(std::shared_ptr<C2ReflectorHelper> reflector);
+
+    /**
+     * Adds a parameter to this interface.
+     * \note This method CHECKs.
+     *
+     * \param param parameter to add.
+     */
+    void addParameter(std::shared_ptr<ParamHelper> param);
+
+    /**
+     * Returns the dependency index for a parameter.
+     *
+     * \param ix the index of the parameter
+     */
+    size_t getDependencyIndex_l(C2Param::Index ix) const;
+
+    virtual ~C2InterfaceHelper() = default;
+
+    /**
+     * Sets subclass instance's address and size.
+     *
+     * \todo allow subclass to specify parameter address range directly (e.g. do not assume that
+     *       they are local to the subclass instance)
+     *
+     * \param T type of the derived instance
+     * \param instance pointer to the derived instance
+     */
+    template<typename T>
+    inline C2_HIDE void setDerivedInstance(T *instance) {
+        setInterfaceAddressBounds((uintptr_t)instance, (uintptr_t)(instance + 1));
+    }
+
+    C2_DO_NOT_COPY(C2InterfaceHelper);
+};
+
+/**
+ * Creates a C2ParamFieldValuesBuilder class for a field of a parameter
+ *
+ * \param spParam a configuration parameter in an interface class subclassed from C2InterfaceHelper.
+ * \param field   a field of such parameter
+ */
+#define C2F(spParam, field) \
+    C2ParamFieldValuesBuilder< \
+            typename _c2_reduce_enum_to_underlying_type< \
+                    typename std::remove_reference< \
+                            typename std::remove_extent< \
+                                    decltype(spParam->field)>::type>::type>::type>( \
+                                            C2ParamField(spParam.get(), &spParam->field))
+
+#endif  // C2UTILS_INTERFACE_HELPER_H_
diff --git a/media/codec2/vndk/include/util/C2InterfaceUtils.h b/media/codec2/vndk/include/util/C2InterfaceUtils.h
new file mode 100644
index 0000000..e9037e5
--- /dev/null
+++ b/media/codec2/vndk/include/util/C2InterfaceUtils.h
@@ -0,0 +1,1135 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#ifndef C2UTILS_INTERFACE_UTILS_H_
+#define C2UTILS_INTERFACE_UTILS_H_
+
+#include <C2Param.h>
+#include <C2Work.h>
+
+#include <cmath>
+#include <iterator>
+#include <limits>
+#include <type_traits>
+
+/**
+ * Helper class to map underlying types to C2Value types as well as to print field values. This is
+ * generally the same as simply the underlying type except for characters (STRING) and bytes (BLOB).
+ */
+template<typename T>
+struct C2_HIDE _C2FieldValueHelper {
+    typedef T ValueType;
+    inline static std::ostream& put(std::ostream &os, const C2Value::Primitive &p) {
+        return os << p.ref<T>();
+    }
+};
+
+template<>
+struct C2_HIDE _C2FieldValueHelper<uint8_t> {
+    typedef uint32_t ValueType;
+    static std::ostream& put(std::ostream &os, const C2Value::Primitive &p);
+};
+
+template<>
+struct C2_HIDE _C2FieldValueHelper<char> {
+    typedef int32_t ValueType;
+    static std::ostream& put(std::ostream &os, const C2Value::Primitive &p);
+};
+
+/**
+ * Supported value range utility for a field of a given type.
+ *
+ * This mimics C2FieldSupportedValue for RANGE type.
+ */
+template<typename T>
+class C2SupportedRange {
+    typedef typename _C2FieldValueHelper<T>::ValueType ValueType;
+
+//private:
+    constexpr static T MIN_VALUE = std::numeric_limits<T>::min();
+    constexpr static T MAX_VALUE = std::numeric_limits<T>::max();
+    constexpr static T MIN_STEP = std::is_floating_point<T>::value ? 0 : 1;
+
+public:
+    /**
+     * Constructs an empty range with no supported values.
+     *
+     * \note This is a specializated supported range representation that is only used for
+     * this object - it is equivalent to the EMPTY type in C2FieldSupportedValues.
+     */
+    inline static constexpr C2SupportedRange<T> None() {
+        return C2SupportedRange(MAX_VALUE, MIN_VALUE);
+    }
+
+    /**
+     * Constructs a range with all values supported.
+     */
+    inline static constexpr C2SupportedRange<T> Any() {
+        return C2SupportedRange(MIN_VALUE, MAX_VALUE);
+    }
+
+    /**
+     * Constructs a range with a single supported value.
+     *
+     * \param value the sole supported value
+     */
+    inline static constexpr C2SupportedRange<T> EqualTo(T value) {
+        return C2SupportedRange(value, value);
+    }
+
+    /**
+     * Constructs a range with supported values greater than a given value.
+     *
+     * \param value the given value
+     */
+    inline static C2SupportedRange<T> GreaterThan(T value) {
+        return (value == MAX_VALUE ? None() :
+                std::is_floating_point<T>::value ?
+                        C2SupportedRange(std::nextafter(value, MAX_VALUE), MAX_VALUE) :
+                        C2SupportedRange(value + MIN_STEP, MAX_VALUE));
+    }
+
+    /**
+     * Constructs a range with supported values greater than or equal to a given value.
+     *
+     * \param value the given value
+     */
+    inline static constexpr C2SupportedRange<T> GreaterThanOrEqualTo(T value) {
+        return C2SupportedRange(value, MAX_VALUE);
+    }
+
+    /**
+     * Constructs a range with supported values greater than or equal to (aka not less than) a given
+     * value.
+     *
+     * \param value the given value
+     */
+    inline static constexpr C2SupportedRange<T> NotLessThan(T value) {
+        return GreaterThanOrEqualTo(value);
+    }
+
+    /**
+     * Constructs a range with supported values less than or equal to a given value.
+     *
+     * \param value the given value
+     */
+    inline static constexpr C2SupportedRange<T> LessThanOrEqualTo(T value) {
+        return C2SupportedRange(MIN_VALUE, value);
+    }
+
+    /**
+     * Constructs a range with supported values less than or equal to (aka not greater than) a given
+     * value.
+     *
+     * \param value the given value
+     */
+    inline static constexpr C2SupportedRange<T> NotGreaterThan(T value) {
+        return LessThanOrEqualTo(value);
+    }
+
+    /**
+     * Constructs a range with supported values less than a given value.
+     *
+     * \param value the given value
+     */
+    inline static C2SupportedRange<T> LessThan(T value) {
+        return (value == MIN_VALUE ? None() :
+                std::is_floating_point<T>::value ?
+                        C2SupportedRange(MIN_VALUE, std::nextafter(value, MIN_VALUE)) :
+                        C2SupportedRange(MIN_VALUE, value - MIN_STEP));
+    }
+
+    /**
+     * Constructs a continuous or arithmetic range between two values.
+     *
+     * \param min the lower value
+     * \param max the higher value (if this is lower than |min| the range will be empty)
+     * \param step the step of the arithmetic range. (If this is 0 for floating point types or 1 for
+     *        integer types, the constructed range is continuous)
+     */
+    inline static constexpr
+    C2SupportedRange<T> InRange(T min, T max, T step = MIN_STEP) {
+        return C2SupportedRange(min, max, step);
+    }
+
+    /**
+     * Constructs a range over a geometric series between two values.
+     *
+     * \param min the lower bound of the range. This value is always part of the constructed range
+     *        as long as it is not greater than |max|.
+     * \param max the upper bound of the range. This value is only part of the constructed
+     *        range if it is part of the geometric series.
+     * \param num the numerator of the geometric series.
+     * \param denom the denominator of the geometric series.
+     */
+    inline static constexpr
+    C2SupportedRange<T> InSeries(T min, T max, T num, T denom) {
+        return C2SupportedRange(min, max, 0, num, denom);
+    }
+
+    /**
+     * Constructs a range over a multiply-accumulate series between two values.
+     *
+     * \param min the lower bound of the range. This value is always part of the constructed range
+     *        as long as it is not greater than |max|.
+     * \param max the upper bound of the range. This value is only part of the constructed
+     *        range if it is part of the series.
+     * \param step the accumulator of the multiply-accumulate series
+     * \param num the numerator of the multiply-accumulate series.
+     * \param denom the denominator of the multiply-accumulate series.
+     */
+    inline static constexpr
+    C2SupportedRange<T> InMacSeries(T min, T max, T step, T num, T denom) {
+        return C2SupportedRange(min, max, step, num, denom);
+    }
+
+    /**
+     * Constructs a range from a generic C2FieldSupportedValues object. This will be an empty
+     * range if the supported values are not of RANGE type.
+     *
+     * \param values the supported values object
+     */
+    C2SupportedRange(const C2FieldSupportedValues &values);
+
+    /**
+     * Returns whether this range is empty.
+     */
+    inline constexpr bool isEmpty() const {
+        return _mMin > _mMax;
+    }
+
+    /**
+     * Returns whether this range is valid.
+     *
+     * Ranges are valid if they are continuous or monotonic.
+     */
+    inline constexpr bool isValid() const {
+        // TODO: handle overflow or negative series
+        return _mDenom > 0 && _mNum >= _mDenom && _mMin * (_mDenom - _mNum) < _mStep * _mDenom;
+    }
+
+    /**
+     * Returns whether a value is part of this range.
+     *
+     * \param value the value to check.
+     */
+    bool contains(T value) const;
+
+    /**
+     * Returns a new range that is the intersection of this range and another, if it is
+     * representable as a range object.
+     *
+     * \param limit the other range
+     */
+    C2SupportedRange<T> limitedTo(const C2SupportedRange<T> &limit) const;
+
+    /**
+     * Converts this object to a C2FieldSupportedValues object.
+     */
+    inline operator C2FieldSupportedValues() const {
+        return C2FieldSupportedValues(_mMin, _mMax, _mStep, _mNum, _mDenom);
+    }
+
+    /**
+     * Returns the lower bound and starting point of this range.
+     */
+    inline C2_HIDE constexpr T min() const   { return _mMin;   }
+
+    /**
+     * Returns the upper bound of this range.
+     */
+    inline C2_HIDE constexpr T max() const   { return _mMax;   }
+
+    /**
+     * Returns the step of this range.
+     */
+    inline C2_HIDE constexpr T step() const  { return _mStep;  }
+
+    /**
+     * Returns the numerator of this range.
+     */
+    inline C2_HIDE constexpr T num() const   { return _mNum;   }
+
+    /**
+     * Returns the denominator of this range.
+     */
+    inline C2_HIDE constexpr T denom() const { return _mDenom; }
+
+private:
+    /**
+     * Returns whether x[i...] is all values between _mMin and _mMax.
+     */
+    inline C2_HIDE constexpr bool isSimpleRange() const {
+        return _mStep == MIN_STEP && _mNum == 1 && _mDenom == 1;
+    }
+
+    /**
+     * Returns whether x[i...] is defined as such:
+     * x[i + 1] = x[i] + _mStep, where _mStep > 0 and _mMin <= x[i] <= _mMax
+     */
+    inline C2_HIDE constexpr bool isArithmeticSeries() const {
+        return _mStep > MIN_STEP && _mNum == 1 && _mDenom == 1;
+    }
+
+    /**
+     * Returns whether x[i...] is defined as such:
+     * x[i] = x[0] * (_mNum / _mDenom) ^ i (with rounding), where _mNum > _mDenom > 0 and x[0] > 0
+     */
+    inline C2_HIDE constexpr bool isGeometricSeries() const {
+        return _mMin > 0 && _mStep == 0 && _mNum > _mDenom && _mDenom > 0;
+    }
+
+    /**
+     * Returns whether x[i...] is defined as such:
+     * x[i + 1] = x[i] * _mNum / _mDenom + _mStep (with rounding), while x[i + 1] > x[i], where
+     * _mStep != 0, _mDenom > 0 and _mNum > 0
+     */
+    inline C2_HIDE constexpr bool isMacSeries() const {
+        return _mStep != 0 && _mNum > 0 && _mDenom > 0;
+    }
+
+    /**
+     * Constructs an arithmetic or continuous range.
+     *
+     * \param min the lower value
+     * \param max the higher value (if this is lower than |min| the range will be empty)
+     * \param step the step of the arithmetic range. (If this is 0 for floating point types or 1 for
+     *        integer types, the constructed range is continuous)
+     */
+    constexpr C2_HIDE C2SupportedRange(T min, T max, T step = T(std::is_floating_point<T>::value ? 0 : 1))
+        : _mMin(min), _mMax(max), _mStep(step), _mNum(1), _mDenom(1) { }
+
+    /**
+     * Constructs a range over a geomertic sor multiply-accumulate series.
+     *
+     * \param min the lower bound of the range. This value is always part of the constructed range
+     *        as long as it is not greater than |max|.
+     * \param max the upper bound of the range. This value is only part of the constructed
+     *        range if it is part of the geometric series.
+     * \param step the accumulator of the multiply-accumulate series. This is 0 for a pure geometric
+     *        series
+     * \param num the numerator of the geometric series.
+     * \param denom the denominator of the geometric series.
+     */
+    constexpr C2_HIDE C2SupportedRange(T min, T max, T step, T num, T den)
+        : _mMin(min), _mMax(max), _mStep(step), _mNum(num), _mDenom(den) { }
+
+    T _mMin; ///< lower bound and starting point
+    T _mMax; ///< upper bound
+    T _mStep; ///< step of an arithmetic series (0 if continuous floating point range)
+    T _mNum; ///< numerator of a geometric series
+    T _mDenom; ///< denominator of a geometric series
+};
+
+/**
+ * Ordered supported flag set for a field of a given type.
+ */
+template<typename T>
+class C2SupportedFlags {
+    typedef typename _C2FieldValueHelper<T>::ValueType ValueType;
+
+public:
+    /**
+     * Constructs an empty flag set.
+     *
+     * \note This is a specializated supported flags representation that is only used for
+     * this object - it is equivalent to the EMPTY type in C2FieldSupportedValues.
+     */
+    static inline C2SupportedFlags<T> None() {
+        return C2SupportedFlags(std::initializer_list<C2Value::Primitive>());
+    }
+
+    /**
+     * Constructs a flags set of given flags.
+     *
+     * \param flags the ordered set of flags as an initializer list.
+     * \param min minimum set of flags to be set.
+     */
+    static inline C2SupportedFlags<T> Flags(const std::initializer_list<T> flags, T min = T(0)) {
+        return C2SupportedFlags(min, flags);
+    }
+
+    /**
+     * Constructs a flags set of given flags.
+     *
+     * \param flags the ordered set of flags.
+     * \param min minimum set of flags to be set.
+     */
+    static inline C2SupportedFlags<T> Flags(const std::vector<T> &flags, T min = T(0)) {
+        return C2SupportedFlags(min, flags);
+    }
+
+    /**
+     * Constructs a flag set from a generic C2FieldSupportedValues object. This will be an empty
+     * set if the supported values are not of FLAGS type.
+     *
+     * \param values the supported values object
+     */
+    C2SupportedFlags<T>(const C2FieldSupportedValues &values) {
+        if (values.type == C2FieldSupportedValues::FLAGS) {
+            _mValues.insert(_mValues.end(), values.values.begin(), values.values.end());
+        }
+    }
+
+    /**
+     * Returns whether this set is empty.
+     */
+    constexpr bool isEmpty() const {
+        return _mValues.empty();
+    }
+
+    /**
+     * Returns whether a value is part of this set.
+     *
+     * \param value the value to check.
+     */
+    bool contains(T value) const;
+
+    /**
+     * Returns a new flag set that is the intersection of this set and another.
+     *
+     * \param limit the other value set
+     */
+    C2SupportedFlags<T> limitedTo(const C2SupportedFlags<T> &limit) const;
+
+    /**
+     * Converts this object to a C2FieldSupportedValues object.
+     */
+    operator C2FieldSupportedValues() const {
+        return C2FieldSupportedValues(!isEmpty() /* flags */, _mValues);
+    }
+
+    /**
+     * Returns the ordered set of flags of this object.
+     */
+    const std::vector<T> flags() const;
+
+    /**
+     * Returns the minimum set of flags for this object.
+     */
+    T min() const;
+
+    /**
+     * Clears this supported value set.
+     */
+    inline void clear() {
+        _mValues.clear();
+    }
+
+private:
+    /**
+     * Constructs a flag set directly from an internal representation.
+     *
+     * \param values a vector containing the minimum flag set followed by the set of flags
+     */
+    C2SupportedFlags(std::vector<C2Value::Primitive> &&values)
+        : _mValues(values) {
+    }
+
+    /**
+     * Constructs a flag set from a set of flags and a minimum flag set.
+     *
+     * \param flags the set
+     */
+    C2SupportedFlags(T min, const std::vector<T> &flags) {
+        _mValues.emplace_back(min);
+        for (T elem : flags) {
+            _mValues.emplace_back(elem);
+        }
+    }
+
+    /**
+     * Constructs a flag set from a set of initializer list values and a minimum flag set
+     *
+     * \param flags the set
+     */
+    C2SupportedFlags(T min, const std::initializer_list<T> flags) {
+        _mValues.emplace_back(min);
+        for (T elem : flags) {
+            _mValues.emplace_back(elem);
+        }
+    }
+
+    std::vector<C2Value::Primitive> _mValues; ///< the minimum flag set followed by the set of flags
+};
+
+/**
+ * Ordered supported value set for a field of a given type.
+ */
+template<typename T>
+class C2SupportedValueSet {
+    typedef typename _C2FieldValueHelper<T>::ValueType ValueType;
+
+public:
+    /**
+     * Constructs an empty value set.
+     *
+     * \note This is a specializated supported range representation that is only used for
+     * this object - it is equivalent to the EMPTY type in C2FieldSupportedValues.
+     */
+    static inline C2SupportedValueSet<T> None() {
+        return C2SupportedValueSet({ });
+    }
+
+    /**
+     * Constructs a value set of given values.
+     *
+     * \param values the ordered set of values as an initializer list.
+     */
+    static inline C2SupportedValueSet<T> OneOf(const std::initializer_list<T> values) {
+        return C2SupportedValueSet(values);
+    }
+
+    /**
+     * Constructs a value set of given values.
+     *
+     * \param values the ordered set of values.
+     */
+    static inline C2SupportedValueSet<T> OneOf(const std::vector<T> &values) {
+        return C2SupportedValueSet(values);
+    }
+
+    /**
+     * Constructs a value set from a generic C2FieldSupportedValues object. This will be an empty
+     * set if the supported values are not of VALUES type.
+     *
+     * \param values the supported values object
+     */
+    C2SupportedValueSet<T>(const C2FieldSupportedValues &values) {
+        if (values.type == C2FieldSupportedValues::VALUES) {
+            _mValues.insert(_mValues.end(), values.values.begin(), values.values.end());
+        }
+    }
+
+    /**
+     * Returns whether this range is empty.
+     */
+    constexpr bool isEmpty() const {
+        return _mValues.empty();
+    }
+
+    /**
+     * Returns whether a value is part of this set.
+     *
+     * \param value the value to check.
+     */
+    bool contains(T value) const;
+
+    /**
+     * Returns a new value set that is the intersection of this set and another.
+     *
+     * \param limit the other value set
+     */
+    C2SupportedValueSet<T> limitedTo(const C2SupportedValueSet<T> &limit) const;
+
+    /**
+     * Returns a new value set that is the intersection of this set and a value range.
+     *
+     * \param limit the other range
+     */
+    C2SupportedValueSet<T> limitedTo(const C2SupportedRange<T> &limit) const;
+
+    /**
+     * Returns a new value set that is the intersection of this set and a flag set.
+     *
+     * \param limit the other flag set
+     */
+    C2SupportedValueSet<T> limitedTo(const C2SupportedFlags<T> &limit) const;
+
+    /**
+     * Converts this object to a C2FieldSupportedValues object.
+     */
+    operator C2FieldSupportedValues() const {
+        return C2FieldSupportedValues(false /* flags */, _mValues);
+    }
+
+    /**
+     * Returns the ordered set of values of this object.
+     */
+    const std::vector<T> values() const;
+
+    /**
+     * Clears this supported value set.
+     */
+    inline void clear() {
+        _mValues.clear();
+    }
+
+private:
+    /**
+     * Constructs a value set from a set of C2Value::Primitive values.
+     *
+     * \param values the set
+     */
+    C2SupportedValueSet(std::vector<C2Value::Primitive> &&values)
+        : _mValues(values) {
+    }
+
+    /**
+     * Constructs a value set from a set of values.
+     *
+     * \param values the set
+     */
+    C2SupportedValueSet(const std::vector<T> &values) {
+        for (T elem : values) {
+            _mValues.emplace_back(elem);
+        }
+    }
+
+    /**
+     * Constructs a value set from a set of initializer list values
+     *
+     * \param values the set
+     */
+    C2SupportedValueSet(const std::initializer_list<T> values) {
+        for (T elem : values) {
+            _mValues.emplace_back(elem);
+        }
+    }
+
+    std::vector<C2Value::Primitive> _mValues; ///< the supported set of values
+};
+
+/**
+ * Helper class to handle C2FieldSupporteValues object for fields of various types.
+ */
+template<typename T>
+class C2FieldSupportedValuesHelper;
+
+// templated operator must be predeclared for friend declaration
+template<typename T>
+std::ostream& operator<<(std::ostream& os, const C2FieldSupportedValuesHelper<T> &i);
+
+template<typename T>
+class C2FieldSupportedValuesHelper {
+public:
+    /**
+     * Creates a helper for a specific type from a generic C2FieldSupportedValues struct.
+     */
+    C2FieldSupportedValuesHelper(const C2FieldSupportedValues &values);
+
+    // TRICKY: needed for std::unique_ptr<Impl> declaration
+    ~C2FieldSupportedValuesHelper();
+
+    // support copy constructor/operator
+    C2FieldSupportedValuesHelper(const C2FieldSupportedValuesHelper &);
+    C2FieldSupportedValuesHelper& operator=(const C2FieldSupportedValuesHelper &);
+
+    bool supports(T value) const;
+
+private:
+    // use pimpl as implementation may change in the future
+    struct Impl;
+    std::unique_ptr<Impl> _mImpl;
+
+    friend std::ostream& operator<< <T>(std::ostream& os, const C2FieldSupportedValuesHelper<T> &i);
+    //friend std::ostream& operator<<(std::ostream& os, const C2FieldSupportedValuesHelper &i);
+};
+
+/**
+ * Builder for supported values for a field of a given type.
+ *
+ * This builder can be used to successively restrict the supported values for a field. Upon
+ * creation, there are no supported values specified - which for this builder means that all
+ * values are supported.
+ */
+template<typename T>
+class C2ParamFieldValuesBuilder {
+public:
+    /**
+     * Creates a builder with no defined values - but implicitly any value allowed.
+     */
+    C2ParamFieldValuesBuilder(const C2ParamField &field);
+
+    /**
+     * Get C2ParamFieldValues from this builder.
+     */
+    operator C2ParamFieldValues() const;
+
+    /**
+     * Define the supported values as the currently supported values of this builder.
+     */
+    C2ParamFieldValuesBuilder<T> &any();
+
+    /**
+     * Restrict (and thus define) the supported values to none.
+     *
+     * \note This really should not be used from the builder as all params must have supported
+     *       values, but is here in case this is really the case.
+     */
+    C2ParamFieldValuesBuilder<T> &none();
+
+    /**
+     * Restrict (and thus define) the supported values to |value| alone.
+     */
+    C2ParamFieldValuesBuilder<T> &equalTo(T value);
+
+    /**
+     * Restrict (and thus define) the supported values to values greater than |value|.
+     */
+    inline C2ParamFieldValuesBuilder<T> &greaterThan(T value) {
+        return limitTo(C2SupportedRange<T>::GreaterThan(value));
+    }
+
+    /**
+     * Restrict (and thus define) the supported values to values greater than or equal to |value|.
+     */
+    C2ParamFieldValuesBuilder<T> &greaterThanOrEqualTo(T value) {
+        return limitTo(C2SupportedRange<T>::GreaterThanOrEqualTo(value));
+    }
+
+    /**
+     * Restrict (and thus define) the supported values to values greater than or equal to |value|.
+     */
+    C2ParamFieldValuesBuilder<T> &notLessThan(T value) {
+        return limitTo(C2SupportedRange<T>::NotLessThan(value));
+    }
+
+    /**
+     * Restrict (and thus define) the supported values to values less than or equal to |value|.
+     */
+    C2ParamFieldValuesBuilder<T> &lessThanOrEqualTo(T value) {
+        return limitTo(C2SupportedRange<T>::LessThanOrEqualTo(value));
+    }
+
+    /**
+     * Restrict (and thus define) the supported values to values less than or equal to |value|.
+     */
+    C2ParamFieldValuesBuilder<T> &notGreaterThan(T value) {
+        return limitTo(C2SupportedRange<T>::NotGreaterThan(value));
+    }
+
+    /**
+     * Restrict (and thus define) the supported values to values less than |value|.
+     */
+    C2ParamFieldValuesBuilder<T> &lessThan(T value) {
+        return limitTo(C2SupportedRange<T>::LessThan(value));
+    }
+
+    /**
+     * Restrict (and thus define) the supported values to values in the range of [ |min|, |max| ]
+     * with optional |step|.
+     */
+    C2ParamFieldValuesBuilder<T> &inRange(
+            T min, T max, T step = std::is_floating_point<T>::value ? T(0) : T(1))  {
+        return limitTo(C2SupportedRange<T>::InRange(min, max, step));
+    }
+
+    /**
+     * Restrict (and thus define) the supported values to values in the geometric series starting
+     * from |min| with factor |num| / |denom|, not greater than |max|.
+     */
+    C2ParamFieldValuesBuilder<T> &inSeries(T min, T max, T num, T denom) {
+        return limitTo(C2SupportedRange<T>::InSeries(min, max, num, denom));
+    }
+
+    /**
+     * Restrict (and thus define) the supported values to values in the multiply-accumulate series
+     * starting from |min| with factor |num| / |denom| and |step|, not greater than |max|.
+     */
+    C2ParamFieldValuesBuilder<T> &inMacSeries(T min, T max, T step, T num, T denom) {
+        return limitTo(C2SupportedRange<T>::InMacSeries(min, max, step, num, denom));
+    }
+
+    /**
+     * Restrict (and thus define) the supported values to values in |values|.
+     */
+    C2ParamFieldValuesBuilder<T> &oneOf(const std::initializer_list<T> values) {
+        return limitTo(C2SupportedValueSet<T>::OneOf(values));
+    }
+
+    /**
+     * Restrict (and thus define) the supported values to values in |values|.
+     */
+    C2ParamFieldValuesBuilder<T> &oneOf(const std::vector<T> &values) {
+        return limitTo(C2SupportedValueSet<T>::OneOf(values));
+    }
+
+    /**
+     * Restrict (and thus define) the supported values to flags in |flags| with at least |min|
+     * set.
+     */
+    C2ParamFieldValuesBuilder<T> &flags(const std::vector<T> &flags, T min = T(0)) {
+        return limitTo(C2SupportedFlags<T>::Flags(flags, min));
+    }
+
+    /**
+     * Restrict (and thus define) the supported values to flags in |values| with at least |min|
+     * set.
+     */
+    C2ParamFieldValuesBuilder<T> &flags(const std::initializer_list<T> flags, T min = T(0)) {
+        return limitTo(C2SupportedFlags<T>::Flags(flags, min));
+    }
+
+    virtual ~C2ParamFieldValuesBuilder();
+
+    // support copy constructor/operator
+    C2ParamFieldValuesBuilder(const C2ParamFieldValuesBuilder &);
+    C2ParamFieldValuesBuilder& operator=(const C2ParamFieldValuesBuilder &);
+
+private:
+    /**
+     * Restrict (and thus define) the supported values to a value set.
+     */
+    C2ParamFieldValuesBuilder<T> &limitTo(const C2SupportedValueSet<T> &limit);
+
+    /**
+     * Restrict (and thus define) the supported values to a value set.
+     */
+    C2ParamFieldValuesBuilder<T> &limitTo(const C2SupportedFlags<T> &limit);
+
+    /**
+     * Restrict (and thus define) the supported values to a range.
+     */
+    C2ParamFieldValuesBuilder<T> &limitTo(const C2SupportedRange<T> &limit);
+
+    struct Impl;
+    std::unique_ptr<Impl> _mImpl;
+};
+
+/**
+ * Builder for a list of setting conflicts.
+ */
+class C2SettingConflictsBuilder {
+public:
+    /**
+     * Creates an empty list of setting conflicts.
+     */
+    C2SettingConflictsBuilder();
+
+    /**
+     * Creates a list containing a single setting conflict.
+     */
+    C2SettingConflictsBuilder(C2ParamFieldValues &&conflict);
+
+    /**
+     * Adds a conflict to the current list of conflicts and returns this
+     */
+    C2SettingConflictsBuilder& with(C2ParamFieldValues &&conflict);
+
+    /**
+     * Gets the current list of conflicts (and moves them out of this builder.)
+     * (this is why it is not const)
+     */
+    std::vector<C2ParamFieldValues> retrieveConflicts();
+
+    /**
+     * Returns whether the current list is empty.
+     */
+    inline bool empty() const { return _mConflicts.empty(); }
+
+    inline operator bool() const { return empty(); }
+
+private:
+    std::vector<C2ParamFieldValues> _mConflicts;
+};
+
+/**
+ * Setting result builder for a parameter.
+ */
+struct C2SettingResultBuilder {
+    /**
+     * Creates a read-only setting result failure.
+     *
+     * This does not take FSV as only the current value of the field is supported.
+     */
+    static C2SettingResult ReadOnly(const C2ParamField &param);
+
+    /**
+     * Creates a bad-value or infoinformational bad-value setting result failure.
+     *
+     * This does not take FSV as the value is outside of the possible values. As such, there are no
+     * conflicts for this case either.
+     */
+    static C2SettingResult BadValue(const C2ParamField &paramField, bool isInfo = false);
+
+    /**
+     * Creates a conflict (or informational conflict) setting result failure.
+     *
+     * This takes FSV so use paramFieldValues and optional conflicts.
+     */
+    static C2SettingResult Conflict(
+            C2ParamFieldValues &&paramFieldValues, C2SettingConflictsBuilder &conflicts,
+            bool isInfo = false);
+
+    // TODO: retrieve results
+
+
+private:
+    C2ParamField _mParamField;
+    C2SettingResult _mResult;
+
+    C2SettingResultBuilder(const C2SettingResultBuilder &) = delete;
+};
+
+/**
+ * Setting results (PLURAL) builder.
+ *
+ * Setting results contain a failure status along with a list of failing fields or params.
+ */
+struct C2SettingResultsBuilder {
+    C2SettingResultsBuilder(const C2SettingResultsBuilder&) = delete;
+    C2SettingResultsBuilder(C2SettingResultsBuilder&&) = default;
+    C2SettingResultsBuilder &operator=(C2SettingResultsBuilder&&) = default;
+
+    /** \returns (default) successful result with no details. */
+    inline static C2SettingResultsBuilder Ok() {
+        return C2SettingResultsBuilder(C2_OK);
+    }
+
+    /** \returns Interface is in bad state, with no further details. */
+    inline static C2SettingResultsBuilder BadState() {
+        return C2SettingResultsBuilder(C2_BAD_STATE);
+    }
+
+    /** \returns Interface connection timed out, with no further details. */
+    inline static C2SettingResultsBuilder TimedOut() {
+        return C2SettingResultsBuilder(C2_TIMED_OUT);
+    }
+
+    /** \returns Interface connection is corrupted, with no further details. */
+    inline static C2SettingResultsBuilder Corrupted() {
+        return C2SettingResultsBuilder(C2_CORRUPTED);
+    }
+
+    inline static C2SettingResultsBuilder NoMemory(C2Param::Index index_ __unused) {
+        // TODO: try to add failure result
+        return C2SettingResultsBuilder(C2_NO_MEMORY);
+    }
+
+    // TODO: this should not be a constructor
+    /** Creates a builder with a single bad value setting result. */
+    C2SettingResultsBuilder(C2SettingResult &&result);
+
+    /** Combines this results with other results. */
+    C2SettingResultsBuilder plus(C2SettingResultsBuilder&& results);
+
+    /** Retrieve (get and move out) failures and return the failure status. */
+    c2_status_t retrieveFailures(std::vector<std::unique_ptr<C2SettingResult>>* const failures);
+
+private:
+    /** Setting results based on a single status. This is used when actual setting could not be
+     *  attempted to get a single C2SettingResult, or when a setting succeeded without
+     *  'complaints'. */
+    C2SettingResultsBuilder(c2_status_t status);
+        // status must be one of OK, BAD_STATE, TIMED_OUT, CORRUPTED or NO_MEMORY
+        // mainly: BLOCKING, BAD_INDEX, BAD_VALUE and NO_MEMORY requires a setting attempt, but
+        // NO_MEMORY may not allow us to create a results structure.
+
+    /**
+     * One of OK, BAD_INDEX, BAD_VALUE, BAD_STATE, NO_MEMORY, TIMED_OUT, BLOCKING or CORRUPTED.
+     */
+    c2_status_t _mStatus __unused;
+
+    /**
+     * Vector of individual setting result details.
+     */
+    std::vector<std::unique_ptr<C2SettingResult>> _mResults;
+};
+
+/**
+ * Utility class to enumerate fields of parameters.
+ */
+struct C2FieldUtils {
+    struct _Inspector;
+
+    /**
+     * An extended field descriptor object with structural information (lineage back to the root of
+     * the param).
+     */
+    struct Info {
+        typedef C2FieldDescriptor::type_t type_t; ///< field type
+        typedef C2FieldDescriptor::NamedValuesType NamedValuesType; ///< named values list type
+
+        /// returns the name of the field
+        C2String name() const;
+
+        /// returns the type of this field
+        type_t type() const;
+
+        /**
+         * Returns the defined name-value pairings for this field. The returned reference is
+         * only valid during the validity of this object
+         */
+        const NamedValuesType &namedValues() const;
+
+        /**
+         * The index of this field. E.g. param.field or param.field[0] has an index of 0, and
+         * param.struct[2].field[3] has an index of 3.
+         */
+        size_t index() const;
+
+        /// returns the length of the field in case it is an array. Returns 0 for
+        /// T[] arrays if this info comes from a C2Param::Index object, and the currently used
+        /// extent if it comes from a C2Param object. Returns 1 for T[1] arrays as well as if the
+        /// field is not an array.
+        size_t extent() const;
+
+        /**
+         * The (structural) depth of this field. E.g. param.field or param.field[0] has a depth of
+         *  0, and param.struct.field or param.struct[0].field[0] has a depth of 1.
+         */
+        size_t depth() const;
+
+        /**
+         * Returns the offset of this field in the parameter in bytes.
+         */
+        size_t offset() const;
+
+        /**
+         * Returns the size of this field in bytes.
+         */
+        size_t size() const;
+
+        /**
+         * The offset of this field's array. E.g. for param.struct[2].field[3] this is the offset
+         * of its smallest sibling: param.struct[2].field[0].
+         */
+        size_t arrayOffset() const;
+
+        /**
+         * Returns the size of this field's array. This is equivalent to extent() * size()
+         */
+        size_t arraySize() const;
+
+        /**
+         * The offset of the base field. The base field is a cousin of the current field where
+         * all indices are 0. E.g. the the base field for param.struct[2].field[3] is
+         * param.struct[0].field[0]. Base fields are used to specify supported values for
+         * all cousin fields.
+         */
+        size_t baseFieldOffset() const;
+
+        /**
+         * Returns whether this field is an arithmetic (integral, counter or float) field.
+         */
+        bool isArithmetic() const;
+
+        /**
+         * Returns whether this field can have a flexible extent. extent() returns the current
+         * extent.
+         */
+        bool isFlexible() const;
+
+        /// returns whether this info is valid
+        inline bool isValid() const { return _mImpl != nullptr; }
+
+        /// returns the info for the parent of this field, or an invalid Info object if it has no
+        /// parents
+        Info parent() const;
+
+        /// returns whether this info is valid
+        inline operator bool() const { return isValid(); }
+
+        struct Impl;
+        Info(std::shared_ptr<Impl>);
+
+    private:
+        std::shared_ptr<Impl> _mImpl;
+        friend struct _Inspector;
+    };
+
+    /**
+     * An (input) iterator object over fields using Info objects.
+     */
+    struct Iterator {
+        typedef Info const value_type;
+        typedef ptrdiff_t difference_type;
+        typedef Info const * pointer;
+        typedef Info const reference;
+        typedef std::input_iterator_tag iterator_category;
+
+        /// return Info at current position
+        virtual reference operator*() const;
+
+        /// move to the next field
+        virtual Iterator& operator++();
+
+        virtual bool operator==(const Iterator &) const;
+        inline bool operator!=(const Iterator &other) const { return !operator==(other); }
+
+        virtual ~Iterator() = default;
+
+        struct Impl;
+        Iterator(std::shared_ptr<Impl>);
+
+    protected:
+        std::shared_ptr<Impl> mImpl;
+    };
+
+    /**
+     * An (input) iterable object representing a list of fields.
+     */
+    struct List {
+        /// returns an iterator to the beginning of the list
+        virtual Iterator begin() const;
+
+        /// returns an iterator to the end of the list
+        virtual Iterator end() const;
+
+        virtual ~List() = default;
+
+        struct Impl;
+        List(std::shared_ptr<Impl>);
+
+    protected:
+        std::shared_ptr<Impl> mImpl;
+    };
+
+    /**
+     * Enumerates all (base) fields at index 0 of the parameter. The order of iteration is the
+     * following:
+     *   Fields of a structure are enumerated in field order. However, sub-fields of a structure
+     *   are enumerated directly after the structure field, and prior to sibling fields.
+     *
+     * In essence the order of enumeration is first by increasing offset, then by decreasing size.
+     *
+     * \param param parameter to enumerate its fields
+     * \param reflector parameter reflector used for enumeration
+     *
+     * \return an iterable object
+     */
+    static List enumerateFields(
+            const C2Param &param,
+            const std::shared_ptr<C2ParamReflector> &reflector);
+
+    /**
+     * Enumerates all cousin fields up to depth - level for a field. If level is 0, it enumerates
+     * only the field. For level 1, it enumerates all fields in its current array (which may be
+     * itself if extent is 1). The order of iteration is by increasing field offset.
+     */
+    static List enumerateCousins(
+            const Info &field,
+            uint32_t level = ~0);
+
+    /**
+     * Locates the field in a parameter and returns a list of 2 elements - the most-specific field
+     * array of the parameter that contains the entire field. If the field is not a valid field
+     * specifier for this parameter (e.g. it is outside the bounds of the parameter), it returns
+     * an empty list.
+     */
+    static std::vector<Info> locateField(
+            const C2Param &param, const _C2FieldId &field,
+            const std::shared_ptr<C2ParamReflector> &reflector);
+
+    static std::vector<Info> locateField(
+            const C2ParamField &pf, const std::shared_ptr<C2ParamReflector> &reflector);
+
+};
+
+#include <util/C2Debug-interface.h>
+
+#endif  // C2UTILS_INTERFACE_UTILS_H_
diff --git a/media/codec2/vndk/include/util/C2ParamUtils.h b/media/codec2/vndk/include/util/C2ParamUtils.h
new file mode 100644
index 0000000..b51bd94
--- /dev/null
+++ b/media/codec2/vndk/include/util/C2ParamUtils.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#ifndef C2UTILS_PARAM_UTILS_H_
+#define C2UTILS_PARAM_UTILS_H_
+
+#include <utility>
+#include <vector>
+
+/** \file
+ * Utilities for parameter handling to be used by Codec2 implementations.
+ */
+
+/// \cond INTERNAL
+
+struct C2Param;
+
+class C2ParamUtils {
+    friend class C2UtilTest_ParamUtilsTest_Test;
+
+public:
+
+    /// safe(r) parsing from parameter blob
+    static
+    C2Param *ParseFirst(const uint8_t *blob, size_t size);
+};
+
+/// \endcond
+
+#endif  // C2UTILS_PARAM_UTILS_H_
+
diff --git a/media/codec2/vndk/internal/C2BlockInternal.h b/media/codec2/vndk/internal/C2BlockInternal.h
new file mode 100644
index 0000000..2abf3ac
--- /dev/null
+++ b/media/codec2/vndk/internal/C2BlockInternal.h
@@ -0,0 +1,337 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#ifndef ANDROID_STAGEFRIGHT_C2BLOCK_INTERNAL_H_
+#define ANDROID_STAGEFRIGHT_C2BLOCK_INTERNAL_H_
+
+#include <android/hardware/graphics/bufferqueue/1.0/IGraphicBufferProducer.h>
+
+#include <C2Buffer.h>
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace bufferpool {
+
+struct BufferPoolData;
+
+}
+}
+}
+}
+
+/**
+ * Stores informations from C2BlockPool implementations which are required by C2Block.
+ */
+struct C2_HIDE _C2BlockPoolData {
+    enum type_t : int {
+        TYPE_BUFFERPOOL = 0,
+        TYPE_BUFFERQUEUE,
+    };
+
+    virtual type_t getType() const = 0;
+
+protected:
+    _C2BlockPoolData() = default;
+
+    virtual ~_C2BlockPoolData() = default;
+};
+
+struct C2BufferQueueBlockPoolData;
+
+/**
+ * Internal only interface for creating blocks by block pool/buffer passing implementations.
+ *
+ * \todo this must be hidden
+ */
+struct _C2BlockFactory {
+    /**
+     * Create a linear block from an allocation for an allotted range.
+     *
+     * \param alloc parent allocation
+     * \param data  blockpool data
+     * \param offset allotted range offset
+     * \param size  allotted size
+     *
+     * \return shared pointer to the linear block. nullptr if there was not enough memory to
+     *         create this block.
+     */
+    static
+    std::shared_ptr<C2LinearBlock> CreateLinearBlock(
+            const std::shared_ptr<C2LinearAllocation> &alloc,
+            const std::shared_ptr<_C2BlockPoolData> &data = nullptr,
+            size_t offset = 0,
+            size_t size = ~(size_t)0);
+
+    /**
+     * Create a graphic block from an allocation for an allotted section.
+     *
+     * \param alloc parent allocation
+     * \param data  blockpool data
+     * \param crop  allotted crop region
+     *
+     * \return shared pointer to the graphic block. nullptr if there was not enough memory to
+     *         create this block.
+     */
+    static
+    std::shared_ptr<C2GraphicBlock> CreateGraphicBlock(
+            const std::shared_ptr<C2GraphicAllocation> &alloc,
+            const std::shared_ptr<_C2BlockPoolData> &data = nullptr,
+            const C2Rect &allottedCrop = C2Rect(~0u, ~0u));
+
+    /**
+     * Return a block pool data from 1D block.
+     *
+     * \param shared pointer to the 1D block which is already created.
+     */
+    static
+    std::shared_ptr<_C2BlockPoolData> GetLinearBlockPoolData(
+            const C2Block1D& block);
+
+    /**
+     * Return a block pool data from 2D block.
+     *
+     * \param shared pointer to the 2D block which is already created.
+     */
+    static
+    std::shared_ptr<_C2BlockPoolData> GetGraphicBlockPoolData(
+            const C2Block2D& block);
+
+    /**
+     * Create a linear block from the received native handle.
+     *
+     * \param handle    native handle to a linear block
+     *
+     * \return shared pointer to the linear block. nullptr if there was not enough memory to
+     *         create this block.
+     */
+    static
+    std::shared_ptr<C2LinearBlock> CreateLinearBlock(
+            const C2Handle *handle);
+
+    /**
+     * Create a graphic block from the received native handle.
+     *
+     * \param handle    native handle to a graphic block
+     *
+     * \return shared pointer to the graphic block. nullptr if there was not enough memory to
+     *         create this block.
+     */
+    static
+    std::shared_ptr<C2GraphicBlock> CreateGraphicBlock(
+            const C2Handle *handle);
+
+    /**
+     * Create a linear block from the received bufferpool data.
+     *
+     * \param data  bufferpool data to a linear block
+     *
+     * \return shared pointer to the linear block. nullptr if there was not enough memory to
+     *         create this block.
+     */
+    static
+    std::shared_ptr<C2LinearBlock> CreateLinearBlock(
+            const C2Handle *handle,
+            const std::shared_ptr<android::hardware::media::bufferpool::BufferPoolData> &data);
+
+    /**
+     * Create a graphic block from the received bufferpool data.
+     *
+     * \param data  bufferpool data to a graphic block
+     *
+     * \return shared pointer to the graphic block. nullptr if there was not enough memory to
+     *         create this block.
+     */
+    static
+    std::shared_ptr<C2GraphicBlock> CreateGraphicBlock(
+            const C2Handle *handle,
+            const std::shared_ptr<android::hardware::media::bufferpool::BufferPoolData> &data);
+
+    /**
+     * Get bufferpool data from the blockpool data.
+     *
+     * \param poolData          blockpool data
+     * \param bufferPoolData    pointer to bufferpool data where the bufferpool
+     *                          data is stored.
+     *
+     * \return {\code true} when there is valid bufferpool data, {\code false} otherwise.
+     */
+    static
+    bool GetBufferPoolData(
+            const std::shared_ptr<const _C2BlockPoolData> &poolData,
+            std::shared_ptr<android::hardware::media::bufferpool::BufferPoolData> *bufferPoolData);
+
+    /*
+     * Life Cycle Management of BufferQueue-Based Blocks
+     * =================================================
+     *
+     * A block that is created by a bufferqueue-based blockpool requires some
+     * special treatment when it is destroyed. In particular, if the block
+     * corresponds to a held (dequeued/attached) GraphicBuffer in a slot of a
+     * bufferqueue, its destruction should trigger a call to
+     * IGraphicBufferProducer::cancelBuffer(). On the other hand, if the
+     * GraphicBuffer is not held, i.e., if it has been queued or detached,
+     * cancelBuffer() should not be called upon the destruction of the block.
+     *
+     * _C2BlockPoolData created by a bufferqueue-based blockpool includes two
+     * main pieces of information:
+     *   - "held" status: Whether cancelBuffer() should be called upon
+     *     destruction of the block.
+     *   - bufferqueue assignment: The quadruple (igbp, generation, bqId,
+     *     bqSlot), where igbp is the IGraphicBufferProducer instance of the
+     *     bufferqueue, generation is the latest generation number, of the
+     *     bufferqueue, bqId is the globally unique id of the bufferqueue, and
+     *     bqSlot is the slot in the bufferqueue.
+     *
+     * igbp is the instance of IGraphicBufferProducer on which cancelBuffer()
+     * will be called if "held" status is true when the block is destroyed.
+     * (bqSlot is an input to cancelBuffer().) However, only generation, bqId
+     * and bqSlot are retained when a block is transferred from one process to
+     * another. It is the responsibility of both the sending and receiving
+     * processes to maintain consistency of "held" status and igbp. Below are
+     * functions provided for this purpose:
+     *
+     *   - GetBufferQueueData(): Returns generation, bqId and bqSlot.
+     *   - HoldBlockFromBufferQueue(): Sets "held" status to true.
+     *   - YieldBlockToBufferQueue(): Sets "held" status to false.
+     *   - AssignBlockToBufferQueue(): Sets the bufferqueue assignment and
+     *     "held" status.
+     *
+     * All these functions operate on _C2BlockPoolData, which can be obtained by
+     * calling GetGraphicBlockPoolData().
+     *
+     * HoldBlockFromBufferQueue() will mark the block as held, while
+     * YieldBlockToBufferQueue() will do the opposite. These two functions do
+     * not modify the bufferqueue assignment, so it is not wrong to call
+     * HoldBlockFromBufferQueue() after YieldBlockToBufferQueue() if it can be
+     * guaranteed that the block is not destroyed during the period between the
+     * two calls.
+     *
+     * AssingBlockToBufferQueue() has a "held" status as an optional argument.
+     * The default value is true.
+     *
+     * Maintaining Consistency with IGraphicBufferProducer Operations
+     * ==============================================================
+     *
+     * dequeueBuffer()
+     *   - This function is called by the blockpool. It should not be called
+     *     manually. The blockpool will automatically generate the correct
+     *     information for _C2BlockPoolData, with "held" status set to true.
+     *
+     * queueBuffer()
+     *   - After queueBuffer() is called, YieldBlockToBufferQueue() should be
+     *     called.
+     *
+     * attachBuffer()
+     *   - After attachBuffer() is called, AssignBlockToBufferQueue() should be
+     *     called with "held" status set to true.
+     *
+     * detachBuffer()
+     *   - After detachBuffer() is called, HoldBlockFromBufferQueue() should be
+     *     called.
+     */
+
+    /**
+     * Get bufferqueue data from the blockpool data.
+     *
+     * Calling this function with \p generation set to nullptr will return
+     * whether the block comes from a bufferqueue-based blockpool, but will not
+     * fill in the values for \p generation, \p bqId or \p bqSlot.
+     *
+     * \param[in]  poolData   blockpool data.
+     * \param[out] generation Generation number attached to the buffer.
+     * \param[out] bqId       Id of the bufferqueue owning the buffer (block).
+     * \param[out] bqSlot     Slot number of the buffer.
+     *
+     * \return \c true when there is valid bufferqueue data;
+     *         \c false otherwise.
+     */
+    static
+    bool GetBufferQueueData(
+            const std::shared_ptr<_C2BlockPoolData>& poolData,
+            uint32_t* generation = nullptr,
+            uint64_t* bqId = nullptr,
+            int32_t* bqSlot = nullptr);
+
+    /**
+     * Set bufferqueue assignment and "held" status to a block created by a
+     * bufferqueue-based blockpool.
+     *
+     * \param poolData blockpool data associated to the block.
+     * \param igbp       \c IGraphicBufferProducer instance from the designated
+     *                   bufferqueue.
+     * \param generation Generation number that the buffer belongs to.
+     * \param bqId       Id of the bufferqueue that will own the buffer (block).
+     * \param bqSlot     Slot number of the buffer.
+     * \param held       Whether the block is held. This "held" status can be
+     *                   changed later by calling YieldBlockToBufferQueue() or
+     *                   HoldBlockFromBufferQueue().
+     *
+     * \return \c true if \p poolData is valid bufferqueue data;
+     *         \c false otherwise.
+     *
+     * Note: \p generation should match the latest generation number set on the
+     * bufferqueue, and \p bqId should match the unique id for the bufferqueue
+     * (obtainable by calling igbp->getUniqueId()).
+     */
+    static
+    bool AssignBlockToBufferQueue(
+            const std::shared_ptr<_C2BlockPoolData>& poolData,
+            const ::android::sp<::android::hardware::graphics::bufferqueue::
+                                V1_0::IGraphicBufferProducer>& igbp,
+            uint32_t generation,
+            uint64_t bqId,
+            int32_t bqSlot,
+            bool held = true);
+
+    /**
+     * Hold a block from the designated bufferqueue. This causes the destruction
+     * of the block to trigger a call to cancelBuffer().
+     *
+     * This function assumes that \p poolData comes from a bufferqueue-based
+     * block. It does not check if that is the case.
+     *
+     * \param poolData blockpool data associated to the block.
+     * \param igbp     \c IGraphicBufferProducer instance to be assigned to the
+     *                 block. This is not needed when the block is local.
+     *
+     * \return The previous held status.
+     */
+    static
+    bool HoldBlockFromBufferQueue(
+            const std::shared_ptr<_C2BlockPoolData>& poolData,
+            const ::android::sp<::android::hardware::graphics::bufferqueue::
+                                V1_0::IGraphicBufferProducer>& igbp = nullptr);
+
+    /**
+     * Yield a block to the designated bufferqueue. This causes the destruction
+     * of the block not to trigger a call to cancelBuffer();
+     *
+     * This function assumes that \p poolData comes from a bufferqueue-based
+     * block. It does not check if that is the case.
+     *
+     * \param poolData blockpool data associated to the block.
+     *
+     * \return The previous held status.
+     */
+    static
+    bool YieldBlockToBufferQueue(
+            const std::shared_ptr<_C2BlockPoolData>& poolData);
+
+};
+
+#endif // ANDROID_STAGEFRIGHT_C2BLOCK_INTERNAL_H_
+
diff --git a/media/codec2/vndk/internal/C2ParamInternal.h b/media/codec2/vndk/internal/C2ParamInternal.h
new file mode 100644
index 0000000..24a8f27
--- /dev/null
+++ b/media/codec2/vndk/internal/C2ParamInternal.h
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#ifndef ANDROID_STAGEFRIGHT_C2PARAM_INTERNAL_H_
+#define ANDROID_STAGEFRIGHT_C2PARAM_INTERNAL_H_
+
+#include <C2Param.h>
+
+struct C2_HIDE _C2ParamInspector {
+    inline static uint32_t GetOffset(const C2FieldDescriptor &fd) {
+        return fd._mFieldId._mOffset;
+    }
+
+    inline static void SetOffset(C2FieldDescriptor &fd, uint32_t offset) {
+        fd._mFieldId._mOffset = offset;
+    }
+
+    inline static uint32_t GetEndOffset(const C2FieldDescriptor &fd, uint32_t paramSize = 0) {
+        uint32_t endOffset = fd._mFieldId._mOffset + fd._mExtent * fd._mFieldId._mSize;
+        /// for flex parameters return paramSize if given
+        return fd._mExtent ? endOffset : std::max(endOffset, paramSize);
+    }
+
+    inline static uint32_t GetSize(const C2FieldDescriptor &fd) {
+        return fd._mFieldId._mSize;
+    }
+
+    inline static uint32_t GetIndex(const C2ParamField &pf) {
+        return pf._mIndex;
+    }
+
+    inline static uint32_t GetOffset(const C2ParamField &pf) {
+        return pf._mFieldId._mOffset;
+    }
+
+    inline static uint32_t GetSize(const C2ParamField &pf) {
+        return pf._mFieldId._mSize;
+    }
+
+    inline static uint32_t GetOffset(const _C2FieldId &f) {
+        return f._mOffset;
+    }
+
+    inline static uint32_t GetSize(const _C2FieldId &f) {
+        return f._mSize;
+    }
+
+    inline static _C2FieldId GetField(const C2FieldDescriptor &fd) {
+        return fd._mFieldId;
+    }
+
+    inline static uint32_t GetAttrib(const C2ParamDescriptor &pd) {
+        return pd._mAttrib;
+    }
+
+    inline static _C2FieldId GetField(const C2ParamField &pf) {
+        return pf._mFieldId;
+    }
+
+    inline static
+    C2ParamField CreateParamField(C2Param::Index index, uint32_t offset, uint32_t size) {
+        return C2ParamField(index, offset, size);
+    }
+
+    inline static
+    C2ParamField CreateParamField(C2Param::Index index, _C2FieldId field) {
+        return C2ParamField(index, field._mOffset, field._mSize);
+    }
+
+    inline static
+    void TrimParam(C2Param *param, uint32_t newSize) {
+        if (param && *param && param->size() > newSize && newSize >= sizeof(C2Param)) {
+            param->_mSize = newSize;
+        }
+    }
+
+    inline static void AddNamedValues(
+            C2FieldDescriptor &fd, C2FieldDescriptor::NamedValuesType &&namedValues) {
+        fd._mNamedValues = std::move(namedValues);
+    }
+
+    inline static
+    C2StructDescriptor CreateStructDescriptor(C2Param::CoreIndex index,
+                                        std::vector<C2FieldDescriptor> &&fields) {
+        return C2StructDescriptor(index, std::move(fields));
+    }
+
+    inline static
+    C2FieldDescriptor OffsetFieldDescriptor(const C2FieldDescriptor &fd, size_t offset) {
+        return C2FieldDescriptor(fd, offset);
+    }
+
+    // expose attributes
+    typedef C2ParamDescriptor::attrib_t attrib_t;
+};
+
+#endif // ANDROID_STAGEFRIGHT_C2PARAM_INTERNAL_H_
+
diff --git a/media/codec2/vndk/platform/C2BqBuffer.cpp b/media/codec2/vndk/platform/C2BqBuffer.cpp
new file mode 100644
index 0000000..7a26035
--- /dev/null
+++ b/media/codec2/vndk/platform/C2BqBuffer.cpp
@@ -0,0 +1,494 @@
+/*
+ * Copyright (C) 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_NDEBUG 0
+#define LOG_TAG "C2BqBuffer"
+#include <utils/Log.h>
+
+#include <gui/BufferQueueDefs.h>
+#include <list>
+#include <map>
+#include <mutex>
+
+#include <C2AllocatorGralloc.h>
+#include <C2BqBufferPriv.h>
+#include <C2BlockInternal.h>
+
+using ::android::AnwBuffer;
+using ::android::BufferQueueDefs::NUM_BUFFER_SLOTS;
+using ::android::C2AllocatorGralloc;
+using ::android::C2AndroidMemoryUsage;
+using ::android::Fence;
+using ::android::GraphicBuffer;
+using ::android::HGraphicBufferProducer;
+using ::android::IGraphicBufferProducer;
+using ::android::hidl_handle;
+using ::android::sp;
+using ::android::status_t;
+using ::android::wp;
+
+using ::android::hardware::Return;
+using ::android::hardware::graphics::common::V1_0::PixelFormat;
+
+struct C2BufferQueueBlockPoolData : public _C2BlockPoolData {
+
+    bool held;
+    bool local;
+    uint32_t generation;
+    uint64_t bqId;
+    int32_t bqSlot;
+    sp<HGraphicBufferProducer> igbp;
+    std::shared_ptr<C2BufferQueueBlockPool::Impl> localPool;
+
+    virtual type_t getType() const override {
+        return TYPE_BUFFERQUEUE;
+    }
+
+    // Create a remote BlockPoolData.
+    C2BufferQueueBlockPoolData(
+            uint32_t generation, uint64_t bqId, int32_t bqSlot,
+            const sp<HGraphicBufferProducer>& producer = nullptr);
+
+    // Create a local BlockPoolData.
+    C2BufferQueueBlockPoolData(
+            uint32_t generation, uint64_t bqId, int32_t bqSlot,
+            const std::shared_ptr<C2BufferQueueBlockPool::Impl>& pool);
+
+    virtual ~C2BufferQueueBlockPoolData() override;
+
+};
+
+bool _C2BlockFactory::GetBufferQueueData(
+        const std::shared_ptr<_C2BlockPoolData>& data,
+        uint32_t* generation, uint64_t* bqId, int32_t* bqSlot) {
+    if (data && data->getType() == _C2BlockPoolData::TYPE_BUFFERQUEUE) {
+        if (generation) {
+            const std::shared_ptr<C2BufferQueueBlockPoolData> poolData =
+                    std::static_pointer_cast<C2BufferQueueBlockPoolData>(data);
+            *generation = poolData->generation;
+            if (bqId) {
+                *bqId = poolData->bqId;
+            }
+            if (bqSlot) {
+                *bqSlot = poolData->bqSlot;
+            }
+        }
+        return true;
+    }
+    return false;
+}
+
+bool _C2BlockFactory::AssignBlockToBufferQueue(
+        const std::shared_ptr<_C2BlockPoolData>& data,
+        const sp<HGraphicBufferProducer>& igbp,
+        uint32_t generation,
+        uint64_t bqId,
+        int32_t bqSlot,
+        bool held) {
+    if (data && data->getType() == _C2BlockPoolData::TYPE_BUFFERQUEUE) {
+        const std::shared_ptr<C2BufferQueueBlockPoolData> poolData =
+                std::static_pointer_cast<C2BufferQueueBlockPoolData>(data);
+        poolData->igbp = igbp;
+        poolData->generation = generation;
+        poolData->bqId = bqId;
+        poolData->bqSlot = bqSlot;
+        poolData->held = held;
+        return true;
+    }
+    return false;
+}
+
+bool _C2BlockFactory::HoldBlockFromBufferQueue(
+        const std::shared_ptr<_C2BlockPoolData>& data,
+        const sp<HGraphicBufferProducer>& igbp) {
+    const std::shared_ptr<C2BufferQueueBlockPoolData> poolData =
+            std::static_pointer_cast<C2BufferQueueBlockPoolData>(data);
+    if (!poolData->local) {
+        poolData->igbp = igbp;
+    }
+    if (poolData->held) {
+        poolData->held = true;
+        return false;
+    }
+    poolData->held = true;
+    return true;
+}
+
+bool _C2BlockFactory::YieldBlockToBufferQueue(
+        const std::shared_ptr<_C2BlockPoolData>& data) {
+    const std::shared_ptr<C2BufferQueueBlockPoolData> poolData =
+            std::static_pointer_cast<C2BufferQueueBlockPoolData>(data);
+    if (!poolData->held) {
+        poolData->held = false;
+        return false;
+    }
+    poolData->held = false;
+    return true;
+}
+
+std::shared_ptr<C2GraphicBlock> _C2BlockFactory::CreateGraphicBlock(
+        const C2Handle *handle) {
+    // TODO: get proper allocator? and mutex?
+    static std::unique_ptr<C2AllocatorGralloc> sAllocator = std::make_unique<C2AllocatorGralloc>(0);
+
+    std::shared_ptr<C2GraphicAllocation> alloc;
+    if (C2AllocatorGralloc::isValid(handle)) {
+        uint32_t width;
+        uint32_t height;
+        uint32_t format;
+        uint64_t usage;
+        uint32_t stride;
+        uint32_t generation;
+        uint64_t bqId;
+        uint32_t bqSlot;
+        android::_UnwrapNativeCodec2GrallocMetadata(
+                handle, &width, &height, &format, &usage, &stride, &generation, &bqId, &bqSlot);
+        c2_status_t err = sAllocator->priorGraphicAllocation(handle, &alloc);
+        if (err == C2_OK) {
+            std::shared_ptr<C2GraphicBlock> block;
+            if (bqId || bqSlot) {
+                // BQBBP
+                std::shared_ptr<C2BufferQueueBlockPoolData> poolData =
+                        std::make_shared<C2BufferQueueBlockPoolData>(generation,
+                                                                     bqId,
+                                                                     (int32_t)bqSlot);
+                block = _C2BlockFactory::CreateGraphicBlock(alloc, poolData);
+            } else {
+                block = _C2BlockFactory::CreateGraphicBlock(alloc);
+            }
+            return block;
+        }
+    }
+    return nullptr;
+}
+
+class C2BufferQueueBlockPool::Impl
+        : public std::enable_shared_from_this<C2BufferQueueBlockPool::Impl> {
+private:
+    c2_status_t fetchFromIgbp_l(
+            uint32_t width,
+            uint32_t height,
+            uint32_t format,
+            C2MemoryUsage usage,
+            std::shared_ptr<C2GraphicBlock> *block /* nonnull */) {
+        // We have an IGBP now.
+        sp<Fence> fence = new Fence();
+        C2AndroidMemoryUsage androidUsage = usage;
+        status_t status;
+        PixelFormat pixelFormat = static_cast<PixelFormat>(format);
+        int slot;
+        ALOGV("tries to dequeue buffer");
+        Return<void> transStatus = mProducer->dequeueBuffer(
+                width, height, pixelFormat, androidUsage.asGrallocUsage(), true,
+                [&status, &slot, &fence](
+                        int32_t tStatus, int32_t tSlot, hidl_handle const& tFence,
+                        HGraphicBufferProducer::FrameEventHistoryDelta const& tTs) {
+                    status = tStatus;
+                    slot = tSlot;
+                    if (!android::conversion::convertTo(fence.get(), tFence) &&
+                            status == android::NO_ERROR) {
+                        status = android::BAD_VALUE;
+                    }
+                    (void) tTs;
+                });
+        // dequeueBuffer returns flag.
+        if (!transStatus.isOk() || status < android::OK) {
+            ALOGD("cannot dequeue buffer %d", status);
+            if (transStatus.isOk() && status == android::INVALID_OPERATION) {
+              // Too many buffer dequeued. retrying after some time is required.
+              return C2_TIMED_OUT;
+            } else {
+              return C2_BAD_VALUE;
+            }
+        }
+        ALOGV("dequeued a buffer successfully");
+        native_handle_t* nh = nullptr;
+        hidl_handle fenceHandle;
+        if (fence) {
+            android::conversion::wrapAs(&fenceHandle, &nh, *fence);
+        }
+        if (fence) {
+            static constexpr int kFenceWaitTimeMs = 10;
+
+            status_t status = fence->wait(kFenceWaitTimeMs);
+            if (status == -ETIME) {
+                // fence is not signalled yet.
+                (void)mProducer->cancelBuffer(slot, fenceHandle).isOk();
+                return C2_TIMED_OUT;
+            }
+            if (status != android::NO_ERROR) {
+                ALOGD("buffer fence wait error %d", status);
+                (void)mProducer->cancelBuffer(slot, fenceHandle).isOk();
+                return C2_BAD_VALUE;
+            } else if (mRenderCallback) {
+                nsecs_t signalTime = fence->getSignalTime();
+                if (signalTime >= 0 && signalTime < INT64_MAX) {
+                    mRenderCallback(mProducerId, slot, signalTime);
+                } else {
+                    ALOGV("got fence signal time of %lld", (long long)signalTime);
+                }
+            }
+        }
+
+        sp<GraphicBuffer> &slotBuffer = mBuffers[slot];
+        if (status & IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION || !slotBuffer) {
+            if (!slotBuffer) {
+                slotBuffer = new GraphicBuffer();
+            }
+            // N.B. This assumes requestBuffer# returns an existing allocation
+            // instead of a new allocation.
+            Return<void> transStatus = mProducer->requestBuffer(
+                    slot,
+                    [&status, &slotBuffer](int32_t tStatus, AnwBuffer const& tBuffer){
+                        status = tStatus;
+                        if (!android::conversion::convertTo(slotBuffer.get(), tBuffer) &&
+                                status == android::NO_ERROR) {
+                            status = android::BAD_VALUE;
+                        }
+                    });
+
+            if (!transStatus.isOk()) {
+                return C2_BAD_VALUE;
+            } else if (status != android::NO_ERROR) {
+                slotBuffer.clear();
+                (void)mProducer->cancelBuffer(slot, fenceHandle).isOk();
+                return C2_BAD_VALUE;
+            }
+        }
+        if (slotBuffer) {
+            native_handle_t *grallocHandle = native_handle_clone(slotBuffer->handle);
+
+            if (grallocHandle) {
+                ALOGV("buffer wraps %llu %d", (unsigned long long)mProducerId, slot);
+                C2Handle *c2Handle = android::WrapNativeCodec2GrallocHandle(
+                        grallocHandle,
+                        slotBuffer->width,
+                        slotBuffer->height,
+                        slotBuffer->format,
+                        slotBuffer->usage,
+                        slotBuffer->stride,
+                        slotBuffer->getGenerationNumber(),
+                        mProducerId, slot);
+                if (c2Handle) {
+                    // Moved everything to c2Handle.
+                    native_handle_delete(grallocHandle);
+                    std::shared_ptr<C2GraphicAllocation> alloc;
+                    c2_status_t err = mAllocator->priorGraphicAllocation(c2Handle, &alloc);
+                    if (err != C2_OK) {
+                        return err;
+                    }
+                    std::shared_ptr<C2BufferQueueBlockPoolData> poolData =
+                            std::make_shared<C2BufferQueueBlockPoolData>(
+                                    slotBuffer->getGenerationNumber(),
+                                    mProducerId, slot, shared_from_this());
+                    *block = _C2BlockFactory::CreateGraphicBlock(alloc, poolData);
+                    return C2_OK;
+                }
+                native_handle_close(grallocHandle);
+                native_handle_delete(grallocHandle);
+            }
+            // Block was not created. call requestBuffer# again next time.
+            slotBuffer.clear();
+            (void)mProducer->cancelBuffer(slot, fenceHandle).isOk();
+        }
+        return C2_BAD_VALUE;
+    }
+
+public:
+    Impl(const std::shared_ptr<C2Allocator> &allocator)
+        : mInit(C2_OK), mProducerId(0), mAllocator(allocator) {
+    }
+
+    ~Impl() {
+        bool noInit = false;
+        for (int i = 0; i < NUM_BUFFER_SLOTS; ++i) {
+            if (!noInit && mProducer) {
+                Return<int32_t> transResult =
+                        mProducer->detachBuffer(static_cast<int32_t>(i));
+                noInit = !transResult.isOk() ||
+                         static_cast<int32_t>(transResult) == android::NO_INIT;
+            }
+            mBuffers[i].clear();
+        }
+    }
+
+    c2_status_t fetchGraphicBlock(
+            uint32_t width,
+            uint32_t height,
+            uint32_t format,
+            C2MemoryUsage usage,
+            std::shared_ptr<C2GraphicBlock> *block /* nonnull */) {
+        block->reset();
+        if (mInit != C2_OK) {
+            return mInit;
+        }
+
+        static int kMaxIgbpRetry = 20; // TODO: small number can cause crash in releasing.
+        static int kMaxIgbpRetryDelayUs = 10000;
+
+        int curTry = 0;
+
+        while (curTry++ < kMaxIgbpRetry) {
+            std::unique_lock<std::mutex> lock(mMutex);
+            // TODO: return C2_NO_INIT
+            if (mProducerId == 0) {
+                std::shared_ptr<C2GraphicAllocation> alloc;
+                c2_status_t err = mAllocator->newGraphicAllocation(
+                        width, height, format, usage, &alloc);
+                if (err != C2_OK) {
+                    return err;
+                }
+                std::shared_ptr<C2BufferQueueBlockPoolData> poolData =
+                        std::make_shared<C2BufferQueueBlockPoolData>(
+                                0, (uint64_t)0, ~0, shared_from_this());
+                // TODO: config?
+                *block = _C2BlockFactory::CreateGraphicBlock(alloc, poolData);
+                ALOGV("allocated a buffer successfully");
+
+                return C2_OK;
+            }
+            c2_status_t status = fetchFromIgbp_l(width, height, format, usage, block);
+            if (status == C2_TIMED_OUT) {
+                lock.unlock();
+                ::usleep(kMaxIgbpRetryDelayUs);
+                continue;
+            }
+            return status;
+        }
+        return C2_TIMED_OUT;
+    }
+
+    void setRenderCallback(const OnRenderCallback &renderCallback) {
+        std::lock_guard<std::mutex> lock(mMutex);
+        mRenderCallback = renderCallback;
+    }
+
+    void configureProducer(const sp<HGraphicBufferProducer> &producer) {
+        int32_t status = android::OK;
+        uint64_t producerId = 0;
+        if (producer) {
+            Return<void> transStatus = producer->getUniqueId(
+                    [&status, &producerId](int32_t tStatus, int64_t tProducerId) {
+                        status = tStatus;
+                        producerId = tProducerId;
+                    });
+            if (!transStatus.isOk()) {
+                ALOGD("configureProducer -- failed to connect to the producer");
+                return;
+            }
+        }
+        {
+            std::lock_guard<std::mutex> lock(mMutex);
+            bool noInit = false;
+            for (int i = 0; i < NUM_BUFFER_SLOTS; ++i) {
+                if (!noInit && mProducer) {
+                    Return<int32_t> transResult =
+                            mProducer->detachBuffer(static_cast<int32_t>(i));
+                    noInit = !transResult.isOk() ||
+                             static_cast<int32_t>(transResult) == android::NO_INIT;
+                }
+                mBuffers[i].clear();
+            }
+            if (producer && status == android::OK) {
+                mProducer = producer;
+                mProducerId = producerId;
+            } else {
+                mProducer = nullptr;
+                mProducerId = 0;
+            }
+        }
+    }
+
+private:
+    friend struct C2BufferQueueBlockPoolData;
+
+    void cancel(uint64_t igbp_id, int32_t igbp_slot) {
+        std::lock_guard<std::mutex> lock(mMutex);
+        if (igbp_id == mProducerId && mProducer) {
+            (void)mProducer->cancelBuffer(igbp_slot, nullptr).isOk();
+        }
+    }
+
+    c2_status_t mInit;
+    uint64_t mProducerId;
+    OnRenderCallback mRenderCallback;
+
+    const std::shared_ptr<C2Allocator> mAllocator;
+
+    std::mutex mMutex;
+    sp<HGraphicBufferProducer> mProducer;
+
+    sp<GraphicBuffer> mBuffers[NUM_BUFFER_SLOTS];
+};
+
+C2BufferQueueBlockPoolData::C2BufferQueueBlockPoolData(
+        uint32_t generation, uint64_t bqId, int32_t bqSlot,
+        const sp<HGraphicBufferProducer>& producer) :
+        held(producer && bqId != 0), local(false),
+        generation(generation), bqId(bqId), bqSlot(bqSlot),
+        igbp(producer),
+        localPool() {
+}
+
+C2BufferQueueBlockPoolData::C2BufferQueueBlockPoolData(
+        uint32_t generation, uint64_t bqId, int32_t bqSlot,
+        const std::shared_ptr<C2BufferQueueBlockPool::Impl>& pool) :
+        held(true), local(true),
+        generation(generation), bqId(bqId), bqSlot(bqSlot),
+        igbp(pool ? pool->mProducer : nullptr),
+        localPool(pool) {
+}
+
+C2BufferQueueBlockPoolData::~C2BufferQueueBlockPoolData() {
+    if (!held || bqId == 0) {
+        return;
+    }
+    if (local && localPool) {
+        localPool->cancel(bqId, bqSlot);
+    } else if (igbp) {
+        igbp->cancelBuffer(bqSlot, nullptr);
+    }
+}
+
+C2BufferQueueBlockPool::C2BufferQueueBlockPool(
+        const std::shared_ptr<C2Allocator> &allocator, const local_id_t localId)
+        : mAllocator(allocator), mLocalId(localId), mImpl(new Impl(allocator)) {}
+
+C2BufferQueueBlockPool::~C2BufferQueueBlockPool() {}
+
+c2_status_t C2BufferQueueBlockPool::fetchGraphicBlock(
+        uint32_t width,
+        uint32_t height,
+        uint32_t format,
+        C2MemoryUsage usage,
+        std::shared_ptr<C2GraphicBlock> *block /* nonnull */) {
+    if (mImpl) {
+        return mImpl->fetchGraphicBlock(width, height, format, usage, block);
+    }
+    return C2_CORRUPTED;
+}
+
+void C2BufferQueueBlockPool::configureProducer(const sp<HGraphicBufferProducer> &producer) {
+    if (mImpl) {
+        mImpl->configureProducer(producer);
+    }
+}
+
+void C2BufferQueueBlockPool::setRenderCallback(const OnRenderCallback &renderCallback) {
+    if (mImpl) {
+        mImpl->setRenderCallback(renderCallback);
+    }
+}
diff --git a/media/codec2/vndk/util/C2Debug.cpp b/media/codec2/vndk/util/C2Debug.cpp
new file mode 100644
index 0000000..b4aa719
--- /dev/null
+++ b/media/codec2/vndk/util/C2Debug.cpp
@@ -0,0 +1,279 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#include <C2ParamInternal.h>
+#include <util/C2Debug-interface.h>
+#include <util/C2Debug-param.h>
+#include <util/C2InterfaceUtils.h>
+
+#include <iostream>
+
+#include <android-base/stringprintf.h>
+
+using android::base::StringPrintf;
+
+/* -------------------------------- asString -------------------------------- */
+
+const char *asString(c2_status_t i, const char *def) {
+    switch (i) {
+        case C2_OK:        return "OK";
+        case C2_BAD_VALUE: return "BAD_VALUE";
+        case C2_BAD_INDEX: return "BAD_INDEX";
+        case C2_CANNOT_DO: return "CANNOT_DO";
+        case C2_DUPLICATE: return "DUPLICATE";
+        case C2_NOT_FOUND: return "NOT_FOUND";
+        case C2_BAD_STATE: return "BAD_STATE";
+        case C2_BLOCKING:  return "BLOCKING";
+        case C2_CANCELED:  return "CANCELED";
+        case C2_NO_MEMORY: return "NO_MEMORY";
+        case C2_REFUSED:   return "REFUSED";
+        case C2_TIMED_OUT: return "TIMED_OUT";
+        case C2_OMITTED:   return "OMITTED";
+        case C2_CORRUPTED: return "CORRUPTED";
+        case C2_NO_INIT:   return "NO_INIT";
+        default:           return def;
+    }
+}
+
+const char *asString(C2FieldDescriptor::type_t i, const char *def) {
+    switch (i) {
+        case C2FieldDescriptor::BLOB:   return "u8";
+        case C2FieldDescriptor::CNTR32: return "c32";
+        case C2FieldDescriptor::CNTR64: return "c64";
+        case C2FieldDescriptor::FLOAT:  return "fp";
+        case C2FieldDescriptor::INT32:  return "i32";
+        case C2FieldDescriptor::INT64:  return "i64";
+        case C2FieldDescriptor::STRING: return "chr";
+        case C2FieldDescriptor::UINT32: return "u32";
+        case C2FieldDescriptor::UINT64: return "u64";
+        default: return (i & C2FieldDescriptor::STRUCT_FLAG) ? "struct" : def;
+    }
+}
+
+/* ------------------------------ C2ParamField ------------------------------ */
+
+static std::string attribParamCoreIndex(const C2Param::CoreIndex &i) {
+    return StringPrintf("%c%c%03x",
+            i.isFlexible() ? 'F' : '-',
+            i.isVendor() ? 'V' : '-',
+            i.coreIndex());
+}
+
+static std::string attribParamIndex(
+        const C2Param::Type &i, bool addStream, unsigned streamId) {
+    std::string v = StringPrintf("%c%c",
+            i.forInput() ? 'I' : i.forOutput() ? 'O' : '-',
+            i.forStream() ? 'S' : i.forPort() ? 'P' : 'G');
+    if (addStream) {
+        if (i.forStream()) {
+            v += StringPrintf("%02d", streamId);
+        } else {
+            v += "--";
+        }
+    }
+
+    return v
+            + StringPrintf("%c ",
+                       i.kind() == C2Param::STRUCT  ? 'S' :
+                       i.kind() == C2Param::INFO    ? 'i' :
+                       i.kind() == C2Param::TUNING  ? 't' :
+                       i.kind() == C2Param::SETTING ? 's' :
+                       i.kind() == C2Param::NONE    ? '-' : '?')
+            + attribParamCoreIndex(i);
+}
+
+std::ostream& operator<<(std::ostream& os, const C2Param::CoreIndex &i) {
+    return os << "Param::CoreIndex(" << attribParamCoreIndex(i) << ")";
+}
+
+std::ostream& operator<<(std::ostream& os, const C2Param::Type &i) {
+    return os << StringPrintf("Param::Type(%08x: ", i.type())
+            << attribParamIndex(i, false, 0) << ")";
+}
+
+std::ostream& operator<<(std::ostream& os, const C2Param::Index &i) {
+    return os << StringPrintf("Param::Index(%08x: ", (uint32_t)i)
+            << attribParamIndex(i, true, i.stream()) << ")";
+}
+
+static std::string attribFieldId(const _C2FieldId &i) {
+    return StringPrintf("Field(@%02x+%02x)",
+            _C2ParamInspector::GetOffset(i),
+            _C2ParamInspector::GetSize(i));
+}
+
+
+std::ostream& operator<<(std::ostream& os, const _C2FieldId &i) {
+    return os << "<" << attribFieldId(i) << ">";
+}
+
+
+std::ostream& operator<<(std::ostream& os, const C2FieldDescriptor &i) {
+    os << attribFieldId(_C2ParamInspector::GetField(i)) << " ";
+    if (i.namedValues().size()) {
+        os << "enum ";
+    }
+    return os << asString(i.type()) << " " << i.name()
+            << StringPrintf("[%zu]", i.extent());
+}
+
+
+std::ostream& operator<<(std::ostream& os, const C2ParamField &i) {
+    os << "<" << C2Param::Index(_C2ParamInspector::GetIndex(i))
+            << StringPrintf("::Field(@%02x+%02x)>",
+                            _C2ParamInspector::GetOffset(i),
+                            _C2ParamInspector::GetSize(i));
+    return os;
+}
+
+
+/* -------------------------- _C2FieldValueHelper -------------------------- */
+
+std::ostream& _C2FieldValueHelper<char>::put(std::ostream &os, const C2Value::Primitive &p) {
+    if (isprint(p.i32)) {
+        return os << StringPrintf("'%c'", p.i32);
+    } else {
+        return os << StringPrintf("'\\x%02x'", (uint32_t)p.i32);
+    }
+}
+
+std::ostream& _C2FieldValueHelper<uint8_t>::put(std::ostream &os, const C2Value::Primitive &p) {
+    return os << StringPrintf("0x%02x", p.u32);
+}
+
+/* ---------------------- C2FieldSupportedValuesHelper ---------------------- */
+
+template<typename T>
+std::ostream& operator<<(std::ostream &os, const c2_cntr_t<T> &v) {
+    return os << "ctr(" << v.peeku() << ")";
+}
+
+template<typename T>
+std::ostream& operator<<(std::ostream& os, const C2SupportedRange<T> &i) {
+    os << "Range(";
+    _C2FieldValueHelper<T>::put(os, i.min());
+    os << "..";
+    _C2FieldValueHelper<T>::put(os, i.max());
+    os << " *= " << i.num() << " /= " << i.denom() << " += " << i.step() << ")";
+    return os;
+}
+template std::ostream& operator<<(std::ostream& os, const C2SupportedRange<char> &i);
+template std::ostream& operator<<(std::ostream& os, const C2SupportedRange<uint8_t> &i);
+template std::ostream& operator<<(std::ostream& os, const C2SupportedRange<int32_t> &i);
+template std::ostream& operator<<(std::ostream& os, const C2SupportedRange<uint32_t> &i);
+//template std::ostream& operator<<(std::ostream& os, const C2SupportedRange<c2_cntr32_t> &i);
+template std::ostream& operator<<(std::ostream& os, const C2SupportedRange<int64_t> &i);
+template std::ostream& operator<<(std::ostream& os, const C2SupportedRange<uint64_t> &i);
+//template std::ostream& operator<<(std::ostream& os, const C2SupportedRange<c2_cntr64_t> &i);
+template std::ostream& operator<<(std::ostream& os, const C2SupportedRange<float> &i);
+
+template<typename T>
+std::ostream& operator<<(std::ostream& os, const C2SupportedFlags<T> &i) {
+    os << "Flags[";
+    if (!i.isEmpty()) {
+        os << "min=";
+        _C2FieldValueHelper<T>::put(os, i.min());
+    }
+    bool comma = false;
+    for (const T &v : i.flags()) {
+        if (comma) {
+            os << ", ";
+        }
+        _C2FieldValueHelper<T>::put(os, v);
+        comma = true;
+    }
+    os << "]";
+    return os;
+}
+template std::ostream& operator<<(std::ostream& os, const C2SupportedFlags<char> &i);
+template std::ostream& operator<<(std::ostream& os, const C2SupportedFlags<uint8_t> &i);
+template std::ostream& operator<<(std::ostream& os, const C2SupportedFlags<int32_t> &i);
+template std::ostream& operator<<(std::ostream& os, const C2SupportedFlags<uint32_t> &i);
+//template std::ostream& operator<<(std::ostream& os, const C2SupportedFlags<c2_cntr32_t> &i);
+template std::ostream& operator<<(std::ostream& os, const C2SupportedFlags<int64_t> &i);
+template std::ostream& operator<<(std::ostream& os, const C2SupportedFlags<uint64_t> &i);
+//template std::ostream& operator<<(std::ostream& os, const C2SupportedFlags<c2_cntr64_t> &i);
+template std::ostream& operator<<(std::ostream& os, const C2SupportedFlags<float> &i);
+
+template<typename T>
+std::ostream& operator<<(std::ostream& os, const C2SupportedValueSet<T> &i) {
+    os << "Values[";
+    bool comma = false;
+    for (const T &v : i.values()) {
+        if (comma) {
+            os << ", ";
+        }
+        _C2FieldValueHelper<T>::put(os, v);
+        comma = true;
+    }
+    os << "]";
+    return os;
+}
+template std::ostream& operator<<(std::ostream& os, const C2SupportedValueSet<char> &i);
+template std::ostream& operator<<(std::ostream& os, const C2SupportedValueSet<uint8_t> &i);
+template std::ostream& operator<<(std::ostream& os, const C2SupportedValueSet<int32_t> &i);
+template std::ostream& operator<<(std::ostream& os, const C2SupportedValueSet<uint32_t> &i);
+//template std::ostream& operator<<(std::ostream& os, const C2SupportedValueSet<c2_cntr32_t> &i);
+template std::ostream& operator<<(std::ostream& os, const C2SupportedValueSet<int64_t> &i);
+template std::ostream& operator<<(std::ostream& os, const C2SupportedValueSet<uint64_t> &i);
+//template std::ostream& operator<<(std::ostream& os, const C2SupportedValueSet<c2_cntr64_t> &i);
+template std::ostream& operator<<(std::ostream& os, const C2SupportedValueSet<float> &i);
+
+template<typename T>
+struct C2FieldSupportedValuesHelper<T>::Impl {
+    Impl(const C2FieldSupportedValues &values);
+
+private:
+    typedef typename _C2FieldValueHelper<T>::ValueType ValueType;
+    C2FieldSupportedValues::type_t _mType;
+    C2SupportedRange<ValueType> _mRange;
+    C2SupportedValueSet<ValueType> _mValues;
+    C2SupportedFlags<ValueType> _mFlags;
+
+public:
+//    friend std::ostream& operator<< <T>(std::ostream& os, const C2FieldSupportedValuesHelper<T>::Impl &i);
+//    friend std::ostream& operator<<(std::ostream& os, const Impl &i);
+    std::ostream& streamOut(std::ostream& os) const;
+};
+
+template<typename T>
+std::ostream& C2FieldSupportedValuesHelper<T>::Impl::streamOut(std::ostream& os) const {
+    if (_mType == C2FieldSupportedValues::RANGE) {
+        os << _mRange;
+    } else if (_mType == C2FieldSupportedValues::VALUES) {
+        os << _mValues;
+    } else if (_mType == C2FieldSupportedValues::FLAGS) {
+        os << _mFlags;
+    } else {
+        os << "Unknown FSV type: " << (uint32_t)_mType;
+    }
+    return os;
+}
+
+template<typename T>
+std::ostream& operator<<(std::ostream& os, const C2FieldSupportedValuesHelper<T> &i) {
+    return i._mImpl->streamOut(os);
+}
+template std::ostream& operator<<(std::ostream& os, const C2FieldSupportedValuesHelper<char> &i);
+template std::ostream& operator<<(std::ostream& os, const C2FieldSupportedValuesHelper<uint8_t> &i);
+template std::ostream& operator<<(std::ostream& os, const C2FieldSupportedValuesHelper<int32_t> &i);
+template std::ostream& operator<<(std::ostream& os, const C2FieldSupportedValuesHelper<uint32_t> &i);
+//template std::ostream& operator<<(std::ostream& os, const C2FieldSupportedValuesHelper<c2_cntr32_t> &i);
+template std::ostream& operator<<(std::ostream& os, const C2FieldSupportedValuesHelper<int64_t> &i);
+template std::ostream& operator<<(std::ostream& os, const C2FieldSupportedValuesHelper<uint64_t> &i);
+//template std::ostream& operator<<(std::ostream& os, const C2FieldSupportedValuesHelper<c2_cntr64_t> &i);
+template std::ostream& operator<<(std::ostream& os, const C2FieldSupportedValuesHelper<float> &i);
+
diff --git a/media/codec2/vndk/util/C2InterfaceHelper.cpp b/media/codec2/vndk/util/C2InterfaceHelper.cpp
new file mode 100644
index 0000000..e447fbe
--- /dev/null
+++ b/media/codec2/vndk/util/C2InterfaceHelper.cpp
@@ -0,0 +1,841 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#include <C2Debug.h>
+#include <C2ParamInternal.h>
+#include <util/C2InterfaceHelper.h>
+
+#include <android-base/stringprintf.h>
+
+using ::android::base::StringPrintf;
+
+/* --------------------------------- ReflectorHelper --------------------------------- */
+
+void C2ReflectorHelper::addStructDescriptors(
+        std::vector<C2StructDescriptor> &structs, _Tuple<> *) {
+    std::lock_guard<std::mutex> lock(_mMutex);
+    for (C2StructDescriptor &strukt : structs) {
+        // TODO: check if structure descriptions conflict with existing ones
+        addStructDescriptor(std::move(strukt));
+    }
+}
+
+std::unique_ptr<C2StructDescriptor>
+C2ReflectorHelper::describe(C2Param::CoreIndex paramIndex) const {
+    std::lock_guard<std::mutex> lock(_mMutex);
+    auto it = _mStructs.find(paramIndex);
+    if (it == _mStructs.end()) {
+        return nullptr;
+    } else {
+        return std::make_unique<C2StructDescriptor>(it->second);
+    }
+};
+
+void C2ReflectorHelper::addStructDescriptor(C2StructDescriptor &&strukt) {
+    if (_mStructs.find(strukt.coreIndex()) != _mStructs.end()) {
+        // already added
+        // TODO: validate that descriptor matches stored descriptor
+    }
+    // validate that all struct fields are known to this reflector
+    for (const C2FieldDescriptor &fd : strukt) {
+        if (fd.type() & C2FieldDescriptor::STRUCT_FLAG) {
+            C2Param::CoreIndex coreIndex = fd.type() &~ C2FieldDescriptor::STRUCT_FLAG;
+            if (_mStructs.find(coreIndex) == _mStructs.end()) {
+                C2_LOG(INFO) << "missing struct descriptor #" << coreIndex << " for field "
+                        << fd.name() << " of struct #" << strukt.coreIndex();
+            }
+        }
+    }
+    _mStructs.emplace(strukt.coreIndex(), strukt);
+}
+
+
+/* ---------------------------- ParamHelper ---------------------------- */
+
+class C2InterfaceHelper::ParamHelper::Impl {
+public:
+    Impl(ParamRef param, C2StringLiteral name, C2StructDescriptor &&strukt)
+        : mParam(param), mName(name), _mStruct(strukt) { }
+
+    Impl(Impl&&) = default;
+
+    void addDownDependency(C2Param::Index index) {
+        mDownDependencies.push_back(index);
+    }
+
+    C2InterfaceHelper::ParamHelper::attrib_t& attrib() {
+        return mAttrib;
+    }
+
+    void build() {
+        // move dependencies into descriptor
+        mDescriptor = std::make_shared<C2ParamDescriptor>(
+                index(), (C2ParamDescriptor::attrib_t)mAttrib,
+                std::move(mName), std::move(mDependencies));
+    }
+
+    void createFieldsAndSupportedValues(const std::shared_ptr<C2ParamReflector> &reflector) {
+        for (const C2FieldUtils::Info &f :
+                C2FieldUtils::enumerateFields(*mDefaultValue, reflector)) {
+            if (!f.isArithmetic()) {
+                continue;
+            }
+            std::unique_ptr<C2FieldSupportedValues> fsvPointer;
+
+            // create a breakable structure
+            do {
+                C2FieldSupportedValues fsv;
+                switch (f.type()) {
+                    case C2FieldDescriptor::INT32:  fsv = C2SupportedRange<int32_t>::Any(); break;
+                    case C2FieldDescriptor::UINT32: fsv = C2SupportedRange<uint32_t>::Any(); break;
+                    case C2FieldDescriptor::INT64:  fsv = C2SupportedRange<int64_t>::Any(); break;
+                    case C2FieldDescriptor::UINT64: fsv = C2SupportedRange<uint64_t>::Any(); break;
+                    case C2FieldDescriptor::FLOAT:  fsv = C2SupportedRange<float>::Any(); break;
+                    case C2FieldDescriptor::BLOB:   fsv = C2SupportedRange<uint8_t>::Any(); break;
+                    case C2FieldDescriptor::STRING: fsv = C2SupportedRange<char>::Any(); break;
+                default:
+                    continue; // break out of do {} while
+                }
+                fsvPointer = std::make_unique<C2FieldSupportedValues>(fsv);
+            } while (false);
+
+            mFields.emplace_hint(
+                    mFields.end(),
+                    _C2FieldId(f.offset(), f.size()),
+                    std::make_shared<FieldHelper>(
+                            mParam, _C2FieldId(f.offset(), f.size()), std::move(fsvPointer)));
+        }
+    }
+
+    /**
+     * Finds a field descriptor.
+     */
+    std::shared_ptr<FieldHelper> findField(size_t baseOffs, size_t baseSize) const {
+        auto it = mFields.find(_C2FieldId(baseOffs, baseSize));
+        if (it == mFields.end()) {
+            return nullptr;
+        }
+        return it->second;
+    }
+
+    const std::vector<ParamRef> getDependenciesAsRefs() const {
+        return mDependenciesAsRefs;
+    }
+
+    std::shared_ptr<const C2ParamDescriptor> getDescriptor() const {
+        return mDescriptor;
+    }
+
+    const std::vector<C2Param::Index> getDownDependencies() const {
+        return mDownDependencies;
+    }
+
+    C2Param::Index index() const {
+        if (!mDefaultValue) {
+            fprintf(stderr, "%s missing default value\n", mName.c_str());
+        }
+        return mDefaultValue->index();
+    }
+
+    C2String name() const {
+        return mName;
+    }
+
+    const ParamRef ref() const {
+        return mParam;
+    }
+
+    C2StructDescriptor retrieveStructDescriptor() {
+        return std::move(_mStruct);
+    }
+
+    void setDefaultValue(std::shared_ptr<C2Param> default_) {
+        mDefaultValue = default_;
+    }
+
+    void setDependencies(std::vector<C2Param::Index> indices, std::vector<ParamRef> refs) {
+        mDependencies = indices;
+        mDependenciesAsRefs = refs;
+    }
+
+    void setFields(std::vector<C2ParamFieldValues> &&fields) {
+        // do not allow adding fields multiple times, or to const values
+        if (!mFields.empty()) {
+            C2_LOG(FATAL) << "Trying to add fields to param " << mName << " multiple times";
+        } else if (mAttrib & attrib_t::IS_CONST) {
+            C2_LOG(FATAL) << "Trying to add fields to const param " << mName;
+        }
+
+        for (C2ParamFieldValues &pfv : fields) {
+            mFields.emplace_hint(
+                    mFields.end(),
+                    // _C2FieldId constructor
+                    _C2ParamInspector::GetField(pfv.paramOrField),
+                    // Field constructor
+                    std::make_shared<FieldHelper>(mParam,
+                                            _C2ParamInspector::GetField(pfv.paramOrField),
+                                            std::move(pfv.values)));
+        }
+    }
+
+    void setGetter(std::function<std::shared_ptr<C2Param>(bool)> getter) {
+        mGetter = getter;
+    }
+
+    void setSetter(std::function<C2R(const C2Param *, bool, bool *, Factory &)> setter) {
+        mSetter = setter;
+    }
+
+    c2_status_t trySet(
+            const C2Param *value, bool mayBlock, bool *changed, Factory &f,
+            std::vector<std::unique_ptr<C2SettingResult>>* const failures) {
+        C2R result = mSetter(value, mayBlock, changed, f);
+        return result.retrieveFailures(failures);
+    }
+
+    c2_status_t validate(const std::shared_ptr<C2ParamReflector> &reflector) {
+        if (!mSetter && mFields.empty()) {
+            C2_LOG(WARNING) << "Param " << mName << " has no setter, making it const";
+            // dependencies are empty in this case
+            mAttrib |= attrib_t::IS_CONST;
+        } else if (!mSetter) {
+            C2_LOG(FATAL) << "Param " << mName << " has no setter";
+        }
+
+        if (mAttrib & attrib_t::IS_CONST) {
+            createFieldsAndSupportedValues(reflector);
+        } else {
+            // TODO: update default based on setter and verify that FSV covers the values
+        }
+
+        if (mFields.empty()) {
+            C2_LOG(FATAL) << "Param " << mName << " has no fields";
+        }
+
+        return C2_OK;
+    }
+
+    std::shared_ptr<C2Param> value() {
+        return mParam.get();
+    }
+
+    std::shared_ptr<const C2Param> value() const {
+        return mParam.get();
+    }
+
+private:
+    typedef _C2ParamInspector::attrib_t attrib_t;
+    ParamRef mParam;
+    C2String mName;
+    C2StructDescriptor _mStruct;
+    std::shared_ptr<C2Param> mDefaultValue;
+    attrib_t mAttrib;
+    std::function<C2R(const C2Param *, bool, bool *, Factory &)> mSetter;
+    std::function<std::shared_ptr<C2Param>(bool)> mGetter;
+    std::vector<C2Param::Index> mDependencies;
+    std::vector<ParamRef> mDependenciesAsRefs;
+    std::vector<C2Param::Index> mDownDependencies; // TODO: this does not work for stream dependencies
+    std::map<_C2FieldId, std::shared_ptr<FieldHelper>> mFields;
+    std::shared_ptr<C2ParamDescriptor> mDescriptor;
+};
+
+C2InterfaceHelper::ParamHelper::ParamHelper(
+        ParamRef param, C2StringLiteral name, C2StructDescriptor &&strukt)
+    : mImpl(std::make_unique<C2InterfaceHelper::ParamHelper::Impl>(
+            param, name, std::move(strukt))) { }
+
+C2InterfaceHelper::ParamHelper::ParamHelper(C2InterfaceHelper::ParamHelper &&) = default;
+
+C2InterfaceHelper::ParamHelper::~ParamHelper() = default;
+
+void C2InterfaceHelper::ParamHelper::addDownDependency(C2Param::Index index) {
+    return mImpl->addDownDependency(index);
+}
+
+C2InterfaceHelper::ParamHelper::attrib_t& C2InterfaceHelper::ParamHelper::attrib() {
+    return mImpl->attrib();
+}
+
+std::shared_ptr<C2InterfaceHelper::ParamHelper> C2InterfaceHelper::ParamHelper::build() {
+    mImpl->build();
+    return std::make_shared<C2InterfaceHelper::ParamHelper>(std::move(*this));
+}
+
+std::shared_ptr<C2InterfaceHelper::FieldHelper>
+C2InterfaceHelper::ParamHelper::findField(size_t baseOffs, size_t baseSize) const {
+    return mImpl->findField(baseOffs, baseSize);
+}
+
+const std::vector<C2InterfaceHelper::ParamRef>
+C2InterfaceHelper::ParamHelper::getDependenciesAsRefs() const {
+    return mImpl->getDependenciesAsRefs();
+}
+
+std::shared_ptr<const C2ParamDescriptor>
+C2InterfaceHelper::ParamHelper::getDescriptor() const {
+    return mImpl->getDescriptor();
+}
+
+const std::vector<C2Param::Index> C2InterfaceHelper::ParamHelper::getDownDependencies() const {
+    return mImpl->getDownDependencies();
+}
+
+C2Param::Index C2InterfaceHelper::ParamHelper::index() const {
+    return mImpl->index();
+}
+
+C2String C2InterfaceHelper::ParamHelper::name() const {
+    return mImpl->name();
+}
+
+const C2InterfaceHelper::ParamRef C2InterfaceHelper::ParamHelper::ref() const {
+    return mImpl->ref();
+}
+
+C2StructDescriptor C2InterfaceHelper::ParamHelper::retrieveStructDescriptor() {
+    return mImpl->retrieveStructDescriptor();
+}
+
+void C2InterfaceHelper::ParamHelper::setDefaultValue(std::shared_ptr<C2Param> default_) {
+    mImpl->setDefaultValue(default_);
+}
+
+void C2InterfaceHelper::ParamHelper::setDependencies(
+        std::vector<C2Param::Index> indices, std::vector<ParamRef> refs) {
+    mImpl->setDependencies(indices, refs);
+}
+
+void C2InterfaceHelper::ParamHelper::setFields(std::vector<C2ParamFieldValues> &&fields) {
+    return mImpl->setFields(std::move(fields));
+}
+
+void C2InterfaceHelper::ParamHelper::setGetter(
+        std::function<std::shared_ptr<C2Param>(bool)> getter) {
+    mImpl->setGetter(getter);
+}
+
+void C2InterfaceHelper::ParamHelper::setSetter(
+        std::function<C2R(const C2Param *, bool, bool *, Factory &)> setter) {
+    mImpl->setSetter(setter);
+}
+
+c2_status_t C2InterfaceHelper::ParamHelper::trySet(
+        const C2Param *value, bool mayBlock, bool *changed, Factory &f,
+        std::vector<std::unique_ptr<C2SettingResult>>* const failures) {
+    return mImpl->trySet(value, mayBlock, changed, f, failures);
+}
+
+c2_status_t C2InterfaceHelper::ParamHelper::validate(
+        const std::shared_ptr<C2ParamReflector> &reflector) {
+    return mImpl->validate(reflector);
+}
+
+std::shared_ptr<C2Param> C2InterfaceHelper::ParamHelper::value() {
+    return mImpl->value();
+}
+
+std::shared_ptr<const C2Param> C2InterfaceHelper::ParamHelper::value() const {
+    return mImpl->value();
+}
+
+/* ---------------------------- FieldHelper ---------------------------- */
+
+C2ParamField C2InterfaceHelper::FieldHelper::makeParamField(C2Param::Index index) const {
+    return _C2ParamInspector::CreateParamField(index, mFieldId);
+}
+
+C2InterfaceHelper::FieldHelper::FieldHelper(const ParamRef &param, const _C2FieldId &field,
+            std::unique_ptr<C2FieldSupportedValues> &&values)
+    : mParam(param),
+      mFieldId(field),
+      mPossible(std::move(values)) {
+    C2_LOG(VERBOSE) << "Creating field helper " << field << " "
+            << C2FieldSupportedValuesHelper<uint32_t>(*mPossible);
+}
+
+void C2InterfaceHelper::FieldHelper::setSupportedValues(
+        std::unique_ptr<C2FieldSupportedValues> &&values) {
+    mSupported = std::move(values);
+}
+
+const C2FieldSupportedValues *C2InterfaceHelper::FieldHelper::getSupportedValues() const {
+    return (mSupported ? mSupported : mPossible).get();
+}
+
+const C2FieldSupportedValues *C2InterfaceHelper::FieldHelper::getPossibleValues() const {
+    return mPossible.get();
+}
+
+
+/* ---------------------------- Field ---------------------------- */
+
+/**
+ * Wrapper around field-supported-values builder that gets stored in the
+ * field helper when the builder goes out of scope.
+ */
+template<typename T>
+struct SupportedValuesBuilder : C2ParamFieldValuesBuilder<T> {
+    SupportedValuesBuilder(
+            C2ParamField &field, std::shared_ptr<C2InterfaceHelper::FieldHelper> helper)
+        : C2ParamFieldValuesBuilder<T>(field), _mHelper(helper), _mField(field) {
+    }
+
+    /**
+     * Save builder values on destruction.
+     */
+    virtual ~SupportedValuesBuilder() override {
+        _mHelper->setSupportedValues(std::move(C2ParamFieldValues(*this).values));
+    }
+
+private:
+    std::shared_ptr<C2InterfaceHelper::FieldHelper> _mHelper;
+    C2ParamField _mField;
+};
+
+
+template<typename T>
+C2ParamFieldValuesBuilder<T> C2InterfaceHelper::Field<T>::shouldBe() const {
+    return C2ParamFieldValuesBuilder<T>(_mField);
+}
+
+template<typename T>
+C2ParamFieldValuesBuilder<T> C2InterfaceHelper::Field<T>::mustBe() {
+    return SupportedValuesBuilder<T>(_mField, _mHelper);
+}
+
+/*
+template<typename T> C2SettingResultsBuilder C2InterfaceHelper::Field<T>::validatePossible(T &value)
+const {
+    /// TODO
+    return C2SettingResultsBuilder::Ok();
+}
+*/
+
+template<typename T>
+C2InterfaceHelper::Field<T>::Field(std::shared_ptr<FieldHelper> helper, C2Param::Index index)
+    : _mHelper(helper), _mField(helper->makeParamField(index)) { }
+
+template struct C2InterfaceHelper::Field<uint8_t>;
+template struct C2InterfaceHelper::Field<char>;
+template struct C2InterfaceHelper::Field<int32_t>;
+template struct C2InterfaceHelper::Field<uint32_t>;
+//template struct C2InterfaceHelper::Field<c2_cntr32_t>;
+template struct C2InterfaceHelper::Field<int64_t>;
+template struct C2InterfaceHelper::Field<uint64_t>;
+//template struct C2InterfaceHelper::Field<c2_cntr64_t>;
+template struct C2InterfaceHelper::Field<float>;
+
+/* --------------------------------- Factory --------------------------------- */
+
+struct C2InterfaceHelper::FactoryImpl : public C2InterfaceHelper::Factory {
+    virtual std::shared_ptr<C2ParamReflector> getReflector() const override {
+        return _mReflector;
+    }
+
+    virtual std::shared_ptr<ParamHelper>
+    getParamHelper(const ParamRef &param) const override {
+        return _mParams.find(param)->second;
+    }
+
+public:
+    FactoryImpl(std::shared_ptr<C2ParamReflector> reflector)
+        : _mReflector(reflector) { }
+
+    virtual ~FactoryImpl() = default;
+
+    void addParam(std::shared_ptr<ParamHelper> param) {
+        _mParams.insert({ param->ref(), param });
+        _mIndexToHelper.insert({param->index(), param});
+
+        // add down-dependencies (and validate dependencies as a result)
+        size_t ix = 0;
+        for (const ParamRef &ref : param->getDependenciesAsRefs()) {
+            // dependencies must already be defined
+            if (!_mParams.count(ref)) {
+                C2_LOG(FATAL) << "Parameter " << param->name() << " has a dependency at index "
+                        << ix << " that is not yet defined";
+            }
+            _mParams.find(ref)->second->addDownDependency(param->index());
+            ++ix;
+        }
+
+        _mDependencyIndex.emplace(param->index(), _mDependencyIndex.size());
+    }
+
+    std::shared_ptr<ParamHelper> getParam(C2Param::Index ix) const {
+        // TODO: handle streams separately
+        const auto it = _mIndexToHelper.find(ix);
+        if (it == _mIndexToHelper.end()) {
+            return nullptr;
+        }
+        return it->second;
+    }
+
+    /**
+     * TODO: this could return a copy using proper pointer cast.
+     */
+    std::shared_ptr<C2Param> getParamValue(C2Param::Index ix) const {
+        std::shared_ptr<ParamHelper> helper = getParam(ix);
+        return helper ? helper->value() : nullptr;
+    }
+
+    c2_status_t querySupportedParams(
+            std::vector<std::shared_ptr<C2ParamDescriptor>> *const params) const {
+        for (const auto &it : _mParams) {
+            // TODO: change querySupportedParams signature?
+            params->push_back(
+                    std::const_pointer_cast<C2ParamDescriptor>(it.second->getDescriptor()));
+        }
+        // TODO: handle errors
+        return C2_OK;
+    }
+
+    size_t getDependencyIndex(C2Param::Index ix) {
+        // in this version of the helper there is only a single stream so
+        // we can look up directly by index
+        auto it = _mDependencyIndex.find(ix);
+        return it == _mDependencyIndex.end() ? SIZE_MAX : it->second;
+    }
+
+private:
+    std::map<ParamRef, std::shared_ptr<ParamHelper>> _mParams;
+    std::map<C2Param::Index, std::shared_ptr<ParamHelper>> _mIndexToHelper;
+    std::shared_ptr<C2ParamReflector> _mReflector;
+    std::map<C2Param::Index, size_t> _mDependencyIndex;
+};
+
+/* --------------------------------- Helper --------------------------------- */
+
+namespace {
+
+static std::string asString(C2Param *p) {
+    char addr[20];
+    sprintf(addr, "%p:[", p);
+    std::string v = addr;
+    for (size_t i = 0; i < p->size(); ++i) {
+        char d[4];
+        sprintf(d, " %02x", *(((uint8_t *)p) + i));
+        v += d + (i == 0);
+    }
+    return v + "]";
+}
+
+}
+
+C2InterfaceHelper::C2InterfaceHelper(std::shared_ptr<C2ReflectorHelper> reflector)
+    : mReflector(reflector),
+      _mFactory(std::make_shared<FactoryImpl>(reflector)) { }
+
+
+size_t C2InterfaceHelper::GetBaseOffset(const std::shared_ptr<C2ParamReflector> &reflector,
+        C2Param::CoreIndex index, size_t offset) {
+    std::unique_ptr<C2StructDescriptor> param = reflector->describe(index);
+    if (param == nullptr) {
+        return ~(size_t)0; // param structure not described
+    }
+
+    for (const C2FieldDescriptor &field : *param) {
+        size_t fieldOffset = _C2ParamInspector::GetOffset(field);
+        size_t fieldSize = _C2ParamInspector::GetSize(field);
+        size_t fieldExtent = field.extent();
+        if (offset < fieldOffset) {
+            return ~(size_t)0; // not found
+        }
+        if (offset == fieldOffset) {
+            // exact match
+            return offset;
+        }
+        if (field.extent() == 0 || offset < fieldOffset + fieldSize * fieldExtent) {
+            // reduce to first element in case of array
+            offset = fieldOffset + (offset - fieldOffset) % fieldSize;
+            if (field.type() >= C2FieldDescriptor::STRUCT_FLAG) {
+                // this offset is within a field
+                offset = GetBaseOffset(
+                        reflector, field.type() & ~C2FieldDescriptor::STRUCT_FLAG,
+                        offset - fieldOffset);
+                return ~offset ? fieldOffset + offset : offset;
+            }
+        }
+    }
+    return ~(size_t)0; // not found
+}
+
+void C2InterfaceHelper::addParameter(std::shared_ptr<ParamHelper> param) {
+    std::lock_guard<std::mutex> lock(mMutex);
+    mReflector->addStructDescriptor(param->retrieveStructDescriptor());
+    c2_status_t err = param->validate(mReflector);
+    if (err != C2_CORRUPTED) {
+        _mFactory->addParam(param);
+
+        // run setter to ensure correct values
+        bool changed = false;
+        std::vector<std::unique_ptr<C2SettingResult>> failures;
+        (void)param->trySet(param->value().get(), C2_MAY_BLOCK, &changed, *_mFactory, &failures);
+    }
+}
+
+c2_status_t C2InterfaceHelper::config(
+       const std::vector<C2Param*> &params, c2_blocking_t mayBlock,
+       std::vector<std::unique_ptr<C2SettingResult>>* const failures, bool updateParams,
+       std::vector<std::shared_ptr<C2Param>> *changes __unused /* TODO */) {
+    std::lock_guard<std::mutex> lock(mMutex);
+    bool paramWasInvalid = false; // TODO is this the same as bad value?
+    bool paramNotFound = false;
+    bool paramBadValue = false;
+    bool paramNoMemory = false;
+    bool paramBlocking = false;
+    bool paramTimedOut = false;
+    bool paramCorrupted = false;
+
+    // dependencies
+    // down dependencies are marked dirty, but params set are not immediately
+    // marked dirty (unless they become down dependency) so that we can
+    // avoid setting them if they did not change
+
+    // TODO: there could be multiple indices for the same dependency index
+    // { depIx, paramIx } may be a suitable key
+    std::map<size_t, std::pair<C2Param::Index, bool>> dependencies;
+
+    // we cannot determine the last valid parameter, so add an extra
+    // loop iteration after the last parameter
+    for (size_t p_ix = 0; p_ix <= params.size(); ++p_ix) {
+        C2Param *p = nullptr;
+        C2Param::Index paramIx = 0u;
+        size_t paramDepIx = SIZE_MAX;
+        bool last = p_ix == params.size();
+        if (!last) {
+            p = params[p_ix];
+            if (!*p) {
+                paramWasInvalid = true;
+                p->invalidate();
+                continue;
+            }
+
+            paramIx = p->index();
+            paramDepIx = getDependencyIndex_l(paramIx);
+            if (paramDepIx == SIZE_MAX) {
+                // unsupported parameter
+                paramNotFound = true;
+                continue;
+            }
+
+            //
+            // first insert - mark not dirty
+            // it may have been marked dirty by a dependency update
+            // this does not overrwrite(!)
+            (void)dependencies.insert({ paramDepIx, { paramIx, false /* dirty */ }});
+            auto it = dependencies.find(paramDepIx);
+            C2_LOG(VERBOSE) << "marking dependency for setting at #" << paramDepIx << ": "
+                    << it->second.first << ", update "
+                    << (it->second.second ? "always (dirty)" : "only if changed");
+        } else {
+            // process any remaining dependencies
+            if (dependencies.empty()) {
+                continue;
+            }
+            C2_LOG(VERBOSE) << "handling dirty down dependencies after last setting";
+        }
+
+        // process any dirtied down-dependencies until the next param
+        while (dependencies.size() && dependencies.begin()->first <= paramDepIx) {
+            auto min = dependencies.begin();
+            C2Param::Index ix = min->second.first;
+            bool dirty = min->second.second;
+            dependencies.erase(min);
+
+            std::shared_ptr<ParamHelper> param = _mFactory->getParam(ix);
+            C2_LOG(VERBOSE) << "old value " << asString(param->value().get());
+            if (!last) {
+                C2_LOG(VERBOSE) << "new value " << asString(p);
+            }
+            if (!last && !dirty && ix == paramIx && *param->value() == *p) {
+                // no change in value - and dependencies were not updated
+                // no need to update
+                C2_LOG(VERBOSE) << "ignoring setting unchanged param " << ix;
+                continue;
+            }
+
+            // apply setting
+            bool changed = false;
+            C2_LOG(VERBOSE) << "setting param " << ix;
+            std::shared_ptr<C2Param> oldValue = param->value();
+            c2_status_t res = param->trySet(
+                    (!last && paramIx == ix) ? p : param->value().get(), mayBlock,
+                    &changed, *_mFactory, failures);
+            std::shared_ptr<C2Param> newValue = param->value();
+            C2_CHECK_EQ(oldValue == newValue, *oldValue == *newValue);
+            switch (res) {
+                case C2_OK: break;
+                case C2_BAD_VALUE: paramBadValue = true; break;
+                case C2_NO_MEMORY: paramNoMemory = true; break;
+                case C2_TIMED_OUT: paramTimedOut = true; break;
+                case C2_BLOCKING:  paramBlocking = true; break;
+                case C2_CORRUPTED: paramCorrupted = true; break;
+                default: ;// TODO fatal
+            }
+
+            // copy back result for configured values (or invalidate if it does not fit or match)
+            if (updateParams && !last && paramIx == ix) {
+                if (!p->updateFrom(*param->value())) {
+                    p->invalidate();
+                }
+            }
+
+            // compare ptrs as params are copy on write
+            if (changed) {
+                C2_LOG(VERBOSE) << "param " << ix << " value changed";
+                // value changed update down-dependencies and mark them dirty
+                for (const C2Param::Index ix : param->getDownDependencies()) {
+                    C2_LOG(VERBOSE) << 1;
+                    auto insert_res = dependencies.insert(
+                            { getDependencyIndex_l(ix), { ix, true /* dirty */ }});
+                    if (!insert_res.second) {
+                        (*insert_res.first).second.second = true; // mark dirty
+                    }
+
+                    auto it = dependencies.find(getDependencyIndex_l(ix));
+                    C2_CHECK(it->second.second);
+                    C2_LOG(VERBOSE) << "marking down dependencies to update at #"
+                            << getDependencyIndex_l(ix) << ": " << it->second.first;
+                }
+            }
+        }
+    }
+
+    return (paramCorrupted ? C2_CORRUPTED :
+            paramBlocking ? C2_BLOCKING :
+            paramTimedOut ? C2_TIMED_OUT :
+            paramNoMemory ? C2_NO_MEMORY :
+            (paramBadValue || paramWasInvalid) ? C2_BAD_VALUE :
+            paramNotFound ? C2_BAD_INDEX : C2_OK);
+}
+
+size_t C2InterfaceHelper::getDependencyIndex_l(C2Param::Index ix) const {
+    return _mFactory->getDependencyIndex(ix);
+}
+
+c2_status_t C2InterfaceHelper::query(
+        const std::vector<C2Param*> &stackParams,
+        const std::vector<C2Param::Index> &heapParamIndices,
+        c2_blocking_t mayBlock __unused /* TODO */,
+        std::vector<std::unique_ptr<C2Param>>* const heapParams) const {
+    std::lock_guard<std::mutex> lock(mMutex);
+    bool paramWasInvalid = false;
+    bool paramNotFound = false;
+    bool paramDidNotFit = false;
+    bool paramNoMemory = false;
+
+    for (C2Param* const p : stackParams) {
+        if (!*p) {
+            paramWasInvalid = true;
+            p->invalidate();
+        } else {
+            std::shared_ptr<C2Param> value = _mFactory->getParamValue(p->index());
+            if (!value) {
+                paramNotFound = true;
+                p->invalidate();
+            } else if (!p->updateFrom(*value)) {
+                paramDidNotFit = true;
+                p->invalidate();
+            }
+        }
+    }
+
+    for (const C2Param::Index ix : heapParamIndices) {
+        std::shared_ptr<C2Param> value = _mFactory->getParamValue(ix);
+        if (value) {
+            std::unique_ptr<C2Param> p = C2Param::Copy(*value);
+            if (p != nullptr) {
+                heapParams->push_back(std::move(p));
+            } else {
+                paramNoMemory = true;
+            }
+        } else {
+            paramNotFound = true;
+        }
+    }
+
+    return paramNoMemory ? C2_NO_MEMORY :
+           paramNotFound ? C2_BAD_INDEX :
+           // the following errors are not marked in the return value
+           paramDidNotFit ? C2_OK :
+           paramWasInvalid ? C2_OK : C2_OK;
+}
+
+c2_status_t C2InterfaceHelper::querySupportedParams(
+        std::vector<std::shared_ptr<C2ParamDescriptor>> *const params) const {
+    std::lock_guard<std::mutex> lock(mMutex);
+    return _mFactory->querySupportedParams(params);
+}
+
+
+c2_status_t C2InterfaceHelper::querySupportedValues(
+        std::vector<C2FieldSupportedValuesQuery> &fields, c2_blocking_t mayBlock __unused) const {
+    std::lock_guard<std::mutex> lock(mMutex);
+    for (C2FieldSupportedValuesQuery &query : fields) {
+        C2_LOG(VERBOSE) << "querying field " << query.field();
+        C2Param::Index ix = _C2ParamInspector::GetIndex(query.field());
+        std::shared_ptr<ParamHelper> param = _mFactory->getParam(ix);
+        if (!param) {
+            C2_LOG(VERBOSE) << "bad param";
+            query.status = C2_BAD_INDEX;
+            continue;
+        }
+        size_t offs = GetBaseOffset(
+                mReflector, ix,
+                _C2ParamInspector::GetOffset(query.field()) - sizeof(C2Param));
+        if (~offs == 0) {
+            C2_LOG(VERBOSE) << "field could not be found";
+            query.status = C2_NOT_FOUND;
+            continue;
+        }
+        offs += sizeof(C2Param);
+        C2_LOG(VERBOSE) << "field resolved to "
+                << StringPrintf("@%02zx+%02x", offs, _C2ParamInspector::GetSize(query.field()));
+        std::shared_ptr<FieldHelper> field =
+            param->findField(offs, _C2ParamInspector::GetSize(query.field()));
+        if (!field) {
+            C2_LOG(VERBOSE) << "bad field";
+            query.status = C2_NOT_FOUND;
+            continue;
+        }
+
+        const C2FieldSupportedValues *values = nullptr;
+        switch (query.type()) {
+        case C2FieldSupportedValuesQuery::CURRENT:
+            values = field->getSupportedValues();
+            break;
+        case C2FieldSupportedValuesQuery::POSSIBLE:
+            values = field->getPossibleValues();
+            break;
+        default:
+            C2_LOG(VERBOSE) << "bad query type: " << query.type();
+            query.status = C2_BAD_VALUE;
+        }
+        if (values) {
+            query.values = *values;
+            query.status = C2_OK;
+        } else {
+            C2_LOG(DEBUG) << "no values published by component";
+            query.status = C2_CORRUPTED;
+        }
+    }
+    return C2_OK;
+}
+
+std::unique_lock<std::mutex> C2InterfaceHelper::lock() const {
+    return std::unique_lock<std::mutex>(mMutex);
+}
diff --git a/media/codec2/vndk/util/C2InterfaceUtils.cpp b/media/codec2/vndk/util/C2InterfaceUtils.cpp
new file mode 100644
index 0000000..61ec911
--- /dev/null
+++ b/media/codec2/vndk/util/C2InterfaceUtils.cpp
@@ -0,0 +1,1292 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunused-parameter"
+#pragma clang diagnostic ignored "-Wunused-variable"
+#pragma clang diagnostic ignored "-Wunused-value"
+
+#define C2_LOG_VERBOSE
+
+#include <C2Debug.h>
+#include <C2Param.h>
+#include <C2ParamDef.h>
+#include <C2ParamInternal.h>
+#include <util/C2InterfaceUtils.h>
+
+#include <cmath>
+#include <limits>
+#include <map>
+#include <type_traits>
+
+#include <android-base/stringprintf.h>
+
+std::ostream& operator<<(std::ostream& os, const _C2FieldId &i);
+
+std::ostream& operator<<(std::ostream& os, const C2ParamField &i);
+
+/* ---------------------------- C2SupportedRange ---------------------------- */
+
+/**
+ * Helper class for supported values range calculations.
+ */
+template<typename T, bool FP=std::is_floating_point<T>::value>
+struct _C2TypedSupportedRangeHelper {
+    /**
+     * type of range size: a - b if a >= b and a and b are of type T
+     */
+    typedef typename std::make_unsigned<T>::type DiffType;
+
+    /**
+     * calculate (high - low) mod step
+     */
+    static DiffType mod(T low, T high, T step) {
+        return DiffType(high - low) % DiffType(step);
+    }
+};
+
+template<typename T>
+struct _C2TypedSupportedRangeHelper<T, true> {
+    typedef T DiffType;
+
+    static DiffType mod(T low, T high, T step) {
+        return fmod(high - low, step);
+    }
+};
+
+template<typename T>
+C2SupportedRange<T>::C2SupportedRange(const C2FieldSupportedValues &values) {
+    if (values.type == C2FieldSupportedValues::RANGE) {
+        _mMin = values.range.min.ref<ValueType>();
+        _mMax = values.range.max.ref<ValueType>();
+        _mStep = values.range.step.ref<ValueType>();
+        _mNum = values.range.num.ref<ValueType>();
+        _mDenom = values.range.denom.ref<ValueType>();
+    } else {
+        _mMin = MAX_VALUE;
+        _mMax = MIN_VALUE;
+        _mStep = MIN_STEP;
+        _mNum = 0;
+        _mDenom = 0;
+    }
+}
+
+template<typename T>
+bool C2SupportedRange<T>::contains(T value) const {
+    // value must fall between min and max
+    if (value < _mMin || value > _mMax) {
+        return false;
+    }
+    // simple ranges contain all values between min and max
+    if (isSimpleRange()) {
+        return true;
+    }
+    // min is always part of the range
+    if (value == _mMin) {
+        return true;
+    }
+    // stepped ranges require (val - min) % step to be zero
+    if (isArithmeticSeries()) {
+        return _C2TypedSupportedRangeHelper<T>::mod(_mMin, value, _mStep) == 0;
+    }
+    // pure geometric series require (val / min) to be integer multiple of (num/denom)
+    if (isGeometricSeries()) {
+        if (value <= 0) {
+            return false;
+        }
+        double log2base = log2(_mNum / _mDenom);
+        double power = llround(log2(value / double(_mMin)) / log2base);
+        // TODO: validate that result falls within precision (other than round)
+        return value == T(_mMin * pow(_mNum / _mDenom, power) + MIN_STEP / 2);
+    }
+    // multiply-accumulate series require validating by walking through the series
+    if (isMacSeries()) {
+        double lastValue = _mMin;
+        double base = _mNum / _mDenom;
+        while (true) {
+            // this cast is safe as _mMin <= lastValue <= _mMax
+            if (T(lastValue + MIN_STEP / 2) == value) {
+                return true;
+            }
+            double nextValue = fma(lastValue, base, _mStep);
+            if (nextValue <= lastValue || nextValue > _mMax) {
+                return false; // series is no longer monotonic or within range
+            }
+            lastValue = nextValue;
+        };
+    }
+    // if we are here, this must be an invalid range
+    return false;
+}
+
+template<typename T>
+C2SupportedRange<T> C2SupportedRange<T>::limitedTo(const C2SupportedRange<T> &limit) const {
+    // TODO - this only works for simple ranges
+    return C2SupportedRange(std::max(_mMin, limit._mMin), std::min(_mMax, limit._mMax),
+                                 std::max(_mStep, limit._mStep));
+}
+
+template class C2SupportedRange<uint8_t>;
+template class C2SupportedRange<char>;
+template class C2SupportedRange<int32_t>;
+template class C2SupportedRange<uint32_t>;
+//template class C2SupportedRange<c2_cntr32_t>;
+template class C2SupportedRange<int64_t>;
+template class C2SupportedRange<uint64_t>;
+//template class C2SupportedRange<c2_cntr64_t>;
+template class C2SupportedRange<float>;
+
+/* -------------------------- C2SupportedFlags -------------------------- */
+
+/**
+ * Ordered supported flag set for a field of a given type.
+ */
+// float flags are not supported, but define a few methods to support generic supported values code
+template<>
+bool C2SupportedFlags<float>::contains(float value) const {
+    return false;
+}
+
+template<>
+const std::vector<float> C2SupportedFlags<float>::flags() const {
+    return std::vector<float>();
+}
+
+template<>
+C2SupportedFlags<float> C2SupportedFlags<float>::limitedTo(const C2SupportedFlags<float> &limit) const {
+    std::vector<C2Value::Primitive> values;
+    return C2SupportedFlags(std::move(values));
+}
+
+template<>
+float C2SupportedFlags<float>::min() const {
+    return 0;
+}
+
+template<typename T>
+bool C2SupportedFlags<T>::contains(T value) const {
+    // value must contain the minimal mask
+    T minMask = min();
+    if (~value & minMask) {
+        return false;
+    }
+    value &= ~minMask;
+    // otherwise, remove flags from value and see if we arrive at 0
+    for (const C2Value::Primitive &v : _mValues) {
+        if (value == 0) {
+            break;
+        }
+        if ((~value & v.ref<ValueType>()) == 0) {
+            value &= ~v.ref<ValueType>();
+        }
+    }
+    return value == 0;
+}
+
+template<typename T>
+const std::vector<T> C2SupportedFlags<T>::flags() const {
+    std::vector<T> vals(c2_max(_mValues.size(), 1u) - 1);
+    if (!_mValues.empty()) {
+        std::transform(_mValues.cbegin() + 1, _mValues.cend(), vals.begin(),
+                       [](const C2Value::Primitive &p)->T {
+            return p.ref<ValueType>();
+        });
+    }
+    return vals;
+}
+
+template<typename T>
+C2SupportedFlags<T> C2SupportedFlags<T>::limitedTo(const C2SupportedFlags<T> &limit) const {
+    std::vector<C2Value::Primitive> values = _mValues; // make a copy
+    T minMask = min() | limit.min();
+    // minimum mask must be covered by both this and other
+    if (limit.contains(minMask) && contains(minMask)) {
+        values[0] = minMask;
+        // keep only flags that are covered by limit
+        std::remove_if(values.begin(), values.end(), [&limit, minMask](const C2Value::Primitive &v) -> bool {
+            T value = v.ref<ValueType>() | minMask;
+            return value == minMask || !limit.contains(value); });
+        // we also need to do it vice versa
+        for (const C2Value::Primitive &v : _mValues) {
+            T value = v.ref<ValueType>() | minMask;
+            if (value != minMask && contains(value)) {
+                values.emplace_back((ValueType)value);
+            }
+        }
+    }
+    return C2SupportedFlags(std::move(values));
+}
+
+template<typename T>
+T C2SupportedFlags<T>::min() const {
+    if (!_mValues.empty()) {
+        return _mValues.front().template ref<ValueType>();
+    } else {
+        return T(0);
+    }
+}
+
+template class C2SupportedFlags<uint8_t>;
+template class C2SupportedFlags<char>;
+template class C2SupportedFlags<int32_t>;
+template class C2SupportedFlags<uint32_t>;
+//template class C2SupportedFlags<c2_cntr32_t>;
+template class C2SupportedFlags<int64_t>;
+template class C2SupportedFlags<uint64_t>;
+//template class C2SupportedFlags<c2_cntr64_t>;
+
+/* -------------------------- C2SupportedValueSet -------------------------- */
+
+/**
+ * Ordered supported value set for a field of a given type.
+ */
+template<typename T>
+bool C2SupportedValueSet<T>::contains(T value) const {
+    return std::find_if(_mValues.cbegin(), _mValues.cend(),
+            [value](const C2Value::Primitive &p) -> bool {
+                return value == p.ref<ValueType>();
+            }) != _mValues.cend();
+}
+
+template<typename T>
+C2SupportedValueSet<T> C2SupportedValueSet<T>::limitedTo(const C2SupportedValueSet<T> &limit) const {
+    std::vector<C2Value::Primitive> values = _mValues; // make a copy
+    std::remove_if(values.begin(), values.end(), [&limit](const C2Value::Primitive &v) -> bool {
+        return !limit.contains(v.ref<ValueType>()); });
+    return C2SupportedValueSet(std::move(values));
+}
+
+template<typename T>
+C2SupportedValueSet<T> C2SupportedValueSet<T>::limitedTo(const C2SupportedRange<T> &limit) const {
+    std::vector<C2Value::Primitive> values = _mValues; // make a copy
+    std::remove_if(values.begin(), values.end(), [&limit](const C2Value::Primitive &v) -> bool {
+        return !limit.contains(v.ref<ValueType>()); });
+    return C2SupportedValueSet(std::move(values));
+}
+
+template<typename T>
+C2SupportedValueSet<T> C2SupportedValueSet<T>::limitedTo(const C2SupportedFlags<T> &limit) const {
+    std::vector<C2Value::Primitive> values = _mValues; // make a copy
+    std::remove_if(values.begin(), values.end(), [&limit](const C2Value::Primitive &v) -> bool {
+        return !limit.contains(v.ref<ValueType>()); });
+    return C2SupportedValueSet(std::move(values));
+}
+
+template<typename T>
+const std::vector<T> C2SupportedValueSet<T>::values() const {
+    std::vector<T> vals(_mValues.size());
+    std::transform(_mValues.cbegin(), _mValues.cend(), vals.begin(), [](const C2Value::Primitive &p) -> T {
+        return p.ref<ValueType>();
+    });
+    return vals;
+}
+
+template class C2SupportedValueSet<uint8_t>;
+template class C2SupportedValueSet<char>;
+template class C2SupportedValueSet<int32_t>;
+template class C2SupportedValueSet<uint32_t>;
+//template class C2SupportedValueSet<c2_cntr32_t>;
+template class C2SupportedValueSet<int64_t>;
+template class C2SupportedValueSet<uint64_t>;
+//template class C2SupportedValueSet<c2_cntr64_t>;
+template class C2SupportedValueSet<float>;
+
+/* ---------------------- C2FieldSupportedValuesHelper ---------------------- */
+
+template<typename T>
+struct C2FieldSupportedValuesHelper<T>::Impl {
+    Impl(const C2FieldSupportedValues &values)
+        : _mType(values.type),
+          _mRange(values),
+          _mValues(values),
+          _mFlags(values) { }
+
+    bool supports(T value) const;
+
+private:
+    typedef typename _C2FieldValueHelper<T>::ValueType ValueType;
+    C2FieldSupportedValues::type_t _mType;
+    C2SupportedRange<ValueType> _mRange;
+    C2SupportedValueSet<ValueType> _mValues;
+    C2SupportedValueSet<ValueType> _mFlags;
+
+//    friend std::ostream& operator<< <T>(std::ostream& os, const C2FieldSupportedValuesHelper<T>::Impl &i);
+//    friend std::ostream& operator<<(std::ostream& os, const Impl &i);
+    std::ostream& streamOut(std::ostream& os) const;
+};
+
+template<typename T>
+bool C2FieldSupportedValuesHelper<T>::Impl::supports(T value) const {
+    switch (_mType) {
+        case C2FieldSupportedValues::RANGE: return _mRange.contains(value);
+        case C2FieldSupportedValues::VALUES: return _mValues.contains(value);
+        case C2FieldSupportedValues::FLAGS: return _mFlags.contains(value);
+        default: return false;
+    }
+}
+
+template<typename T>
+C2FieldSupportedValuesHelper<T>::C2FieldSupportedValuesHelper(const C2FieldSupportedValues &values)
+    : _mImpl(std::make_unique<C2FieldSupportedValuesHelper<T>::Impl>(values)) { }
+
+template<typename T>
+C2FieldSupportedValuesHelper<T>::~C2FieldSupportedValuesHelper() = default;
+
+template<typename T>
+bool C2FieldSupportedValuesHelper<T>::supports(T value) const {
+    return _mImpl->supports(value);
+}
+
+template class C2FieldSupportedValuesHelper<uint8_t>;
+template class C2FieldSupportedValuesHelper<char>;
+template class C2FieldSupportedValuesHelper<int32_t>;
+template class C2FieldSupportedValuesHelper<uint32_t>;
+//template class C2FieldSupportedValuesHelper<c2_cntr32_t>;
+template class C2FieldSupportedValuesHelper<int64_t>;
+template class C2FieldSupportedValuesHelper<uint64_t>;
+//template class C2FieldSupportedValuesHelper<c2_cntr64_t>;
+template class C2FieldSupportedValuesHelper<float>;
+
+/* ----------------------- C2ParamFieldValuesBuilder ----------------------- */
+
+template<typename T>
+struct C2ParamFieldValuesBuilder<T>::Impl {
+    Impl(const C2ParamField &field)
+        : _mParamField(field),
+          _mType(type_t::RANGE),
+          _mDefined(false),
+          _mRange(C2SupportedRange<T>::Any()),
+          _mValues(C2SupportedValueSet<T>::None()),
+          _mFlags(C2SupportedFlags<T>::None()) { }
+
+    /**
+     * Get C2ParamFieldValues from this builder.
+     */
+    operator C2ParamFieldValues() const {
+        if (!_mDefined) {
+            return C2ParamFieldValues(_mParamField);
+        }
+        switch (_mType) {
+        case type_t::EMPTY:
+        case type_t::VALUES:
+            return C2ParamFieldValues(_mParamField, (C2FieldSupportedValues)_mValues);
+        case type_t::RANGE:
+            return C2ParamFieldValues(_mParamField, (C2FieldSupportedValues)_mRange);
+        case type_t::FLAGS:
+            return C2ParamFieldValues(_mParamField, (C2FieldSupportedValues)_mFlags);
+        default:
+            // TRESPASS
+            // should never get here
+            return C2ParamFieldValues(_mParamField);
+        }
+    }
+
+    /** Define the supported values as the currently supported values of this builder. */
+    void any() {
+        _mDefined = true;
+    }
+
+    /** Restrict (and thus define) the supported values to none. */
+    void none() {
+        _mDefined = true;
+        _mType = type_t::VALUES;
+        _mValues.clear();
+    }
+
+    /** Restrict (and thus define) the supported values to |value| alone. */
+    void equalTo(T value) {
+         return limitTo(C2SupportedValueSet<T>::OneOf({value}));
+    }
+
+    /** Restrict (and thus define) the supported values to a value set. */
+    void limitTo(const C2SupportedValueSet<T> &limit) {
+        if (!_mDefined) {
+            C2_LOG(VERBOSE) << "NA.limitTo(" << C2FieldSupportedValuesHelper<T>(limit) << ")";
+
+            // shortcut for first limit applied
+            _mDefined = true;
+            _mValues = limit;
+            _mType = _mValues.isEmpty() ? type_t::EMPTY : type_t::VALUES;
+        } else {
+            switch (_mType) {
+            case type_t::EMPTY:
+            case type_t::VALUES:
+                C2_LOG(VERBOSE) << "(" << C2FieldSupportedValuesHelper<T>(_mValues) << ").limitTo("
+                        << C2FieldSupportedValuesHelper<T>(limit) << ")";
+
+                _mValues = _mValues.limitedTo(limit);
+                _mType = _mValues.isEmpty() ? type_t::EMPTY : type_t::VALUES;
+                break;
+            case type_t::RANGE:
+                C2_LOG(VERBOSE) << "(" << C2FieldSupportedValuesHelper<T>(_mRange) << ").limitTo("
+                        << C2FieldSupportedValuesHelper<T>(limit) << ")";
+
+                _mValues = limit.limitedTo(_mRange);
+                _mType = _mValues.isEmpty() ? type_t::EMPTY : type_t::VALUES;
+                break;
+            case type_t::FLAGS:
+                C2_LOG(VERBOSE) << "(" << C2FieldSupportedValuesHelper<T>(_mRange) << ").limitTo("
+                        << C2FieldSupportedValuesHelper<T>(limit) << ")";
+
+                _mValues = limit.limitedTo(_mFlags);
+                _mType = _mValues.isEmpty() ? type_t::EMPTY : type_t::VALUES;
+                break;
+            default:
+                C2_LOG(FATAL); // should not be here
+            }
+            // TODO: support flags
+        }
+        C2_LOG(VERBOSE) << " = " << _mType << ":" << C2FieldSupportedValuesHelper<T>(_mValues);
+    }
+
+    /** Restrict (and thus define) the supported values to a flag set. */
+    void limitTo(const C2SupportedFlags<T> &limit) {
+        if (!_mDefined) {
+            C2_LOG(VERBOSE) << "NA.limitTo(" << C2FieldSupportedValuesHelper<T>(limit) << ")";
+
+            // shortcut for first limit applied
+            _mDefined = true;
+            _mFlags = limit;
+            _mType = _mFlags.isEmpty() ? type_t::EMPTY : type_t::FLAGS;
+        } else {
+            switch (_mType) {
+            case type_t::EMPTY:
+            case type_t::VALUES:
+                C2_LOG(VERBOSE) << "(" << C2FieldSupportedValuesHelper<T>(_mValues) << ").limitTo("
+                        << C2FieldSupportedValuesHelper<T>(limit) << ")";
+
+                _mValues = _mValues.limitedTo(limit);
+                _mType = _mValues.isEmpty() ? type_t::EMPTY : type_t::VALUES;
+                C2_LOG(VERBOSE) << " = " << _mType << ":" << C2FieldSupportedValuesHelper<T>(_mValues);
+                break;
+            case type_t::FLAGS:
+                C2_LOG(VERBOSE) << "(" << C2FieldSupportedValuesHelper<T>(_mFlags) << ").limitTo("
+                        << C2FieldSupportedValuesHelper<T>(limit) << ")";
+
+                _mFlags = _mFlags.limitedTo(limit);
+                _mType = _mFlags.isEmpty() ? type_t::EMPTY : type_t::FLAGS;
+                C2_LOG(VERBOSE) << " = " << _mType << ":" << C2FieldSupportedValuesHelper<T>(_mFlags);
+                break;
+            case type_t::RANGE:
+                C2_LOG(FATAL) << "limiting ranges to flags is not supported";
+                _mType = type_t::EMPTY;
+                break;
+            default:
+                C2_LOG(FATAL); // should not be here
+            }
+        }
+    }
+
+    void limitTo(const C2SupportedRange<T> &limit) {
+        if (!_mDefined) {
+            C2_LOG(VERBOSE) << "NA.limitTo(" << C2FieldSupportedValuesHelper<T>(limit) << ")";
+
+            // shortcut for first limit applied
+            _mDefined = true;
+            _mRange = limit;
+            _mType = _mRange.isEmpty() ? type_t::EMPTY : type_t::RANGE;
+            C2_LOG(VERBOSE) << " = " << _mType << ":" << C2FieldSupportedValuesHelper<T>(_mRange);
+        } else {
+            switch (_mType) {
+            case type_t::EMPTY:
+            case type_t::VALUES:
+                C2_LOG(VERBOSE) << "(" << C2FieldSupportedValuesHelper<T>(_mValues) << ").limitTo("
+                        << C2FieldSupportedValuesHelper<T>(limit) << ")";
+                _mValues = _mValues.limitedTo(limit);
+                _mType = _mValues.isEmpty() ? type_t::EMPTY : type_t::VALUES;
+                C2_LOG(VERBOSE) << " = " << _mType << ":" << C2FieldSupportedValuesHelper<T>(_mValues);
+                break;
+            case type_t::FLAGS:
+                C2_LOG(FATAL) << "limiting flags to ranges is not supported";
+                _mType = type_t::EMPTY;
+                break;
+            case type_t::RANGE:
+                C2_LOG(VERBOSE) << "(" << C2FieldSupportedValuesHelper<T>(_mRange) << ").limitTo("
+                        << C2FieldSupportedValuesHelper<T>(limit) << ")";
+                _mRange = _mRange.limitedTo(limit);
+                C2_DCHECK(_mValues.isEmpty());
+                _mType = _mRange.isEmpty() ? type_t::EMPTY : type_t::RANGE;
+                C2_LOG(VERBOSE) << " = " << _mType << ":" << C2FieldSupportedValuesHelper<T>(_mRange);
+                break;
+            default:
+                C2_LOG(FATAL); // should not be here
+            }
+        }
+    }
+
+private:
+    void instantiate() __unused {
+        (void)_mValues.values(); // instantiate non-const values()
+    }
+
+    void instantiate() const __unused {
+        (void)_mValues.values(); // instantiate const values()
+    }
+
+    typedef C2FieldSupportedValues::type_t type_t;
+
+    C2ParamField _mParamField;
+    type_t _mType;
+    bool _mDefined;
+    C2SupportedRange<T> _mRange;
+    C2SupportedValueSet<T> _mValues;
+    C2SupportedFlags<T> _mFlags;
+
+};
+
+template<typename T>
+C2ParamFieldValuesBuilder<T>::operator C2ParamFieldValues() const {
+    return (C2ParamFieldValues)(*_mImpl.get());
+}
+
+template<typename T>
+C2ParamFieldValuesBuilder<T>::C2ParamFieldValuesBuilder(const C2ParamField &field)
+    : _mImpl(std::make_unique<C2ParamFieldValuesBuilder<T>::Impl>(field)) { }
+
+template<typename T>
+C2ParamFieldValuesBuilder<T> &C2ParamFieldValuesBuilder<T>::any() {
+    _mImpl->any();
+    return *this;
+}
+
+template<typename T>
+C2ParamFieldValuesBuilder<T> &C2ParamFieldValuesBuilder<T>::none() {
+    _mImpl->none();
+    return *this;
+}
+
+template<typename T>
+C2ParamFieldValuesBuilder<T> &C2ParamFieldValuesBuilder<T>::equalTo(T value) {
+    _mImpl->equalTo(value);
+    return *this;
+}
+
+template<typename T>
+C2ParamFieldValuesBuilder<T> &C2ParamFieldValuesBuilder<T>::limitTo(const C2SupportedValueSet<T> &limit) {
+    _mImpl->limitTo(limit);
+    return *this;
+}
+
+template<typename T>
+C2ParamFieldValuesBuilder<T> &C2ParamFieldValuesBuilder<T>::limitTo(const C2SupportedFlags<T> &limit) {
+    _mImpl->limitTo(limit);
+    return *this;
+}
+
+template<typename T>
+C2ParamFieldValuesBuilder<T> &C2ParamFieldValuesBuilder<T>::limitTo(const C2SupportedRange<T> &limit) {
+    _mImpl->limitTo(limit);
+    return *this;
+}
+
+template<typename T>
+C2ParamFieldValuesBuilder<T>::C2ParamFieldValuesBuilder(const C2ParamFieldValuesBuilder<T> &other)
+    : _mImpl(std::make_unique<C2ParamFieldValuesBuilder<T>::Impl>(*other._mImpl.get())) { }
+
+template<typename T>
+C2ParamFieldValuesBuilder<T> &C2ParamFieldValuesBuilder<T>::operator=(
+        const C2ParamFieldValuesBuilder<T> &other) {
+    _mImpl = std::make_unique<C2ParamFieldValuesBuilder<T>::Impl>(*other._mImpl.get());
+    return *this;
+}
+
+template<typename T>
+C2ParamFieldValuesBuilder<T>::~C2ParamFieldValuesBuilder() = default;
+
+template class C2ParamFieldValuesBuilder<uint8_t>;
+template class C2ParamFieldValuesBuilder<char>;
+template class C2ParamFieldValuesBuilder<int32_t>;
+template class C2ParamFieldValuesBuilder<uint32_t>;
+//template class C2ParamFieldValuesBuilder<c2_cntr32_t>;
+template class C2ParamFieldValuesBuilder<int64_t>;
+template class C2ParamFieldValuesBuilder<uint64_t>;
+//template class C2ParamFieldValuesBuilder<c2_cntr64_t>;
+template class C2ParamFieldValuesBuilder<float>;
+
+/* ------------------------- C2SettingResultBuilder ------------------------- */
+
+C2SettingConflictsBuilder::C2SettingConflictsBuilder() : _mConflicts() { }
+
+C2SettingConflictsBuilder::C2SettingConflictsBuilder(C2ParamFieldValues &&conflict) {
+    _mConflicts.emplace_back(std::move(conflict));
+}
+
+C2SettingConflictsBuilder& C2SettingConflictsBuilder::with(C2ParamFieldValues &&conflict) {
+    _mConflicts.emplace_back(std::move(conflict));
+    return *this;
+}
+
+std::vector<C2ParamFieldValues> C2SettingConflictsBuilder::retrieveConflicts() {
+    return std::move(_mConflicts);
+}
+
+/* ------------------------- C2SettingResult/sBuilder ------------------------- */
+
+C2SettingResult C2SettingResultBuilder::ReadOnly(const C2ParamField &param) {
+    return C2SettingResult { C2SettingResult::READ_ONLY, { param }, { } };
+}
+
+C2SettingResult C2SettingResultBuilder::BadValue(const C2ParamField &paramField, bool isInfo) {
+    return { isInfo ? C2SettingResult::INFO_BAD_VALUE : C2SettingResult::BAD_VALUE,
+             { paramField }, { } };
+}
+
+C2SettingResult C2SettingResultBuilder::Conflict(
+        C2ParamFieldValues &&paramFieldValues, C2SettingConflictsBuilder &conflicts, bool isInfo) {
+    C2_CHECK(!conflicts.empty());
+    if (isInfo) {
+        return C2SettingResult {
+            C2SettingResult::INFO_CONFLICT,
+            std::move(paramFieldValues), conflicts.retrieveConflicts()
+        };
+    } else {
+        return C2SettingResult {
+            C2SettingResult::CONFLICT,
+            std::move(paramFieldValues), conflicts.retrieveConflicts()
+        };
+    }
+}
+
+C2SettingResultsBuilder::C2SettingResultsBuilder(C2SettingResult &&result)
+        : _mStatus(C2_BAD_VALUE) {
+    _mResults.emplace_back(new C2SettingResult(std::move(result)));
+}
+
+C2SettingResultsBuilder C2SettingResultsBuilder::plus(C2SettingResultsBuilder&& results) {
+    for (std::unique_ptr<C2SettingResult> &r : results._mResults) {
+        _mResults.emplace_back(std::move(r));
+    }
+    results._mResults.clear();
+    // TODO: mStatus
+    return std::move(*this);
+}
+
+c2_status_t C2SettingResultsBuilder::retrieveFailures(
+        std::vector<std::unique_ptr<C2SettingResult>>* const failures) {
+    for (std::unique_ptr<C2SettingResult> &r : _mResults) {
+        failures->emplace_back(std::move(r));
+    }
+    _mResults.clear();
+    return _mStatus;
+}
+
+C2SettingResultsBuilder::C2SettingResultsBuilder(c2_status_t status) : _mStatus(status) {
+    // status must be one of OK, BAD_STATE, TIMED_OUT or CORRUPTED
+    // mainly: BLOCKING, BAD_INDEX, BAD_VALUE and NO_MEMORY requires a setting attempt
+}
+
+#pragma clang diagnostic pop
+
+/* ------------------------- C2FieldUtils ------------------------- */
+
+struct C2_HIDE C2FieldUtils::_Inspector {
+    /// returns the implementation object
+    inline static std::shared_ptr<Info::Impl> GetImpl(const Info &info) {
+        return info._mImpl;
+    }
+};
+
+/* ------------------------- C2FieldUtils::Info ------------------------- */
+
+struct C2_HIDE C2FieldUtils::Info::Impl {
+    C2FieldDescriptor field;
+    std::shared_ptr<Impl> parent;
+    uint32_t index;
+    uint32_t depth;
+    uint32_t baseFieldOffset;
+    uint32_t arrayOffset;
+    uint32_t usedExtent;
+
+    /// creates a copy of this object including copies of its parent chain
+    Impl clone() const;
+
+    /// creates a copy of a shared pointer to an object
+    static std::shared_ptr<Impl> Clone(const std::shared_ptr<Impl> &);
+
+    Impl(const C2FieldDescriptor &field_, std::shared_ptr<Impl> parent_,
+            uint32_t index_, uint32_t depth_, uint32_t baseFieldOffset_,
+            uint32_t arrayOffset_, uint32_t usedExtent_)
+        : field(field_), parent(parent_), index(index_), depth(depth_),
+          baseFieldOffset(baseFieldOffset_), arrayOffset(arrayOffset_), usedExtent(usedExtent_) { }
+};
+
+std::shared_ptr<C2FieldUtils::Info::Impl> C2FieldUtils::Info::Impl::Clone(const std::shared_ptr<Impl> &info) {
+    if (info) {
+        return std::make_shared<Impl>(info->clone());
+    }
+    return nullptr;
+}
+
+C2FieldUtils::Info::Impl C2FieldUtils::Info::Impl::clone() const {
+    Impl res = Impl(*this);
+    res.parent = Clone(res.parent);
+    return res;
+}
+
+C2FieldUtils::Info::Info(std::shared_ptr<Impl> impl)
+    : _mImpl(impl) { }
+
+size_t C2FieldUtils::Info::arrayOffset() const {
+    return _mImpl->arrayOffset;
+}
+
+size_t C2FieldUtils::Info::arraySize() const {
+    return extent() * size();
+}
+
+size_t C2FieldUtils::Info::baseFieldOffset() const {
+    return _mImpl->baseFieldOffset;
+};
+
+size_t C2FieldUtils::Info::depth() const {
+    return _mImpl->depth;
+}
+
+size_t C2FieldUtils::Info::extent() const {
+    return _mImpl->usedExtent;
+}
+
+size_t C2FieldUtils::Info::index() const {
+    return _mImpl->index;
+}
+
+bool C2FieldUtils::Info::isArithmetic() const {
+    switch (_mImpl->field.type()) {
+    case C2FieldDescriptor::BLOB:
+    case C2FieldDescriptor::CNTR32:
+    case C2FieldDescriptor::CNTR64:
+    case C2FieldDescriptor::FLOAT:
+    case C2FieldDescriptor::INT32:
+    case C2FieldDescriptor::INT64:
+    case C2FieldDescriptor::STRING:
+    case C2FieldDescriptor::UINT32:
+    case C2FieldDescriptor::UINT64:
+        return true;
+    default:
+        return false;
+    }
+}
+
+bool C2FieldUtils::Info::isFlexible() const {
+    return _mImpl->field.extent() == 0;
+}
+
+C2String C2FieldUtils::Info::name() const {
+    return _mImpl->field.name();
+}
+
+const C2FieldUtils::Info::NamedValuesType &C2FieldUtils::Info::namedValues() const {
+    return _mImpl->field.namedValues();
+}
+
+size_t C2FieldUtils::Info::offset() const {
+    return _C2ParamInspector::GetOffset(_mImpl->field);
+}
+
+C2FieldUtils::Info C2FieldUtils::Info::parent() const {
+    return Info(_mImpl->parent);
+};
+
+size_t C2FieldUtils::Info::size() const {
+    return _C2ParamInspector::GetSize(_mImpl->field);
+}
+
+C2FieldUtils::Info::type_t C2FieldUtils::Info::type() const {
+    return _mImpl->field.type();
+}
+
+/* ------------------------- C2FieldUtils::Iterator ------------------------- */
+
+struct C2_HIDE C2FieldUtils::Iterator::Impl : public _C2ParamInspector {
+    Impl() = default;
+
+    virtual ~Impl() = default;
+
+    /// implements object equality
+    virtual bool equals(const std::shared_ptr<Impl> &other) const {
+        return other != nullptr && mHead == other->mHead;
+    };
+
+    /// returns the info pointed to by this iterator
+    virtual value_type get() const {
+        return Info(mHead);
+    }
+
+    /// increments this iterator
+    virtual void increment() {
+        // note: this cannot be abstract as we instantiate this for List::end(). increment to end()
+        // instead.
+        mHead.reset();
+    }
+
+protected:
+    Impl(std::shared_ptr<C2FieldUtils::Info::Impl> head)
+        : mHead(head) { }
+
+    std::shared_ptr<Info::Impl> mHead; ///< current field
+};
+
+C2FieldUtils::Iterator::Iterator(std::shared_ptr<Impl> impl)
+    : mImpl(impl) { }
+
+C2FieldUtils::Iterator::value_type C2FieldUtils::Iterator::operator*() const {
+    return mImpl->get();
+}
+
+C2FieldUtils::Iterator& C2FieldUtils::Iterator::operator++() {
+    mImpl->increment();
+    return *this;
+}
+
+bool C2FieldUtils::Iterator::operator==(const Iterator &other) const {
+    return mImpl->equals(other.mImpl);
+}
+
+/* ------------------------- C2FieldUtils::List ------------------------- */
+
+struct C2_HIDE C2FieldUtils::List::Impl {
+    virtual std::shared_ptr<Iterator::Impl> begin() const = 0;
+
+    /// returns an iterator to the end of the list
+    virtual std::shared_ptr<Iterator::Impl> end() const {
+        return std::make_shared<Iterator::Impl>();
+    }
+
+    virtual ~Impl() = default;
+};
+
+C2FieldUtils::List::List(std::shared_ptr<Impl> impl)
+    : mImpl(impl) { }
+
+C2FieldUtils::Iterator C2FieldUtils::List::begin() const {
+    return C2FieldUtils::Iterator(mImpl->begin());
+}
+
+C2FieldUtils::Iterator C2FieldUtils::List::end() const {
+    return C2FieldUtils::Iterator(mImpl->end());
+}
+
+/* ------------------------- C2FieldUtils::enumerateFields ------------------------- */
+
+namespace {
+
+/**
+ * Iterator base class helper that allows descending into the field hierarchy.
+ */
+struct C2FieldUtilsFieldsIteratorHelper : public C2FieldUtils::Iterator::Impl {
+    virtual ~C2FieldUtilsFieldsIteratorHelper() override = default;
+
+    /// returns the base-field's offset of the parent field (or the param offset if no parent)
+    static inline uint32_t GetParentBaseFieldOffset(
+            const std::shared_ptr<C2FieldUtils::Info::Impl> parent) {
+        return parent == nullptr ? sizeof(C2Param) : parent->baseFieldOffset;
+    }
+
+    /// returns the offset of the parent field (or the param)
+    static inline uint32_t GetParentOffset(const std::shared_ptr<C2FieldUtils::Info::Impl> parent) {
+        return parent == nullptr ? sizeof(C2Param) : GetOffset(parent->field);
+    }
+
+protected:
+    C2FieldUtilsFieldsIteratorHelper(
+            std::shared_ptr<C2ParamReflector> reflector,
+            uint32_t paramSize,
+            std::shared_ptr<C2FieldUtils::Info::Impl> head = nullptr)
+        : C2FieldUtils::Iterator::Impl(head),
+          mParamSize(paramSize),
+          mReflector(reflector) { }
+
+    /// returns a leaf info object at a specific index for a child field
+    std::shared_ptr<C2FieldUtils::Info::Impl> makeLeaf(
+            const C2FieldDescriptor &field, uint32_t index) {
+        uint32_t parentOffset = GetParentOffset(mHead);
+        uint32_t arrayOffset = parentOffset + GetOffset(field);
+        uint32_t usedExtent = field.extent() ? :
+                (std::max(arrayOffset, mParamSize) - arrayOffset) / GetSize(field);
+
+        return std::make_shared<C2FieldUtils::Info::Impl>(
+                OffsetFieldDescriptor(field, parentOffset + index * GetSize(field)),
+                mHead /* parent */, index, mHead == nullptr ? 0 : mHead->depth + 1,
+                GetParentBaseFieldOffset(mHead) + GetOffset(field),
+                arrayOffset, usedExtent);
+    }
+
+    /// returns whether this struct index have been traversed to get to this field
+    bool visited(C2Param::CoreIndex index) const {
+        for (const std::shared_ptr<C2StructDescriptor> &sd : mHistory) {
+            if (sd->coreIndex() == index) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    uint32_t mParamSize;
+    std::shared_ptr<C2ParamReflector> mReflector;
+    std::vector<std::shared_ptr<C2StructDescriptor>> mHistory; // structure types visited
+};
+
+/**
+ * Iterator implementing enumerateFields() that visits each base field.
+ */
+struct C2FieldUtilsFieldsIterator : public C2FieldUtilsFieldsIteratorHelper {
+    /// enumerate base fields of a parameter
+    C2FieldUtilsFieldsIterator(const C2Param &param, std::shared_ptr<C2ParamReflector> reflector)
+        : C2FieldUtilsFieldsIteratorHelper(reflector, param.size()) {
+        descendInto(param.coreIndex());
+    }
+
+    /// enumerate base fields of a field
+    C2FieldUtilsFieldsIterator(std::shared_ptr<C2FieldUtilsFieldsIterator> impl)
+        : C2FieldUtilsFieldsIteratorHelper(impl->mReflector, impl->mParamSize, impl->mHead) {
+        mHistory = impl->mHistory;
+        if (mHead->field.type() & C2FieldDescriptor::STRUCT_FLAG) {
+            C2Param::CoreIndex index = { mHead->field.type() &~C2FieldDescriptor::STRUCT_FLAG };
+            if (!visited(index)) {
+                descendInto(index);
+            }
+        }
+    }
+
+    virtual ~C2FieldUtilsFieldsIterator() override = default;
+
+    /// Increments this iterator by visiting each base field.
+    virtual void increment() override {
+        // don't go past end
+        if (mHead == nullptr || _mFields.empty()) {
+            return;
+        }
+
+        // descend into structures
+        if (mHead->field.type() & C2FieldDescriptor::STRUCT_FLAG) {
+            C2Param::CoreIndex index = { mHead->field.type() &~C2FieldDescriptor::STRUCT_FLAG };
+            // do not recurse into the same structs
+            if (!visited(index) && descendInto(index)) {
+                return;
+            }
+        }
+
+        // ascend after the last field in the current struct
+        while (!mHistory.empty() && _mFields.back() == mHistory.back()->end()) {
+            mHead = mHead->parent;
+            mHistory.pop_back();
+            _mFields.pop_back();
+        }
+
+        // done if history is now empty
+        if (_mFields.empty()) {
+            // we could be traversing a sub-tree so clear head
+            mHead.reset();
+            return;
+        }
+
+        // move to the next field in the current struct
+        C2StructDescriptor::field_iterator next = _mFields.back();
+        mHead->field = OffsetFieldDescriptor(*next, GetParentOffset(mHead->parent));
+        mHead->index = 0; // reset index just in case for correctness
+        mHead->baseFieldOffset = GetParentBaseFieldOffset(mHead->parent) + GetOffset(*next);
+        mHead->arrayOffset = GetOffset(mHead->field);
+        mHead->usedExtent = mHead->field.extent() ? :
+                (std::max(mHead->arrayOffset, mParamSize) - mHead->arrayOffset)
+                        / GetSize(mHead->field);
+        ++_mFields.back();
+    }
+
+private:
+    /// If the current field is a known, valid (untraversed) structure, it modifies this iterator
+    /// to point to the first field of the structure and returns true. Otherwise, it does not
+    /// modify this iterator and returns false.
+    bool descendInto(C2Param::CoreIndex index) {
+        std::unique_ptr<C2StructDescriptor> descUnique = mReflector->describe(index);
+        // descend into known structs (as long as they have at least one field)
+        if (descUnique && descUnique->begin() != descUnique->end()) {
+            std::shared_ptr<C2StructDescriptor> desc(std::move(descUnique));
+            mHistory.emplace_back(desc);
+            C2StructDescriptor::field_iterator first = desc->begin();
+            mHead = makeLeaf(*first, 0 /* index */);
+            _mFields.emplace_back(++first);
+            return true;
+        }
+        return false;
+    }
+
+    /// next field pointers for each depth.
+    /// note: _mFields may be shorted than mHistory, if iterating at a depth
+    std::vector<C2StructDescriptor::field_iterator> _mFields;
+};
+
+/**
+ * Iterable implementing enumerateFields().
+ */
+struct C2FieldUtilsFieldIterable : public C2FieldUtils::List::Impl {
+    /// returns an iterator to the beginning of the list
+    virtual std::shared_ptr<C2FieldUtils::Iterator::Impl> begin() const override {
+        return std::make_shared<C2FieldUtilsFieldsIterator>(*_mParam, _mReflector);
+    };
+
+    C2FieldUtilsFieldIterable(const C2Param &param, std::shared_ptr<C2ParamReflector> reflector)
+        : _mParam(&param), _mReflector(reflector) { }
+
+private:
+    const C2Param *_mParam;
+    std::shared_ptr<C2ParamReflector> _mReflector;
+};
+
+}
+
+C2FieldUtils::List C2FieldUtils::enumerateFields(
+        const C2Param &param, const std::shared_ptr<C2ParamReflector> &reflector) {
+    return C2FieldUtils::List(std::make_shared<C2FieldUtilsFieldIterable>(param, reflector));
+}
+
+/* ------------------------- C2FieldUtils::enumerate siblings ------------------------- */
+
+namespace {
+
+struct C2FieldUtilsCousinsIterator : public C2FieldUtils::Iterator::Impl {
+    C2FieldUtilsCousinsIterator(
+                const std::shared_ptr<C2FieldUtils::Info::Impl> &info, size_t level)
+          // clone info chain as this iterator will change it
+        : C2FieldUtils::Iterator::Impl(C2FieldUtils::Info::Impl::Clone(info)) {
+        if (level == 0) {
+            return;
+        }
+
+        // store parent chain (up to level) for quick access
+        std::shared_ptr<C2FieldUtils::Info::Impl> node = mHead;
+        size_t ix = 0;
+        for (; ix < level && node; ++ix) {
+            node->index = 0;
+            _mPath.emplace_back(node);
+            node = node->parent;
+        }
+        setupPath(ix);
+    }
+
+    virtual ~C2FieldUtilsCousinsIterator() override = default;
+
+    /// Increments this iterator by visiting each index.
+    virtual void increment() override {
+        size_t ix = 0;
+        while (ix < _mPath.size()) {
+            if (++_mPath[ix]->index < _mPath[ix]->usedExtent) {
+                setupPath(ix + 1);
+                return;
+            }
+            _mPath[ix++]->index = 0;
+        }
+        mHead.reset();
+    }
+
+private:
+    /// adjusts field offsets along the path up to the specific level - 1.
+    /// This in-fact has to be done down the path from parent to child as child fields must
+    /// fall within parent fields.
+    void setupPath(size_t level) {
+        C2_CHECK_LE(level, _mPath.size());
+        uint32_t oldArrayOffset = level ? _mPath[level - 1]->arrayOffset : 0 /* unused */;
+        while (level) {
+            --level;
+            C2FieldUtils::Info::Impl &path = *_mPath[level];
+            uint32_t size = GetSize(path.field);
+            uint32_t offset = path.arrayOffset + size * path.index;
+            SetOffset(path.field, offset);
+            if (level) {
+                // reset child's array offset to fall within current index, but hold onto the
+                // original value of the arrayOffset so that we can adjust subsequent children.
+                // This is because the modulo is only defined within the current array.
+                uint32_t childArrayOffset =
+                    offset + (_mPath[level - 1]->arrayOffset - oldArrayOffset) % size;
+                oldArrayOffset = _mPath[level - 1]->arrayOffset;
+                _mPath[level - 1]->arrayOffset = childArrayOffset;
+            }
+        }
+    }
+
+    std::vector<std::shared_ptr<C2FieldUtils::Info::Impl>> _mPath;
+};
+
+/**
+ * Iterable implementing enumerateFields().
+ */
+struct C2FieldUtilsCousinsIterable : public C2FieldUtils::List::Impl {
+    /// returns an iterator to the beginning of the list
+    virtual std::shared_ptr<C2FieldUtils::Iterator::Impl> begin() const override {
+        return std::make_shared<C2FieldUtilsCousinsIterator>(_mHead, _mLevel);
+    };
+
+    C2FieldUtilsCousinsIterable(const C2FieldUtils::Info &field, uint32_t level)
+        : _mHead(C2FieldUtils::_Inspector::GetImpl(field)), _mLevel(level) { }
+
+private:
+    std::shared_ptr<C2FieldUtils::Info::Impl> _mHead;
+    size_t _mLevel;
+};
+
+}
+
+C2FieldUtils::List C2FieldUtils::enumerateCousins(const C2FieldUtils::Info &field, uint32_t level) {
+    return C2FieldUtils::List(std::make_shared<C2FieldUtilsCousinsIterable>(field, level));
+}
+
+/* ------------------------- C2FieldUtils::locateField ------------------------- */
+
+namespace {
+
+/**
+ * Iterator implementing locateField().
+ */
+struct C2FieldUtilsFieldLocator : public C2FieldUtilsFieldsIteratorHelper {
+    C2FieldUtilsFieldLocator(
+            C2Param::CoreIndex index, const _C2FieldId &field, uint32_t paramSize,
+            std::shared_ptr<C2ParamReflector> reflector)
+        : C2FieldUtilsFieldsIteratorHelper(reflector, paramSize),
+          _mField(field) {
+        while (descendInto(index)) {
+            if ((mHead->field.type() & C2FieldDescriptor::STRUCT_FLAG) == 0) {
+                break;
+            }
+            index = C2Param::CoreIndex(mHead->field.type() &~ C2FieldDescriptor::STRUCT_FLAG);
+        }
+    }
+
+    void increment() {
+        mHead = _mTail;
+        _mTail = nullptr;
+    }
+
+private:
+    /// If the current field is a known, valid (untraversed) structure, it modifies this iterator
+    /// to point to the field at the beginning/end of the given field of the structure and returns
+    /// true. Otherwise, including if no such field exists in the structure, it does not modify this
+    /// iterator and returns false.
+    bool descendInto(C2Param::CoreIndex index) {
+        // check that the boundaries of the field to be located are still within the same parent
+        // field
+        if (mHead != _mTail) {
+            return false;
+        }
+
+        std::unique_ptr<C2StructDescriptor> descUnique = mReflector->describe(index);
+        // descend into known structs (as long as they have at least one field)
+        if (descUnique && descUnique->begin() != descUnique->end()) {
+            std::shared_ptr<C2StructDescriptor> desc(std::move(descUnique));
+            mHistory.emplace_back(desc);
+
+            uint32_t parentOffset = GetParentOffset(mHead);
+
+            // locate field using a dummy field descriptor
+            C2FieldDescriptor dummy = {
+                C2FieldDescriptor::BLOB, 1 /* extent */, "name",
+                GetOffset(_mField) - parentOffset, GetSize(_mField)
+            };
+
+            // locate first field where offset is greater than dummy offset (which is one past)
+            auto it = std::upper_bound(
+                    desc->cbegin(), desc->cend(), dummy,
+                    [](const C2FieldDescriptor &a, const C2FieldDescriptor &b) -> bool {
+                return _C2ParamInspector::GetOffset(a) < _C2ParamInspector::GetOffset(b);
+            });
+            if (it == desc->begin()) {
+                // field is prior to first field
+                return false;
+            }
+            --it;
+            const C2FieldDescriptor &field = *it;
+
+            // check that dummy end-offset is within this field
+            uint32_t structSize = std::max(mParamSize, parentOffset) - parentOffset;
+            if (GetEndOffset(dummy) > GetEndOffset(field, structSize)) {
+                return false;
+            }
+
+            uint32_t startIndex = (GetOffset(dummy) - GetOffset(field)) / GetSize(field);
+            uint32_t endIndex =
+                (GetEndOffset(dummy) - GetOffset(field) + GetSize(field) - 1) / GetSize(field);
+            if (endIndex > startIndex) {
+                // Field size could be zero, in which case end index is still on start index.
+                // However, for all other cases, endIndex was rounded up to the next index, so
+                // decrement it.
+                --endIndex;
+            }
+            std::shared_ptr<C2FieldUtils::Info::Impl> startLeaf =
+                makeLeaf(field, startIndex);
+            if (endIndex == startIndex) {
+                _mTail = startLeaf;
+                mHead = startLeaf;
+            } else {
+                _mTail = makeLeaf(field, endIndex);
+                mHead = startLeaf;
+            }
+            return true;
+        }
+        return false;
+    }
+
+    _C2FieldId _mField;
+    std::shared_ptr<C2FieldUtils::Info::Impl> _mTail;
+};
+
+/**
+ * Iterable implementing locateField().
+ */
+struct C2FieldUtilsFieldLocation : public C2FieldUtils::List::Impl {
+    /// returns an iterator to the beginning of the list
+    virtual std::shared_ptr<C2FieldUtils::Iterator::Impl> begin() const override {
+        return std::make_shared<C2FieldUtilsFieldLocator>(
+                _mIndex, _mField, _mParamSize, _mReflector);
+    };
+
+    C2FieldUtilsFieldLocation(
+            const C2ParamField &pf, std::shared_ptr<C2ParamReflector> reflector)
+        : _mIndex(C2Param::CoreIndex(_C2ParamInspector::GetIndex(pf))),
+          _mField(_C2ParamInspector::GetField(pf)),
+          _mParamSize(0),
+          _mReflector(reflector) { }
+
+
+    C2FieldUtilsFieldLocation(
+            const C2Param &param, const _C2FieldId &field,
+            std::shared_ptr<C2ParamReflector> reflector)
+        : _mIndex(param.coreIndex()),
+          _mField(field),
+          _mParamSize(param.size()),
+          _mReflector(reflector) { }
+
+private:
+    C2Param::CoreIndex _mIndex;
+    _C2FieldId _mField;
+    uint32_t _mParamSize;
+    std::shared_ptr<C2ParamReflector> _mReflector;
+};
+
+}
+
+std::vector<C2FieldUtils::Info> C2FieldUtils::locateField(
+        const C2ParamField &pf, const std::shared_ptr<C2ParamReflector> &reflector) {
+    C2FieldUtils::List location = { std::make_shared<C2FieldUtilsFieldLocation>(pf, reflector) };
+    return std::vector<Info>(location.begin(), location.end());
+}
+
+std::vector<C2FieldUtils::Info> C2FieldUtils::locateField(
+        const C2Param &param, const _C2FieldId &field,
+        const std::shared_ptr<C2ParamReflector> &reflector) {
+    C2FieldUtils::List location = {
+        std::make_shared<C2FieldUtilsFieldLocation>(param, field, reflector)
+    };
+    return std::vector<Info>(location.begin(), location.end());
+}
+
diff --git a/media/codec2/vndk/util/C2ParamUtils.cpp b/media/codec2/vndk/util/C2ParamUtils.cpp
new file mode 100644
index 0000000..80573d8
--- /dev/null
+++ b/media/codec2/vndk/util/C2ParamUtils.cpp
@@ -0,0 +1,209 @@
+/*
+ * Copyright (C) 2016 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 __C2_GENERATE_GLOBAL_VARS__ // to be able to implement the methods defined
+#include <C2Enum.h>
+#include <util/C2Debug-log.h>
+#include <util/C2ParamUtils.h>
+
+#include <utility>
+#include <vector>
+
+/** \file
+ * Utilities for parameter handling to be used by Codec2 implementations.
+ */
+
+/// \cond INTERNAL
+
+/* ---------------------------- UTILITIES FOR ENUMERATION REFLECTION ---------------------------- */
+
+static size_t countLeadingUnderscores(C2StringLiteral a) {
+    size_t i = 0;
+    while (a[i] == '_') {
+        ++i;
+    }
+    return i;
+}
+
+static size_t countMatching(C2StringLiteral a, const C2String &b) {
+    for (size_t i = 0; i < b.size(); ++i) {
+        if (!a[i] || a[i] != b[i]) {
+            return i;
+        }
+    }
+    return b.size();
+}
+
+// ABCDef => abc-def
+// ABCD2ef => abcd2-ef // 0
+// ABCD2Ef => ancd2-ef // -1
+// AbcDef => abc-def // -1
+// Abc2Def => abc-2def
+// Abc2def => abc-2-def
+// _Yo => _yo
+// _yo => _yo
+// C2_yo => c2-yo
+// C2__yo => c2-yo
+
+//static
+C2String _C2EnumUtils::camelCaseToDashed(C2String name) {
+    enum {
+        kNone = '.',
+        kLower = 'a',
+        kUpper = 'A',
+        kDigit = '1',
+        kDash = '-',
+        kUnderscore = '_',
+    } type = kNone;
+    size_t word_start = 0;
+    for (size_t ix = 0; ix < name.size(); ++ix) {
+        C2_LOG(VERBOSE) << name.substr(0, word_start) << "|"
+                << name.substr(word_start, ix - word_start) << "["
+                << name.substr(ix, 1) << "]" << name.substr(ix + 1)
+                << ": " << (char)type;
+        if (isupper(name[ix])) {
+            if (type == kLower) {
+                name.insert(ix++, 1, '-');
+                word_start = ix;
+            }
+            name[ix] = tolower(name[ix]);
+            type = kUpper;
+        } else if (islower(name[ix])) {
+            if (type == kDigit && ix > 0) {
+                name.insert(ix++, 1, '-');
+                word_start = ix;
+            } else if (type == kUpper && ix > word_start + 1) {
+                name.insert(ix++ - 1, 1, '-');
+                word_start = ix - 1;
+            }
+            type = kLower;
+        } else if (isdigit(name[ix])) {
+            if (type == kLower) {
+                name.insert(ix++, 1, '-');
+                word_start = ix;
+            }
+            type = kDigit;
+        } else if (name[ix] == '_') {
+            if (type == kDash) {
+                name.erase(ix--, 1);
+            } else if (type != kNone && type != kUnderscore) {
+                name[ix] = '-';
+                type = kDash;
+                word_start = ix + 1;
+            } else {
+                type = kUnderscore;
+                word_start = ix + 1;
+            }
+        } else {
+            name.resize(ix);
+        }
+    }
+    C2_LOG(VERBOSE) << "=> " << name;
+    return name;
+}
+
+//static
+std::vector<C2String> _C2EnumUtils::sanitizeEnumValueNames(
+        const std::vector<C2StringLiteral> names,
+        C2StringLiteral _prefix) {
+    std::vector<C2String> sanitizedNames;
+    C2String prefix;
+    size_t extraUnderscores = 0;
+    bool first = true;
+    if (_prefix) {
+        extraUnderscores = countLeadingUnderscores(_prefix);
+        prefix = _prefix + extraUnderscores;
+        first = false;
+        C2_LOG(VERBOSE) << "prefix:" << prefix << ", underscores:" << extraUnderscores;
+    }
+
+    // calculate prefix and minimum leading underscores
+    for (C2StringLiteral s : names) {
+        C2_LOG(VERBOSE) << s;
+        size_t underscores = countLeadingUnderscores(s);
+        if (first) {
+            extraUnderscores = underscores;
+            prefix = s + underscores;
+            first = false;
+        } else {
+            size_t matching = countMatching(
+                s + underscores,
+                prefix);
+            prefix.resize(matching);
+            extraUnderscores = std::min(underscores, extraUnderscores);
+        }
+        C2_LOG(VERBOSE) << "prefix:" << prefix << ", underscores:" << extraUnderscores;
+        if (prefix.size() == 0 && extraUnderscores == 0) {
+            break;
+        }
+    }
+
+    // we swallow the first underscore after upper case prefixes
+    bool upperCasePrefix = true;
+    for (size_t i = 0; i < prefix.size(); ++i) {
+        if (islower(prefix[i])) {
+            upperCasePrefix = false;
+            break;
+        }
+    }
+
+    for (C2StringLiteral s : names) {
+        size_t underscores = countLeadingUnderscores(s);
+        C2String sanitized = C2String(s, underscores - extraUnderscores);
+        sanitized.append(s + prefix.size() + underscores +
+                    (upperCasePrefix && s[prefix.size() + underscores] == '_'));
+        sanitizedNames.push_back(camelCaseToDashed(sanitized));
+    }
+
+    for (C2String s : sanitizedNames) {
+        C2_LOG(VERBOSE) << s;
+    }
+
+    return sanitizedNames;
+}
+
+//static
+std::vector<C2String> _C2EnumUtils::parseEnumValuesFromString(C2StringLiteral value) {
+    std::vector<C2String> foundNames;
+    size_t pos = 0, len = strlen(value);
+    do {
+        size_t endPos = strcspn(value + pos, " ,=") + pos;
+        if (endPos > pos) {
+            foundNames.emplace_back(value + pos, endPos - pos);
+        }
+        if (value[endPos] && value[endPos] != ',') {
+            endPos += strcspn(value + endPos, ",");
+        }
+        pos = strspn(value + endPos, " ,") + endPos;
+    } while (pos < len);
+    return foundNames;
+}
+
+/// safe(r) parsing from parameter blob
+//static
+C2Param *C2ParamUtils::ParseFirst(const uint8_t *blob, size_t size) {
+    // _mSize must fit into size, but really C2Param must also to be a valid param
+    if (size < sizeof(C2Param)) {
+        return nullptr;
+    }
+    // _mSize must match length
+    C2Param *param = (C2Param*)blob;
+    if (param->size() > size) {
+        return nullptr;
+    }
+    return param;
+}
+