Move Codec2-related code from hardware/google/av

Test: None
Bug: 112362730
Change-Id: Ie2f8ff431d65c40333f267ab9877d47089adeea4
diff --git a/media/codec2/components/base/Android.bp b/media/codec2/components/base/Android.bp
new file mode 100644
index 0000000..ad456e2
--- /dev/null
+++ b/media/codec2/components/base/Android.bp
@@ -0,0 +1,141 @@
+// DO NOT DEPEND ON THIS DIRECTLY
+// use libstagefright_soft_c2-defaults instead
+cc_library_shared {
+    name: "libstagefright_soft_c2common",
+    defaults: ["libstagefright_codec2-impl-defaults"],
+    vendor_available: true,
+
+    srcs: [
+        "SimpleC2Component.cpp",
+        "SimpleC2Interface.cpp",
+    ],
+
+    export_include_dirs: [
+        "include",
+    ],
+
+    export_shared_lib_headers: [
+        "libstagefright_ccodec_utils",
+    ],
+
+    shared_libs: [
+        "libcutils", // for properties
+        "liblog",    // for ALOG
+        "libstagefright_ccodec_utils", // for ImageCopy
+        "libstagefright_foundation", // for Mutexed
+    ],
+
+    sanitize: {
+        misc_undefined: [
+            "unsigned-integer-overflow",
+            "signed-integer-overflow",
+        ],
+        cfi: true,
+        diag: {
+            cfi: true,
+        },
+    },
+
+    ldflags: ["-Wl,-Bsymbolic"],
+}
+
+// public dependency for software codec implementation
+// to be used by code under media/codecs/* only as its stability is not guaranteed
+cc_defaults {
+    name: "libstagefright_soft_c2-defaults",
+    defaults: ["libstagefright_codec2-impl-defaults"],
+    vendor_available: true,
+
+    export_shared_lib_headers: [
+        "libstagefright_ccodec_utils",
+    ],
+
+    shared_libs: [
+        "libcutils", // for properties
+        "liblog", // for ALOG
+        "libstagefright_foundation", // for ColorUtils and MIME
+        "libstagefright_ccodec_utils", // for ImageCopy
+        "libstagefright_soft_c2common",
+    ],
+
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+
+    ldflags: ["-Wl,-Bsymbolic"],
+}
+
+// public dependency for software codec implementation
+// to be used by code under media/codecs/* only
+cc_defaults {
+    name: "libstagefright_soft_c2_sanitize_all-defaults",
+
+    sanitize: {
+        misc_undefined: [
+            "unsigned-integer-overflow",
+            "signed-integer-overflow",
+        ],
+        cfi: true,
+        diag: {
+            cfi: true,
+        },
+    },
+}
+
+// public dependency for software codec implementation
+// to be used by code under media/codecs/* only
+cc_defaults {
+    name: "libstagefright_soft_c2_sanitize_signed-defaults",
+
+    sanitize: {
+        misc_undefined: [
+            "signed-integer-overflow",
+        ],
+        cfi: true,
+        diag: {
+            cfi: true,
+        },
+    },
+}
+
+// TEMP: used by cheets2 project - remove when no longer used
+cc_library_shared {
+    name: "libstagefright_simple_c2component",
+    vendor_available: true,
+
+    srcs: [
+        "SimpleC2Interface.cpp",
+    ],
+
+    local_include_dirs: [
+        "include",
+    ],
+
+    export_include_dirs: [
+        "include",
+    ],
+
+    shared_libs: [
+        "libcutils",
+        "liblog",
+        "libstagefright_codec2",
+        "libstagefright_codec2_vndk",
+        "libstagefright_foundation",
+        "libutils",
+    ],
+
+    sanitize: {
+        misc_undefined: [
+            "unsigned-integer-overflow",
+            "signed-integer-overflow",
+        ],
+        cfi: true,
+        diag: {
+            cfi: true,
+        },
+    },
+
+    ldflags: ["-Wl,-Bsymbolic"],
+}
+
diff --git a/media/codec2/components/base/SimpleC2Component.cpp b/media/codec2/components/base/SimpleC2Component.cpp
new file mode 100644
index 0000000..7990ee5
--- /dev/null
+++ b/media/codec2/components/base/SimpleC2Component.cpp
@@ -0,0 +1,562 @@
+/*
+ * 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 "SimpleC2Component"
+#include <log/log.h>
+
+#include <cutils/properties.h>
+#include <media/stagefright/foundation/AMessage.h>
+
+#include <inttypes.h>
+
+#include <C2Config.h>
+#include <C2Debug.h>
+#include <C2PlatformSupport.h>
+#include <SimpleC2Component.h>
+
+namespace android {
+
+std::unique_ptr<C2Work> SimpleC2Component::WorkQueue::pop_front() {
+    std::unique_ptr<C2Work> work = std::move(mQueue.front().work);
+    mQueue.pop_front();
+    return work;
+}
+
+void SimpleC2Component::WorkQueue::push_back(std::unique_ptr<C2Work> work) {
+    mQueue.push_back({ std::move(work), NO_DRAIN });
+}
+
+bool SimpleC2Component::WorkQueue::empty() const {
+    return mQueue.empty();
+}
+
+void SimpleC2Component::WorkQueue::clear() {
+    mQueue.clear();
+}
+
+uint32_t SimpleC2Component::WorkQueue::drainMode() const {
+    return mQueue.front().drainMode;
+}
+
+void SimpleC2Component::WorkQueue::markDrain(uint32_t drainMode) {
+    mQueue.push_back({ nullptr, drainMode });
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+SimpleC2Component::WorkHandler::WorkHandler() : mRunning(false) {}
+
+void SimpleC2Component::WorkHandler::setComponent(
+        const std::shared_ptr<SimpleC2Component> &thiz) {
+    mThiz = thiz;
+}
+
+static void Reply(const sp<AMessage> &msg, int32_t *err = nullptr) {
+    sp<AReplyToken> replyId;
+    CHECK(msg->senderAwaitsResponse(&replyId));
+    sp<AMessage> reply = new AMessage;
+    if (err) {
+        reply->setInt32("err", *err);
+    }
+    reply->postReply(replyId);
+}
+
+void SimpleC2Component::WorkHandler::onMessageReceived(const sp<AMessage> &msg) {
+    std::shared_ptr<SimpleC2Component> thiz = mThiz.lock();
+    if (!thiz) {
+        ALOGD("component not yet set; msg = %s", msg->debugString().c_str());
+        sp<AReplyToken> replyId;
+        if (msg->senderAwaitsResponse(&replyId)) {
+            sp<AMessage> reply = new AMessage;
+            reply->setInt32("err", C2_CORRUPTED);
+            reply->postReply(replyId);
+        }
+        return;
+    }
+
+    switch (msg->what()) {
+        case kWhatProcess: {
+            if (mRunning) {
+                if (thiz->processQueue()) {
+                    (new AMessage(kWhatProcess, this))->post();
+                }
+            } else {
+                ALOGV("Ignore process message as we're not running");
+            }
+            break;
+        }
+        case kWhatInit: {
+            int32_t err = thiz->onInit();
+            Reply(msg, &err);
+            [[fallthrough]];
+        }
+        case kWhatStart: {
+            mRunning = true;
+            break;
+        }
+        case kWhatStop: {
+            int32_t err = thiz->onStop();
+            Reply(msg, &err);
+            break;
+        }
+        case kWhatReset: {
+            thiz->onReset();
+            mRunning = false;
+            Reply(msg);
+            break;
+        }
+        case kWhatRelease: {
+            thiz->onRelease();
+            mRunning = false;
+            Reply(msg);
+            break;
+        }
+        default: {
+            ALOGD("Unrecognized msg: %d", msg->what());
+            break;
+        }
+    }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+namespace {
+
+struct DummyReadView : public C2ReadView {
+    DummyReadView() : C2ReadView(C2_NO_INIT) {}
+};
+
+}  // namespace
+
+SimpleC2Component::SimpleC2Component(
+        const std::shared_ptr<C2ComponentInterface> &intf)
+    : mDummyReadView(DummyReadView()),
+      mIntf(intf),
+      mLooper(new ALooper),
+      mHandler(new WorkHandler) {
+    mLooper->setName(intf->getName().c_str());
+    (void)mLooper->registerHandler(mHandler);
+    mLooper->start(false, false, ANDROID_PRIORITY_VIDEO);
+}
+
+SimpleC2Component::~SimpleC2Component() {
+    mLooper->unregisterHandler(mHandler->id());
+    (void)mLooper->stop();
+}
+
+c2_status_t SimpleC2Component::setListener_vb(
+        const std::shared_ptr<C2Component::Listener> &listener, c2_blocking_t mayBlock) {
+    mHandler->setComponent(shared_from_this());
+
+    Mutexed<ExecState>::Locked state(mExecState);
+    if (state->mState == RUNNING) {
+        if (listener) {
+            return C2_BAD_STATE;
+        } else if (!mayBlock) {
+            return C2_BLOCKING;
+        }
+    }
+    state->mListener = listener;
+    // TODO: wait for listener change to have taken place before returning
+    // (e.g. if there is an ongoing listener callback)
+    return C2_OK;
+}
+
+c2_status_t SimpleC2Component::queue_nb(std::list<std::unique_ptr<C2Work>> * const items) {
+    {
+        Mutexed<ExecState>::Locked state(mExecState);
+        if (state->mState != RUNNING) {
+            return C2_BAD_STATE;
+        }
+    }
+    bool queueWasEmpty = false;
+    {
+        Mutexed<WorkQueue>::Locked queue(mWorkQueue);
+        queueWasEmpty = queue->empty();
+        while (!items->empty()) {
+            queue->push_back(std::move(items->front()));
+            items->pop_front();
+        }
+    }
+    if (queueWasEmpty) {
+        (new AMessage(WorkHandler::kWhatProcess, mHandler))->post();
+    }
+    return C2_OK;
+}
+
+c2_status_t SimpleC2Component::announce_nb(const std::vector<C2WorkOutline> &items) {
+    (void)items;
+    return C2_OMITTED;
+}
+
+c2_status_t SimpleC2Component::flush_sm(
+        flush_mode_t flushMode, std::list<std::unique_ptr<C2Work>>* const flushedWork) {
+    (void)flushMode;
+    {
+        Mutexed<ExecState>::Locked state(mExecState);
+        if (state->mState != RUNNING) {
+            return C2_BAD_STATE;
+        }
+    }
+    {
+        Mutexed<WorkQueue>::Locked queue(mWorkQueue);
+        queue->incGeneration();
+        // TODO: queue->splicedBy(flushedWork, flushedWork->end());
+        while (!queue->empty()) {
+            std::unique_ptr<C2Work> work = queue->pop_front();
+            if (work) {
+                flushedWork->push_back(std::move(work));
+            }
+        }
+    }
+    {
+        Mutexed<PendingWork>::Locked pending(mPendingWork);
+        while (!pending->empty()) {
+            flushedWork->push_back(std::move(pending->begin()->second));
+            pending->erase(pending->begin());
+        }
+    }
+
+    return C2_OK;
+}
+
+c2_status_t SimpleC2Component::drain_nb(drain_mode_t drainMode) {
+    if (drainMode == DRAIN_CHAIN) {
+        return C2_OMITTED;
+    }
+    {
+        Mutexed<ExecState>::Locked state(mExecState);
+        if (state->mState != RUNNING) {
+            return C2_BAD_STATE;
+        }
+    }
+    bool queueWasEmpty = false;
+    {
+        Mutexed<WorkQueue>::Locked queue(mWorkQueue);
+        queueWasEmpty = queue->empty();
+        queue->markDrain(drainMode);
+    }
+    if (queueWasEmpty) {
+        (new AMessage(WorkHandler::kWhatProcess, mHandler))->post();
+    }
+
+    return C2_OK;
+}
+
+c2_status_t SimpleC2Component::start() {
+    Mutexed<ExecState>::Locked state(mExecState);
+    if (state->mState == RUNNING) {
+        return C2_BAD_STATE;
+    }
+    bool needsInit = (state->mState == UNINITIALIZED);
+    state.unlock();
+    if (needsInit) {
+        sp<AMessage> reply;
+        (new AMessage(WorkHandler::kWhatInit, mHandler))->postAndAwaitResponse(&reply);
+        int32_t err;
+        CHECK(reply->findInt32("err", &err));
+        if (err != C2_OK) {
+            return (c2_status_t)err;
+        }
+    } else {
+        (new AMessage(WorkHandler::kWhatStart, mHandler))->post();
+    }
+    state.lock();
+    state->mState = RUNNING;
+    return C2_OK;
+}
+
+c2_status_t SimpleC2Component::stop() {
+    ALOGV("stop");
+    {
+        Mutexed<ExecState>::Locked state(mExecState);
+        if (state->mState != RUNNING) {
+            return C2_BAD_STATE;
+        }
+        state->mState = STOPPED;
+    }
+    {
+        Mutexed<WorkQueue>::Locked queue(mWorkQueue);
+        queue->clear();
+    }
+    {
+        Mutexed<PendingWork>::Locked pending(mPendingWork);
+        pending->clear();
+    }
+    sp<AMessage> reply;
+    (new AMessage(WorkHandler::kWhatStop, mHandler))->postAndAwaitResponse(&reply);
+    int32_t err;
+    CHECK(reply->findInt32("err", &err));
+    if (err != C2_OK) {
+        return (c2_status_t)err;
+    }
+    return C2_OK;
+}
+
+c2_status_t SimpleC2Component::reset() {
+    ALOGV("reset");
+    {
+        Mutexed<ExecState>::Locked state(mExecState);
+        state->mState = UNINITIALIZED;
+    }
+    {
+        Mutexed<WorkQueue>::Locked queue(mWorkQueue);
+        queue->clear();
+    }
+    {
+        Mutexed<PendingWork>::Locked pending(mPendingWork);
+        pending->clear();
+    }
+    sp<AMessage> reply;
+    (new AMessage(WorkHandler::kWhatReset, mHandler))->postAndAwaitResponse(&reply);
+    return C2_OK;
+}
+
+c2_status_t SimpleC2Component::release() {
+    ALOGV("release");
+    sp<AMessage> reply;
+    (new AMessage(WorkHandler::kWhatRelease, mHandler))->postAndAwaitResponse(&reply);
+    return C2_OK;
+}
+
+std::shared_ptr<C2ComponentInterface> SimpleC2Component::intf() {
+    return mIntf;
+}
+
+namespace {
+
+std::list<std::unique_ptr<C2Work>> vec(std::unique_ptr<C2Work> &work) {
+    std::list<std::unique_ptr<C2Work>> ret;
+    ret.push_back(std::move(work));
+    return ret;
+}
+
+}  // namespace
+
+void SimpleC2Component::finish(
+        uint64_t frameIndex, std::function<void(const std::unique_ptr<C2Work> &)> fillWork) {
+    std::unique_ptr<C2Work> work;
+    {
+        Mutexed<PendingWork>::Locked pending(mPendingWork);
+        if (pending->count(frameIndex) == 0) {
+            ALOGW("unknown frame index: %" PRIu64, frameIndex);
+            return;
+        }
+        work = std::move(pending->at(frameIndex));
+        pending->erase(frameIndex);
+    }
+    if (work) {
+        fillWork(work);
+        std::shared_ptr<C2Component::Listener> listener = mExecState.lock()->mListener;
+        listener->onWorkDone_nb(shared_from_this(), vec(work));
+        ALOGV("returning pending work");
+    }
+}
+
+void SimpleC2Component::cloneAndSend(
+        uint64_t frameIndex,
+        const std::unique_ptr<C2Work> &currentWork,
+        std::function<void(const std::unique_ptr<C2Work> &)> fillWork) {
+    std::unique_ptr<C2Work> work(new C2Work);
+    if (currentWork->input.ordinal.frameIndex == frameIndex) {
+        work->input.flags = currentWork->input.flags;
+        work->input.ordinal = currentWork->input.ordinal;
+    } else {
+        Mutexed<PendingWork>::Locked pending(mPendingWork);
+        if (pending->count(frameIndex) == 0) {
+            ALOGW("unknown frame index: %" PRIu64, frameIndex);
+            return;
+        }
+        work->input.flags = pending->at(frameIndex)->input.flags;
+        work->input.ordinal = pending->at(frameIndex)->input.ordinal;
+    }
+    work->worklets.emplace_back(new C2Worklet);
+    if (work) {
+        fillWork(work);
+        std::shared_ptr<C2Component::Listener> listener = mExecState.lock()->mListener;
+        listener->onWorkDone_nb(shared_from_this(), vec(work));
+        ALOGV("cloned and sending work");
+    }
+}
+
+bool SimpleC2Component::processQueue() {
+    std::unique_ptr<C2Work> work;
+    uint64_t generation;
+    int32_t drainMode;
+    bool isFlushPending = false;
+    bool hasQueuedWork = false;
+    {
+        Mutexed<WorkQueue>::Locked queue(mWorkQueue);
+        if (queue->empty()) {
+            return false;
+        }
+
+        generation = queue->generation();
+        drainMode = queue->drainMode();
+        isFlushPending = queue->popPendingFlush();
+        work = queue->pop_front();
+        hasQueuedWork = !queue->empty();
+    }
+    if (isFlushPending) {
+        ALOGV("processing pending flush");
+        c2_status_t err = onFlush_sm();
+        if (err != C2_OK) {
+            ALOGD("flush err: %d", err);
+            // TODO: error
+        }
+    }
+
+    if (!mOutputBlockPool) {
+        c2_status_t err = [this] {
+            // TODO: don't use query_vb
+            C2StreamFormatConfig::output outputFormat(0u);
+            std::vector<std::unique_ptr<C2Param>> params;
+            c2_status_t err = intf()->query_vb(
+                    { &outputFormat },
+                    { C2PortBlockPoolsTuning::output::PARAM_TYPE },
+                    C2_DONT_BLOCK,
+                    &params);
+            if (err != C2_OK && err != C2_BAD_INDEX) {
+                ALOGD("query err = %d", err);
+                return err;
+            }
+            C2BlockPool::local_id_t poolId =
+                outputFormat.value == C2FormatVideo
+                        ? C2BlockPool::BASIC_GRAPHIC
+                        : C2BlockPool::BASIC_LINEAR;
+            if (params.size()) {
+                C2PortBlockPoolsTuning::output *outputPools =
+                    C2PortBlockPoolsTuning::output::From(params[0].get());
+                if (outputPools && outputPools->flexCount() >= 1) {
+                    poolId = outputPools->m.values[0];
+                }
+            }
+
+            err = GetCodec2BlockPool(poolId, shared_from_this(), &mOutputBlockPool);
+            ALOGD("Using output block pool with poolID %llu => got %llu - %d",
+                    (unsigned long long)poolId,
+                    (unsigned long long)(
+                            mOutputBlockPool ? mOutputBlockPool->getLocalId() : 111000111),
+                    err);
+            return err;
+        }();
+        if (err != C2_OK) {
+            Mutexed<ExecState>::Locked state(mExecState);
+            std::shared_ptr<C2Component::Listener> listener = state->mListener;
+            state.unlock();
+            listener->onError_nb(shared_from_this(), err);
+            return hasQueuedWork;
+        }
+    }
+
+    if (!work) {
+        c2_status_t err = drain(drainMode, mOutputBlockPool);
+        if (err != C2_OK) {
+            Mutexed<ExecState>::Locked state(mExecState);
+            std::shared_ptr<C2Component::Listener> listener = state->mListener;
+            state.unlock();
+            listener->onError_nb(shared_from_this(), err);
+        }
+        return hasQueuedWork;
+    }
+
+    {
+        std::vector<C2Param *> updates;
+        for (const std::unique_ptr<C2Param> &param: work->input.configUpdate) {
+            if (param) {
+                updates.emplace_back(param.get());
+            }
+        }
+        if (!updates.empty()) {
+            std::vector<std::unique_ptr<C2SettingResult>> failures;
+            c2_status_t err = intf()->config_vb(updates, C2_MAY_BLOCK, &failures);
+            ALOGD("applied %zu configUpdates => %s (%d)", updates.size(), asString(err), err);
+        }
+    }
+
+    ALOGV("start processing frame #%" PRIu64, work->input.ordinal.frameIndex.peeku());
+    process(work, mOutputBlockPool);
+    ALOGV("processed frame #%" PRIu64, work->input.ordinal.frameIndex.peeku());
+    {
+        Mutexed<WorkQueue>::Locked queue(mWorkQueue);
+        if (queue->generation() != generation) {
+            ALOGD("work form old generation: was %" PRIu64 " now %" PRIu64,
+                    queue->generation(), generation);
+            work->result = C2_NOT_FOUND;
+            queue.unlock();
+            {
+                Mutexed<ExecState>::Locked state(mExecState);
+                std::shared_ptr<C2Component::Listener> listener = state->mListener;
+                state.unlock();
+                listener->onWorkDone_nb(shared_from_this(), vec(work));
+            }
+            queue.lock();
+            return hasQueuedWork;
+        }
+    }
+    if (work->workletsProcessed != 0u) {
+        Mutexed<ExecState>::Locked state(mExecState);
+        ALOGV("returning this work");
+        std::shared_ptr<C2Component::Listener> listener = state->mListener;
+        state.unlock();
+        listener->onWorkDone_nb(shared_from_this(), vec(work));
+    } else {
+        ALOGV("queue pending work");
+        work->input.buffers.clear();
+        std::unique_ptr<C2Work> unexpected;
+        {
+            Mutexed<PendingWork>::Locked pending(mPendingWork);
+            uint64_t frameIndex = work->input.ordinal.frameIndex.peeku();
+            if (pending->count(frameIndex) != 0) {
+                unexpected = std::move(pending->at(frameIndex));
+                pending->erase(frameIndex);
+            }
+            (void)pending->insert({ frameIndex, std::move(work) });
+        }
+        if (unexpected) {
+            ALOGD("unexpected pending work");
+            unexpected->result = C2_CORRUPTED;
+            Mutexed<ExecState>::Locked state(mExecState);
+            std::shared_ptr<C2Component::Listener> listener = state->mListener;
+            state.unlock();
+            listener->onWorkDone_nb(shared_from_this(), vec(unexpected));
+        }
+    }
+    return hasQueuedWork;
+}
+
+std::shared_ptr<C2Buffer> SimpleC2Component::createLinearBuffer(
+        const std::shared_ptr<C2LinearBlock> &block) {
+    return createLinearBuffer(block, block->offset(), block->size());
+}
+
+std::shared_ptr<C2Buffer> SimpleC2Component::createLinearBuffer(
+        const std::shared_ptr<C2LinearBlock> &block, size_t offset, size_t size) {
+    return C2Buffer::CreateLinearBuffer(block->share(offset, size, ::C2Fence()));
+}
+
+std::shared_ptr<C2Buffer> SimpleC2Component::createGraphicBuffer(
+        const std::shared_ptr<C2GraphicBlock> &block) {
+    return createGraphicBuffer(block, C2Rect(block->width(), block->height()));
+}
+
+std::shared_ptr<C2Buffer> SimpleC2Component::createGraphicBuffer(
+        const std::shared_ptr<C2GraphicBlock> &block, const C2Rect &crop) {
+    return C2Buffer::CreateGraphicBuffer(block->share(crop, ::C2Fence()));
+}
+
+} // namespace android
diff --git a/media/codec2/components/base/SimpleC2Interface.cpp b/media/codec2/components/base/SimpleC2Interface.cpp
new file mode 100644
index 0000000..c849a4e
--- /dev/null
+++ b/media/codec2/components/base/SimpleC2Interface.cpp
@@ -0,0 +1,315 @@
+/*
+ * 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 "SimpleC2Interface"
+#include <utils/Log.h>
+
+// use MediaDefs here vs. MediaCodecConstants as this is not MediaCodec specific/dependent
+#include <media/stagefright/foundation/MediaDefs.h>
+
+#include <SimpleC2Interface.h>
+
+namespace android {
+
+/* SimpleInterface */
+
+SimpleInterface<void>::BaseParams::BaseParams(
+        const std::shared_ptr<C2ReflectorHelper> &reflector,
+        C2String name,
+        C2Component::kind_t kind,
+        C2Component::domain_t domain,
+        C2String mediaType,
+        std::vector<C2String> aliases)
+    : C2InterfaceHelper(reflector) {
+    setDerivedInstance(this);
+
+    addParameter(
+            DefineParam(mName, C2_PARAMKEY_COMPONENT_NAME)
+            .withConstValue(AllocSharedString<C2ComponentNameSetting>(name.c_str()))
+            .build());
+
+    if (aliases.size()) {
+        C2String joined;
+        for (const C2String &alias : aliases) {
+            if (joined.length()) {
+                joined += ",";
+            }
+            joined += alias;
+        }
+        addParameter(
+                DefineParam(mAliases, C2_PARAMKEY_COMPONENT_ALIASES)
+                .withConstValue(AllocSharedString<C2ComponentAliasesSetting>(joined.c_str()))
+                .build());
+    }
+
+    addParameter(
+            DefineParam(mKind, C2_PARAMKEY_COMPONENT_KIND)
+            .withConstValue(new C2ComponentKindSetting(kind))
+            .build());
+
+    addParameter(
+            DefineParam(mDomain, C2_PARAMKEY_COMPONENT_DOMAIN)
+            .withConstValue(new C2ComponentDomainSetting(domain))
+            .build());
+
+    // simple interfaces have single streams
+    addParameter(
+            DefineParam(mInputStreamCount, C2_PARAMKEY_INPUT_STREAM_COUNT)
+            .withConstValue(new C2PortStreamCountTuning::input(1))
+            .build());
+
+    addParameter(
+            DefineParam(mOutputStreamCount, C2_PARAMKEY_OUTPUT_STREAM_COUNT)
+            .withConstValue(new C2PortStreamCountTuning::output(1))
+            .build());
+
+    // set up buffer formats and allocators
+
+    // default to linear buffers and no media type
+    C2BufferData::type_t rawBufferType = C2BufferData::LINEAR;
+    C2String rawMediaType;
+    C2Allocator::id_t rawAllocator = C2AllocatorStore::DEFAULT_LINEAR;
+    C2BlockPool::local_id_t rawPoolId = C2BlockPool::BASIC_LINEAR;
+    C2BufferData::type_t codedBufferType = C2BufferData::LINEAR;
+    C2Allocator::id_t codedAllocator = C2AllocatorStore::DEFAULT_LINEAR;
+    C2BlockPool::local_id_t codedPoolId = C2BlockPool::BASIC_LINEAR;
+
+    switch (domain) {
+        case C2Component::DOMAIN_IMAGE:
+        case C2Component::DOMAIN_VIDEO:
+            // TODO: should we define raw image? The only difference is timestamp handling
+            rawBufferType = C2BufferData::GRAPHIC;
+            rawMediaType = MEDIA_MIMETYPE_VIDEO_RAW;
+            rawAllocator = C2AllocatorStore::DEFAULT_GRAPHIC;
+            rawPoolId = C2BlockPool::BASIC_GRAPHIC;
+            break;
+        case C2Component::DOMAIN_AUDIO:
+            rawBufferType = C2BufferData::LINEAR;
+            rawMediaType = MEDIA_MIMETYPE_AUDIO_RAW;
+            rawAllocator = C2AllocatorStore::DEFAULT_LINEAR;
+            rawPoolId = C2BlockPool::BASIC_LINEAR;
+            break;
+        default:
+            break;
+    }
+    bool isEncoder = kind == C2Component::KIND_ENCODER;
+
+    // handle raw decoders
+    if (mediaType == rawMediaType) {
+        codedBufferType = rawBufferType;
+        codedAllocator = rawAllocator;
+        codedPoolId = rawPoolId;
+    }
+
+    addParameter(
+            DefineParam(mInputFormat, C2_PARAMKEY_INPUT_STREAM_BUFFER_TYPE)
+            .withConstValue(new C2StreamBufferTypeSetting::input(
+                    0u, isEncoder ? rawBufferType : codedBufferType))
+            .build());
+
+    addParameter(
+            DefineParam(mInputMediaType, C2_PARAMKEY_INPUT_MEDIA_TYPE)
+            .withConstValue(AllocSharedString<C2PortMediaTypeSetting::input>(
+                    isEncoder ? rawMediaType : mediaType))
+            .build());
+
+    addParameter(
+            DefineParam(mOutputFormat, C2_PARAMKEY_OUTPUT_STREAM_BUFFER_TYPE)
+            .withConstValue(new C2StreamBufferTypeSetting::output(
+                    0u, isEncoder ? codedBufferType : rawBufferType))
+            .build());
+
+    addParameter(
+            DefineParam(mOutputMediaType, C2_PARAMKEY_OUTPUT_MEDIA_TYPE)
+            .withConstValue(AllocSharedString<C2PortMediaTypeSetting::output>(
+                    isEncoder ? mediaType : rawMediaType))
+            .build());
+
+    C2Allocator::id_t inputAllocators[1] = { isEncoder ? rawAllocator : codedAllocator };
+    C2Allocator::id_t outputAllocators[1] = { isEncoder ? codedAllocator : rawAllocator };
+    C2BlockPool::local_id_t outputPoolIds[1] = { isEncoder ? codedPoolId : rawPoolId };
+
+    addParameter(
+            DefineParam(mInputAllocators, C2_PARAMKEY_INPUT_ALLOCATORS)
+            .withDefault(C2PortAllocatorsTuning::input::AllocShared(inputAllocators))
+            .withFields({ C2F(mInputAllocators, m.values[0]).any(),
+                          C2F(mInputAllocators, m.values).inRange(0, 1) })
+            .withSetter(Setter<C2PortAllocatorsTuning::input>::NonStrictValuesWithNoDeps)
+            .build());
+
+    addParameter(
+            DefineParam(mOutputAllocators, C2_PARAMKEY_OUTPUT_ALLOCATORS)
+            .withDefault(C2PortAllocatorsTuning::output::AllocShared(outputAllocators))
+            .withFields({ C2F(mOutputAllocators, m.values[0]).any(),
+                          C2F(mOutputAllocators, m.values).inRange(0, 1) })
+            .withSetter(Setter<C2PortAllocatorsTuning::output>::NonStrictValuesWithNoDeps)
+            .build());
+
+    addParameter(
+            DefineParam(mOutputPoolIds, C2_PARAMKEY_OUTPUT_BLOCK_POOLS)
+            .withDefault(C2PortBlockPoolsTuning::output::AllocShared(outputPoolIds))
+            .withFields({ C2F(mOutputPoolIds, m.values[0]).any(),
+                          C2F(mOutputPoolIds, m.values).inRange(0, 1) })
+            .withSetter(Setter<C2PortBlockPoolsTuning::output>::NonStrictValuesWithNoDeps)
+            .build());
+
+    // add stateless params
+    addParameter(
+            DefineParam(mSubscribedParamIndices, C2_PARAMKEY_SUBSCRIBED_PARAM_INDICES)
+            .withDefault(C2SubscribedParamIndicesTuning::AllocShared(0u))
+            .withFields({ C2F(mSubscribedParamIndices, m.values[0]).any(),
+                          C2F(mSubscribedParamIndices, m.values).any() })
+            .withSetter(Setter<C2SubscribedParamIndicesTuning>::NonStrictValuesWithNoDeps)
+            .build());
+
+    /* TODO
+
+    addParameter(
+            DefineParam(mCurrentWorkOrdinal, C2_PARAMKEY_CURRENT_WORK)
+            .withDefault(new C2CurrentWorkTuning())
+            .withFields({ C2F(mCurrentWorkOrdinal, m.timeStamp).any(),
+                          C2F(mCurrentWorkOrdinal, m.frameIndex).any(),
+                          C2F(mCurrentWorkOrdinal, m.customOrdinal).any() })
+            .withSetter(Setter<C2CurrentWorkTuning>::NonStrictValuesWithNoDeps)
+            .build());
+
+    addParameter(
+            DefineParam(mLastInputQueuedWorkOrdinal, C2_PARAMKEY_LAST_INPUT_QUEUED)
+            .withDefault(new C2LastWorkQueuedTuning::input())
+            .withFields({ C2F(mLastInputQueuedWorkOrdinal, m.timeStamp).any(),
+                          C2F(mLastInputQueuedWorkOrdinal, m.frameIndex).any(),
+                          C2F(mLastInputQueuedWorkOrdinal, m.customOrdinal).any() })
+            .withSetter(Setter<C2LastWorkQueuedTuning::input>::NonStrictValuesWithNoDeps)
+            .build());
+
+    addParameter(
+            DefineParam(mLastOutputQueuedWorkOrdinal, C2_PARAMKEY_LAST_OUTPUT_QUEUED)
+            .withDefault(new C2LastWorkQueuedTuning::output())
+            .withFields({ C2F(mLastOutputQueuedWorkOrdinal, m.timeStamp).any(),
+                          C2F(mLastOutputQueuedWorkOrdinal, m.frameIndex).any(),
+                          C2F(mLastOutputQueuedWorkOrdinal, m.customOrdinal).any() })
+            .withSetter(Setter<C2LastWorkQueuedTuning::output>::NonStrictValuesWithNoDeps)
+            .build());
+
+    std::shared_ptr<C2OutOfMemoryTuning> mOutOfMemory;
+
+    std::shared_ptr<C2PortConfigCounterTuning::input> mInputConfigCounter;
+    std::shared_ptr<C2PortConfigCounterTuning::output> mOutputConfigCounter;
+    std::shared_ptr<C2ConfigCounterTuning> mDirectConfigCounter;
+
+    */
+}
+
+void SimpleInterface<void>::BaseParams::noInputLatency() {
+    addParameter(
+            DefineParam(mRequestedInputDelay, C2_PARAMKEY_INPUT_DELAY_REQUEST)
+            .withConstValue(new C2PortRequestedDelayTuning::input(0u))
+            .build());
+
+    addParameter(
+            DefineParam(mActualInputDelay, C2_PARAMKEY_INPUT_DELAY)
+            .withConstValue(new C2PortActualDelayTuning::input(0u))
+            .build());
+}
+
+void SimpleInterface<void>::BaseParams::noOutputLatency() {
+    addParameter(
+            DefineParam(mRequestedOutputDelay, C2_PARAMKEY_OUTPUT_DELAY_REQUEST)
+            .withConstValue(new C2PortRequestedDelayTuning::output(0u))
+            .build());
+
+    addParameter(
+            DefineParam(mActualOutputDelay, C2_PARAMKEY_OUTPUT_DELAY)
+            .withConstValue(new C2PortActualDelayTuning::output(0u))
+            .build());
+}
+
+void SimpleInterface<void>::BaseParams::noPipelineLatency() {
+    addParameter(
+            DefineParam(mRequestedPipelineDelay, C2_PARAMKEY_PIPELINE_DELAY_REQUEST)
+            .withConstValue(new C2RequestedPipelineDelayTuning(0u))
+            .build());
+
+    addParameter(
+            DefineParam(mActualPipelineDelay, C2_PARAMKEY_PIPELINE_DELAY)
+            .withConstValue(new C2ActualPipelineDelayTuning(0u))
+            .build());
+}
+
+void SimpleInterface<void>::BaseParams::noPrivateBuffers() {
+    addParameter(
+            DefineParam(mPrivateAllocators, C2_PARAMKEY_PRIVATE_ALLOCATORS)
+            .withConstValue(C2PrivateAllocatorsTuning::AllocShared(0u))
+            .build());
+
+    addParameter(
+            DefineParam(mMaxPrivateBufferCount, C2_PARAMKEY_MAX_PRIVATE_BUFFER_COUNT)
+            .withConstValue(C2MaxPrivateBufferCountTuning::AllocShared(0u))
+            .build());
+
+    addParameter(
+            DefineParam(mPrivatePoolIds, C2_PARAMKEY_PRIVATE_BLOCK_POOLS)
+            .withConstValue(C2PrivateBlockPoolsTuning::AllocShared(0u))
+            .build());
+}
+
+void SimpleInterface<void>::BaseParams::noInputReferences() {
+    addParameter(
+            DefineParam(mMaxInputReferenceAge, C2_PARAMKEY_INPUT_MAX_REFERENCE_AGE)
+            .withConstValue(new C2StreamMaxReferenceAgeTuning::input(0u))
+            .build());
+
+    addParameter(
+            DefineParam(mMaxInputReferenceCount, C2_PARAMKEY_INPUT_MAX_REFERENCE_COUNT)
+            .withConstValue(new C2StreamMaxReferenceCountTuning::input(0u))
+            .build());
+}
+
+void SimpleInterface<void>::BaseParams::noOutputReferences() {
+    addParameter(
+            DefineParam(mMaxOutputReferenceAge, C2_PARAMKEY_OUTPUT_MAX_REFERENCE_AGE)
+            .withConstValue(new C2StreamMaxReferenceAgeTuning::output(0u))
+            .build());
+
+    addParameter(
+            DefineParam(mMaxOutputReferenceCount, C2_PARAMKEY_OUTPUT_MAX_REFERENCE_COUNT)
+            .withConstValue(new C2StreamMaxReferenceCountTuning::output(0u))
+            .build());
+}
+
+void SimpleInterface<void>::BaseParams::noTimeStretch() {
+    addParameter(
+            DefineParam(mTimeStretch, C2_PARAMKEY_TIME_STRETCH)
+            .withConstValue(new C2ComponentTimeStretchTuning(1.f))
+            .build());
+}
+
+/*
+    Clients need to handle the following base params due to custom dependency.
+
+    std::shared_ptr<C2ApiLevelSetting> mApiLevel;
+    std::shared_ptr<C2ApiFeaturesSetting> mApiFeatures;
+    std::shared_ptr<C2ComponentAttributesSetting> mAttrib;
+
+    std::shared_ptr<C2PortSuggestedBufferCountTuning::input> mSuggestedInputBufferCount;
+    std::shared_ptr<C2PortSuggestedBufferCountTuning::output> mSuggestedOutputBufferCount;
+
+    std::shared_ptr<C2TrippedTuning> mTripped;
+
+*/
+
+} // namespace android
diff --git a/media/codec2/components/base/include/SimpleC2Component.h b/media/codec2/components/base/include/SimpleC2Component.h
new file mode 100644
index 0000000..b3a98f4
--- /dev/null
+++ b/media/codec2/components/base/include/SimpleC2Component.h
@@ -0,0 +1,244 @@
+/*
+ * 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 SIMPLE_C2_COMPONENT_H_
+#define SIMPLE_C2_COMPONENT_H_
+
+#include <list>
+#include <unordered_map>
+
+#include <C2Component.h>
+
+#include <media/stagefright/foundation/AHandler.h>
+#include <media/stagefright/foundation/ALooper.h>
+#include <media/stagefright/foundation/Mutexed.h>
+
+namespace android {
+
+class SimpleC2Component
+        : public C2Component, public std::enable_shared_from_this<SimpleC2Component> {
+public:
+    explicit SimpleC2Component(
+            const std::shared_ptr<C2ComponentInterface> &intf);
+    virtual ~SimpleC2Component();
+
+    // C2Component
+    // From C2Component
+    virtual c2_status_t setListener_vb(
+            const std::shared_ptr<Listener> &listener, c2_blocking_t mayBlock) override;
+    virtual c2_status_t queue_nb(std::list<std::unique_ptr<C2Work>>* const items) override;
+    virtual c2_status_t announce_nb(const std::vector<C2WorkOutline> &items) override;
+    virtual c2_status_t flush_sm(
+            flush_mode_t mode, std::list<std::unique_ptr<C2Work>>* const flushedWork) override;
+    virtual c2_status_t drain_nb(drain_mode_t mode) override;
+    virtual c2_status_t start() override;
+    virtual c2_status_t stop() override;
+    virtual c2_status_t reset() override;
+    virtual c2_status_t release() override;
+    virtual std::shared_ptr<C2ComponentInterface> intf() override;
+
+    // for handler
+    bool processQueue();
+
+protected:
+    /**
+     * Initialize internal states of the component according to the config set
+     * in the interface.
+     *
+     * This method is called during start(), but only at the first invocation or
+     * after reset().
+     */
+    virtual c2_status_t onInit() = 0;
+
+    /**
+     * Stop the component.
+     */
+    virtual c2_status_t onStop() = 0;
+
+    /**
+     * Reset the component.
+     */
+    virtual void onReset() = 0;
+
+    /**
+     * Release the component.
+     */
+    virtual void onRelease() = 0;
+
+    /**
+     * Flush the component.
+     */
+    virtual c2_status_t onFlush_sm() = 0;
+
+    /**
+     * Process the given work and finish pending work using finish().
+     *
+     * \param[in,out]   work    the work to process
+     * \param[in]       pool    the pool to use for allocating output blocks.
+     */
+    virtual void process(
+            const std::unique_ptr<C2Work> &work,
+            const std::shared_ptr<C2BlockPool> &pool) = 0;
+
+    /**
+     * Drain the component and finish pending work using finish().
+     *
+     * \param[in]   drainMode   mode of drain.
+     * \param[in]   pool        the pool to use for allocating output blocks.
+     *
+     * \retval C2_OK            The component has drained all pending output
+     *                          work.
+     * \retval C2_OMITTED       Unsupported mode (e.g. DRAIN_CHAIN)
+     */
+    virtual c2_status_t drain(
+            uint32_t drainMode,
+            const std::shared_ptr<C2BlockPool> &pool) = 0;
+
+    // for derived classes
+    /**
+     * Finish pending work.
+     *
+     * This method will retrieve the pending work according to |frameIndex| and
+     * feed the work into |fillWork| function. |fillWork| must be
+     * "non-blocking". Once |fillWork| returns the filled work will be returned
+     * to the client.
+     *
+     * \param[in]   frameIndex    the index of the pending work
+     * \param[in]   fillWork      the function to fill the retrieved work.
+     */
+    void finish(uint64_t frameIndex, std::function<void(const std::unique_ptr<C2Work> &)> fillWork);
+
+    /**
+     * Clone pending or current work and send the work back to client.
+     *
+     * This method will retrieve and clone the pending or current work according
+     * to |frameIndex| and feed the work into |fillWork| function. |fillWork|
+     * must be "non-blocking". Once |fillWork| returns the filled work will be
+     * returned to the client.
+     *
+     * \param[in]   frameIndex    the index of the work
+     * \param[in]   currentWork   the current work under processing
+     * \param[in]   fillWork      the function to fill the retrieved work.
+     */
+    void cloneAndSend(
+            uint64_t frameIndex,
+            const std::unique_ptr<C2Work> &currentWork,
+            std::function<void(const std::unique_ptr<C2Work> &)> fillWork);
+
+
+    std::shared_ptr<C2Buffer> createLinearBuffer(
+            const std::shared_ptr<C2LinearBlock> &block);
+
+    std::shared_ptr<C2Buffer> createLinearBuffer(
+            const std::shared_ptr<C2LinearBlock> &block, size_t offset, size_t size);
+
+    std::shared_ptr<C2Buffer> createGraphicBuffer(
+            const std::shared_ptr<C2GraphicBlock> &block);
+
+    std::shared_ptr<C2Buffer> createGraphicBuffer(
+            const std::shared_ptr<C2GraphicBlock> &block,
+            const C2Rect &crop);
+
+    static constexpr uint32_t NO_DRAIN = ~0u;
+
+    C2ReadView mDummyReadView;
+
+private:
+    const std::shared_ptr<C2ComponentInterface> mIntf;
+
+    class WorkHandler : public AHandler {
+    public:
+        enum {
+            kWhatProcess,
+            kWhatInit,
+            kWhatStart,
+            kWhatStop,
+            kWhatReset,
+            kWhatRelease,
+        };
+
+        WorkHandler();
+        ~WorkHandler() override = default;
+
+        void setComponent(const std::shared_ptr<SimpleC2Component> &thiz);
+
+    protected:
+        void onMessageReceived(const sp<AMessage> &msg) override;
+
+    private:
+        std::weak_ptr<SimpleC2Component> mThiz;
+        bool mRunning;
+    };
+
+    enum {
+        UNINITIALIZED,
+        STOPPED,
+        RUNNING,
+    };
+
+    struct ExecState {
+        ExecState() : mState(UNINITIALIZED) {}
+
+        int mState;
+        std::shared_ptr<C2Component::Listener> mListener;
+    };
+    Mutexed<ExecState> mExecState;
+
+    sp<ALooper> mLooper;
+    sp<WorkHandler> mHandler;
+
+    class WorkQueue {
+    public:
+        inline WorkQueue() : mFlush(false), mGeneration(0ul) {}
+
+        inline uint64_t generation() const { return mGeneration; }
+        inline void incGeneration() { ++mGeneration; mFlush = true; }
+
+        std::unique_ptr<C2Work> pop_front();
+        void push_back(std::unique_ptr<C2Work> work);
+        bool empty() const;
+        uint32_t drainMode() const;
+        void markDrain(uint32_t drainMode);
+        inline bool popPendingFlush() {
+            bool flush = mFlush;
+            mFlush = false;
+            return flush;
+        }
+        void clear();
+
+    private:
+        struct Entry {
+            std::unique_ptr<C2Work> work;
+            uint32_t drainMode;
+        };
+
+        bool mFlush;
+        uint64_t mGeneration;
+        std::list<Entry> mQueue;
+    };
+    Mutexed<WorkQueue> mWorkQueue;
+
+    typedef std::unordered_map<uint64_t, std::unique_ptr<C2Work>> PendingWork;
+    Mutexed<PendingWork> mPendingWork;
+
+    std::shared_ptr<C2BlockPool> mOutputBlockPool;
+
+    SimpleC2Component() = delete;
+};
+
+}  // namespace android
+
+#endif  // SIMPLE_C2_COMPONENT_H_
diff --git a/media/codec2/components/base/include/SimpleC2Interface.h b/media/codec2/components/base/include/SimpleC2Interface.h
new file mode 100644
index 0000000..2051d3d
--- /dev/null
+++ b/media/codec2/components/base/include/SimpleC2Interface.h
@@ -0,0 +1,236 @@
+/*
+ * 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_SIMPLE_C2_INTERFACE_H_
+#define ANDROID_SIMPLE_C2_INTERFACE_H_
+
+#include <C2Component.h>
+#include <C2Config.h>
+#include <util/C2InterfaceHelper.h>
+
+namespace android {
+
+/**
+ * Wrap a common interface object (such as Codec2Client::Interface, or C2InterfaceHelper into
+ * a C2ComponentInterface.
+ *
+ * \param T common interface type
+ */
+template <typename T>
+class SimpleC2Interface : public C2ComponentInterface {
+public:
+    SimpleC2Interface(const char *name, c2_node_id_t id, const std::shared_ptr<T> &impl)
+        : mName(name),
+          mId(id),
+          mImpl(impl) {
+    }
+
+    ~SimpleC2Interface() override = default;
+
+    // From C2ComponentInterface
+    C2String getName() const override { return mName; }
+    c2_node_id_t getId() const override { return mId; }
+    c2_status_t query_vb(
+            const std::vector<C2Param*> &stackParams,
+            const std::vector<C2Param::Index> &heapParamIndices,
+            c2_blocking_t mayBlock,
+            std::vector<std::unique_ptr<C2Param>>* const heapParams) const override {
+        return mImpl->query(stackParams, heapParamIndices, mayBlock, heapParams);
+    }
+    c2_status_t config_vb(
+            const std::vector<C2Param*> &params,
+            c2_blocking_t mayBlock,
+            std::vector<std::unique_ptr<C2SettingResult>>* const failures) override {
+        return mImpl->config(params, mayBlock, failures);
+    }
+    c2_status_t createTunnel_sm(c2_node_id_t) override { return C2_OMITTED; }
+    c2_status_t releaseTunnel_sm(c2_node_id_t) override { return C2_OMITTED; }
+    c2_status_t querySupportedParams_nb(
+            std::vector<std::shared_ptr<C2ParamDescriptor>> * const params) const override {
+        return mImpl->querySupportedParams(params);
+    }
+    c2_status_t querySupportedValues_vb(
+            std::vector<C2FieldSupportedValuesQuery> &fields,
+            c2_blocking_t mayBlock) const override {
+        return mImpl->querySupportedValues(fields, mayBlock);
+    }
+
+private:
+    C2String mName;
+    const c2_node_id_t mId;
+    const std::shared_ptr<T> mImpl;
+};
+
+/**
+ * Utility classes for common interfaces.
+ */
+template<>
+class SimpleC2Interface<void> {
+public:
+    /**
+     * Base Codec 2.0 parameters required for all components.
+     */
+    struct BaseParams : C2InterfaceHelper {
+        explicit BaseParams(
+                const std::shared_ptr<C2ReflectorHelper> &helper,
+                C2String name,
+                C2Component::kind_t kind,
+                C2Component::domain_t domain,
+                C2String mediaType,
+                std::vector<C2String> aliases = std::vector<C2String>());
+
+        /// Marks that this component has no input latency. Otherwise, component must
+        /// add support for C2PortRequestedDelayTuning::input and C2PortActualDelayTuning::input.
+        void noInputLatency();
+
+        /// Marks that this component has no output latency. Otherwise, component must
+        /// add support for C2PortRequestedDelayTuning::output and C2PortActualDelayTuning::output.
+        void noOutputLatency();
+
+        /// Marks that this component has no pipeline latency. Otherwise, component must
+        /// add support for C2RequestedPipelineDelayTuning and C2ActualPipelineDelayTuning.
+        void noPipelineLatency();
+
+        /// Marks that this component has no need for private buffers. Otherwise, component must
+        /// add support for C2MaxPrivateBufferCountTuning, C2PrivateAllocatorsTuning and
+        /// C2PrivateBlockPoolsTuning.
+        void noPrivateBuffers();
+
+        /// Marks that this component holds no references to input buffers. Otherwise, component
+        /// must add support for C2StreamMaxReferenceAgeTuning::input and
+        /// C2StreamMaxReferenceCountTuning::input.
+        void noInputReferences();
+
+        /// Marks that this component holds no references to output buffers. Otherwise, component
+        /// must add support for C2StreamMaxReferenceAgeTuning::output and
+        /// C2StreamMaxReferenceCountTuning::output.
+        void noOutputReferences();
+
+        /// Marks that this component does not stretch time. Otherwise, component
+        /// must add support for C2ComponentTimeStretchTuning.
+        void noTimeStretch();
+
+        std::shared_ptr<C2ApiLevelSetting> mApiLevel;
+        std::shared_ptr<C2ApiFeaturesSetting> mApiFeatures;
+
+        std::shared_ptr<C2PlatformLevelSetting> mPlatformLevel;
+        std::shared_ptr<C2PlatformFeaturesSetting> mPlatformFeatures;
+
+        std::shared_ptr<C2ComponentNameSetting> mName;
+        std::shared_ptr<C2ComponentAliasesSetting> mAliases;
+        std::shared_ptr<C2ComponentKindSetting> mKind;
+        std::shared_ptr<C2ComponentDomainSetting> mDomain;
+        std::shared_ptr<C2ComponentAttributesSetting> mAttrib;
+        std::shared_ptr<C2ComponentTimeStretchTuning> mTimeStretch;
+
+        std::shared_ptr<C2PortMediaTypeSetting::input> mInputMediaType;
+        std::shared_ptr<C2PortMediaTypeSetting::output> mOutputMediaType;
+        std::shared_ptr<C2StreamBufferTypeSetting::input> mInputFormat;
+        std::shared_ptr<C2StreamBufferTypeSetting::output> mOutputFormat;
+
+        std::shared_ptr<C2PortRequestedDelayTuning::input> mRequestedInputDelay;
+        std::shared_ptr<C2PortRequestedDelayTuning::output> mRequestedOutputDelay;
+        std::shared_ptr<C2RequestedPipelineDelayTuning> mRequestedPipelineDelay;
+
+        std::shared_ptr<C2PortActualDelayTuning::input> mActualInputDelay;
+        std::shared_ptr<C2PortActualDelayTuning::output> mActualOutputDelay;
+        std::shared_ptr<C2ActualPipelineDelayTuning> mActualPipelineDelay;
+
+        std::shared_ptr<C2StreamMaxReferenceAgeTuning::input> mMaxInputReferenceAge;
+        std::shared_ptr<C2StreamMaxReferenceCountTuning::input> mMaxInputReferenceCount;
+        std::shared_ptr<C2StreamMaxReferenceAgeTuning::output> mMaxOutputReferenceAge;
+        std::shared_ptr<C2StreamMaxReferenceCountTuning::output> mMaxOutputReferenceCount;
+        std::shared_ptr<C2MaxPrivateBufferCountTuning> mMaxPrivateBufferCount;
+
+        std::shared_ptr<C2PortStreamCountTuning::input> mInputStreamCount;
+        std::shared_ptr<C2PortStreamCountTuning::output> mOutputStreamCount;
+
+        std::shared_ptr<C2SubscribedParamIndicesTuning> mSubscribedParamIndices;
+        std::shared_ptr<C2PortSuggestedBufferCountTuning::input> mSuggestedInputBufferCount;
+        std::shared_ptr<C2PortSuggestedBufferCountTuning::output> mSuggestedOutputBufferCount;
+
+        std::shared_ptr<C2CurrentWorkTuning> mCurrentWorkOrdinal;
+        std::shared_ptr<C2LastWorkQueuedTuning::input> mLastInputQueuedWorkOrdinal;
+        std::shared_ptr<C2LastWorkQueuedTuning::output> mLastOutputQueuedWorkOrdinal;
+
+        std::shared_ptr<C2PortAllocatorsTuning::input> mInputAllocators;
+        std::shared_ptr<C2PortAllocatorsTuning::output> mOutputAllocators;
+        std::shared_ptr<C2PrivateAllocatorsTuning> mPrivateAllocators;
+        std::shared_ptr<C2PortBlockPoolsTuning::output> mOutputPoolIds;
+        std::shared_ptr<C2PrivateBlockPoolsTuning> mPrivatePoolIds;
+
+        std::shared_ptr<C2TrippedTuning> mTripped;
+        std::shared_ptr<C2OutOfMemoryTuning> mOutOfMemory;
+
+        std::shared_ptr<C2PortConfigCounterTuning::input> mInputConfigCounter;
+        std::shared_ptr<C2PortConfigCounterTuning::output> mOutputConfigCounter;
+        std::shared_ptr<C2ConfigCounterTuning> mDirectConfigCounter;
+    };
+};
+
+template<typename T>
+using SimpleInterface = SimpleC2Interface<T>;
+
+template<typename T, typename ...Args>
+std::shared_ptr<T> AllocSharedString(const Args(&... args), const char *str) {
+    size_t len = strlen(str) + 1;
+    std::shared_ptr<T> ret = T::AllocShared(len, args...);
+    strcpy(ret->m.value, str);
+    return ret;
+}
+
+template<typename T, typename ...Args>
+std::shared_ptr<T> AllocSharedString(const Args(&... args), const std::string &str) {
+    std::shared_ptr<T> ret = T::AllocShared(str.length() + 1, args...);
+    strcpy(ret->m.value, str.c_str());
+    return ret;
+}
+
+template <typename T>
+struct Setter {
+    typedef typename std::remove_reference<T>::type type;
+
+    static C2R NonStrictValueWithNoDeps(
+            bool mayBlock, C2InterfaceHelper::C2P<type> &me) {
+        (void)mayBlock;
+        return me.F(me.v.value).validatePossible(me.v.value);
+    }
+
+    static C2R NonStrictValuesWithNoDeps(
+            bool mayBlock, C2InterfaceHelper::C2P<type> &me) {
+        (void)mayBlock;
+        C2R res = C2R::Ok();
+        for (size_t ix = 0; ix < me.v.flexCount(); ++ix) {
+            res.plus(me.F(me.v.m.values[ix]).validatePossible(me.v.m.values[ix]));
+        }
+        return res;
+    }
+
+    static C2R StrictValueWithNoDeps(
+            bool mayBlock,
+            const C2InterfaceHelper::C2P<type> &old,
+            C2InterfaceHelper::C2P<type> &me) {
+        (void)mayBlock;
+        if (!me.F(me.v.value).supportsNow(me.v.value)) {
+            me.set().value = old.v.value;
+        }
+        return me.F(me.v.value).validatePossible(me.v.value);
+    }
+};
+
+}  // namespace android
+
+#endif  // ANDROID_SIMPLE_C2_INTERFACE_H_