Move Codec2-related code from hardware/google/av

Test: None
Bug: 112362730
Change-Id: Ie2f8ff431d65c40333f267ab9877d47089adeea4
diff --git a/media/codec2/hidl/client/Android.bp b/media/codec2/hidl/client/Android.bp
new file mode 100644
index 0000000..0819055
--- /dev/null
+++ b/media/codec2/hidl/client/Android.bp
@@ -0,0 +1,38 @@
+cc_library {
+    name: "libcodec2_hidl_client",
+
+    srcs: [
+        "client.cpp",
+    ],
+
+    shared_libs: [
+        "android.hardware.graphics.bufferqueue@1.0",
+        "android.hardware.media.bufferpool@1.0",
+        "hardware.google.media.c2@1.0",
+        "libbase",
+        "libbinder",
+        "libcodec2_hidl_utils@1.0",
+        "libcutils",
+        "libgui",
+        "libhidlbase",
+        "libhidltransport",
+        "liblog",
+        "libstagefright_bufferpool@1.0",
+        "libstagefright_bufferqueue_helper",
+        "libstagefright_codec2",
+        "libstagefright_codec2_vndk",
+        "libui",
+        "libutils",
+    ],
+
+    export_include_dirs: [
+        "include",
+    ],
+
+    export_shared_lib_headers: [
+        "libcodec2_hidl_utils@1.0",
+        "libstagefright_codec2",
+    ],
+
+}
+
diff --git a/media/codec2/hidl/client/client.cpp b/media/codec2/hidl/client/client.cpp
new file mode 100644
index 0000000..ff67681
--- /dev/null
+++ b/media/codec2/hidl/client/client.cpp
@@ -0,0 +1,1388 @@
+/*
+ * 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 "Codec2Client"
+#include <log/log.h>
+
+#include <codec2/hidl/client.h>
+
+#include <deque>
+#include <limits>
+#include <map>
+#include <type_traits>
+#include <vector>
+
+#include <android-base/properties.h>
+#include <bufferpool/ClientManager.h>
+#include <cutils/native_handle.h>
+#include <gui/bufferqueue/1.0/H2BGraphicBufferProducer.h>
+#include <hidl/HidlSupport.h>
+#include <media/stagefright/bqhelper/WGraphicBufferProducer.h>
+#undef LOG
+
+#include <android/hardware/media/bufferpool/1.0/IClientManager.h>
+#include <hardware/google/media/c2/1.0/IComponent.h>
+#include <hardware/google/media/c2/1.0/IComponentInterface.h>
+#include <hardware/google/media/c2/1.0/IComponentListener.h>
+#include <hardware/google/media/c2/1.0/IComponentStore.h>
+#include <hardware/google/media/c2/1.0/IConfigurable.h>
+
+#include <C2Debug.h>
+#include <C2BufferPriv.h>
+#include <C2PlatformSupport.h>
+
+namespace android {
+
+using ::android::hardware::hidl_vec;
+using ::android::hardware::hidl_string;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::TWGraphicBufferProducer;
+
+using namespace ::hardware::google::media::c2::V1_0;
+using namespace ::hardware::google::media::c2::V1_0::utils;
+using namespace ::android::hardware::media::bufferpool::V1_0;
+using namespace ::android::hardware::media::bufferpool::V1_0::implementation;
+
+namespace /* unnamed */ {
+
+// c2_status_t value that corresponds to hwbinder transaction failure.
+constexpr c2_status_t C2_TRANSACTION_FAILED = C2_CORRUPTED;
+
+// List of known IComponentStore services in the decreasing order of preference.
+constexpr const char* kClientNames[] = {
+        "default",
+        "software",
+    };
+
+// Number of known IComponentStore services.
+constexpr size_t kNumClients = std::extent<decltype(kClientNames)>::value;
+
+typedef std::array<std::shared_ptr<Codec2Client>, kNumClients> ClientList;
+
+// Convenience methods to obtain known clients.
+std::shared_ptr<Codec2Client> getClient(size_t index) {
+    return Codec2Client::CreateFromService(kClientNames[index]);
+}
+
+ClientList getClientList() {
+    ClientList list;
+    for (size_t i = 0; i < list.size(); ++i) {
+        list[i] = getClient(i);
+    }
+    return list;
+}
+
+} // unnamed
+
+// Codec2ConfigurableClient
+
+const C2String& Codec2ConfigurableClient::getName() const {
+    return mName;
+}
+
+Codec2ConfigurableClient::Base* Codec2ConfigurableClient::base() const {
+    return static_cast<Base*>(mBase.get());
+}
+
+Codec2ConfigurableClient::Codec2ConfigurableClient(
+        const sp<Codec2ConfigurableClient::Base>& base) : mBase(base) {
+    Return<void> transStatus = base->getName(
+            [this](const hidl_string& name) {
+                mName = name.c_str();
+            });
+    if (!transStatus.isOk()) {
+        ALOGE("Cannot obtain name from IConfigurable.");
+    }
+}
+
+c2_status_t Codec2ConfigurableClient::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 {
+    hidl_vec<ParamIndex> indices(
+            stackParams.size() + heapParamIndices.size());
+    size_t numIndices = 0;
+    for (C2Param* const& stackParam : stackParams) {
+        if (!stackParam) {
+            ALOGW("query -- null stack param encountered.");
+            continue;
+        }
+        indices[numIndices++] = static_cast<ParamIndex>(stackParam->index());
+    }
+    size_t numStackIndices = numIndices;
+    for (const C2Param::Index& index : heapParamIndices) {
+        indices[numIndices++] =
+                static_cast<ParamIndex>(static_cast<uint32_t>(index));
+    }
+    indices.resize(numIndices);
+    if (heapParams) {
+        heapParams->reserve(heapParams->size() + numIndices);
+    }
+    c2_status_t status;
+    Return<void> transStatus = base()->query(
+            indices,
+            mayBlock == C2_MAY_BLOCK,
+            [&status, &numStackIndices, &stackParams, heapParams](
+                    Status s, const Params& p) {
+                status = static_cast<c2_status_t>(s);
+                if (status != C2_OK && status != C2_BAD_INDEX) {
+                    ALOGE("query -- call failed. "
+                            "Error code = %d", static_cast<int>(status));
+                    return;
+                }
+                std::vector<C2Param*> paramPointers;
+                c2_status_t parseStatus = parseParamsBlob(&paramPointers, p);
+                if (parseStatus != C2_OK) {
+                    ALOGE("query -- error while parsing params. "
+                            "Error code = %d", static_cast<int>(status));
+                    status = parseStatus;
+                    return;
+                }
+                size_t i = 0;
+                for (auto it = paramPointers.begin(); it != paramPointers.end(); ) {
+                    C2Param* paramPointer = *it;
+                    if (numStackIndices > 0) {
+                        --numStackIndices;
+                        if (!paramPointer) {
+                            ALOGW("query -- null stack param.");
+                            ++it;
+                            continue;
+                        }
+                        for (; i < stackParams.size() && !stackParams[i]; ) {
+                            ++i;
+                        }
+                        if (i >= stackParams.size()) {
+                            ALOGE("query -- unexpected error.");
+                            status = C2_CORRUPTED;
+                            return;
+                        }
+                        if (stackParams[i]->index() != paramPointer->index()) {
+                            ALOGW("query -- param skipped. index = %d",
+                                    static_cast<int>(stackParams[i]->index()));
+                            stackParams[i++]->invalidate();
+                            continue;
+                        }
+                        if (!stackParams[i++]->updateFrom(*paramPointer)) {
+                            ALOGW("query -- param update failed. index = %d",
+                                    static_cast<int>(paramPointer->index()));
+                        }
+                    } else {
+                        if (!paramPointer) {
+                            ALOGW("query -- null heap param.");
+                            ++it;
+                            continue;
+                        }
+                        if (!heapParams) {
+                            ALOGW("query -- unexpected extra stack param.");
+                        } else {
+                            heapParams->emplace_back(C2Param::Copy(*paramPointer));
+                        }
+                    }
+                    ++it;
+                }
+            });
+    if (!transStatus.isOk()) {
+        ALOGE("query -- transaction failed.");
+        return C2_TRANSACTION_FAILED;
+    }
+    return status;
+}
+
+c2_status_t Codec2ConfigurableClient::config(
+        const std::vector<C2Param*> &params,
+        c2_blocking_t mayBlock,
+        std::vector<std::unique_ptr<C2SettingResult>>* const failures) {
+    Params hidlParams;
+    Status hidlStatus = createParamsBlob(&hidlParams, params);
+    if (hidlStatus != Status::OK) {
+        ALOGE("config -- bad input.");
+        return C2_TRANSACTION_FAILED;
+    }
+    c2_status_t status;
+    Return<void> transStatus = base()->config(
+            hidlParams,
+            mayBlock == C2_MAY_BLOCK,
+            [&status, &params, failures](
+                    Status s,
+                    const hidl_vec<SettingResult> f,
+                    const Params& o) {
+                status = static_cast<c2_status_t>(s);
+                if (status != C2_OK) {
+                    ALOGD("config -- call failed. "
+                            "Error code = %d", static_cast<int>(status));
+                }
+                size_t i = failures->size();
+                failures->resize(i + f.size());
+                for (const SettingResult& sf : f) {
+                    status = objcpy(&(*failures)[i++], sf);
+                    if (status != C2_OK) {
+                        ALOGE("config -- invalid returned SettingResult. "
+                                "Error code = %d", static_cast<int>(status));
+                        return;
+                    }
+                }
+                status = updateParamsFromBlob(params, o);
+            });
+    if (!transStatus.isOk()) {
+        ALOGE("config -- transaction failed.");
+        return C2_TRANSACTION_FAILED;
+    }
+    return status;
+}
+
+c2_status_t Codec2ConfigurableClient::querySupportedParams(
+        std::vector<std::shared_ptr<C2ParamDescriptor>>* const params) const {
+    // TODO: Cache and query properly!
+    c2_status_t status;
+    Return<void> transStatus = base()->querySupportedParams(
+            std::numeric_limits<uint32_t>::min(),
+            std::numeric_limits<uint32_t>::max(),
+            [&status, params](
+                    Status s,
+                    const hidl_vec<ParamDescriptor>& p) {
+                status = static_cast<c2_status_t>(s);
+                if (status != C2_OK) {
+                    ALOGE("querySupportedParams -- call failed. "
+                            "Error code = %d", static_cast<int>(status));
+                    return;
+                }
+                size_t i = params->size();
+                params->resize(i + p.size());
+                for (const ParamDescriptor& sp : p) {
+                    status = objcpy(&(*params)[i++], sp);
+                    if (status != C2_OK) {
+                        ALOGE("querySupportedParams -- "
+                                "invalid returned ParamDescriptor. "
+                                "Error code = %d", static_cast<int>(status));
+                        return;
+                    }
+                }
+            });
+    if (!transStatus.isOk()) {
+        ALOGE("querySupportedParams -- transaction failed.");
+        return C2_TRANSACTION_FAILED;
+    }
+    return status;
+}
+
+c2_status_t Codec2ConfigurableClient::querySupportedValues(
+        std::vector<C2FieldSupportedValuesQuery>& fields,
+        c2_blocking_t mayBlock) const {
+    hidl_vec<FieldSupportedValuesQuery> inFields(fields.size());
+    for (size_t i = 0; i < fields.size(); ++i) {
+        Status hidlStatus = objcpy(&inFields[i], fields[i]);
+        if (hidlStatus != Status::OK) {
+            ALOGE("querySupportedValues -- bad input");
+            return C2_TRANSACTION_FAILED;
+        }
+    }
+
+    c2_status_t status;
+    Return<void> transStatus = base()->querySupportedValues(
+            inFields,
+            mayBlock == C2_MAY_BLOCK,
+            [&status, &inFields, &fields](
+                    Status s,
+                    const hidl_vec<FieldSupportedValuesQueryResult>& r) {
+                status = static_cast<c2_status_t>(s);
+                if (status != C2_OK) {
+                    ALOGE("querySupportedValues -- call failed. "
+                            "Error code = %d", static_cast<int>(status));
+                    return;
+                }
+                if (r.size() != fields.size()) {
+                    ALOGE("querySupportedValues -- input and output lists "
+                            "have different sizes.");
+                    status = C2_CORRUPTED;
+                    return;
+                }
+                for (size_t i = 0; i < fields.size(); ++i) {
+                    status = objcpy(&fields[i], inFields[i], r[i]);
+                    if (status != C2_OK) {
+                        ALOGE("querySupportedValues -- invalid returned value. "
+                                "Error code = %d", static_cast<int>(status));
+                        return;
+                    }
+                }
+            });
+    if (!transStatus.isOk()) {
+        ALOGE("querySupportedValues -- transaction failed.");
+        return C2_TRANSACTION_FAILED;
+    }
+    return status;
+}
+
+// Codec2Client::Component::HidlListener
+struct Codec2Client::Component::HidlListener : public IComponentListener {
+    std::weak_ptr<Component> component;
+    std::weak_ptr<Listener> base;
+
+    virtual Return<void> onWorkDone(const WorkBundle& workBundle) override {
+        std::list<std::unique_ptr<C2Work>> workItems;
+        c2_status_t status = objcpy(&workItems, workBundle);
+        if (status != C2_OK) {
+            ALOGI("onWorkDone -- received corrupted WorkBundle. "
+                    "status = %d.", static_cast<int>(status));
+            return Void();
+        }
+        // release input buffers potentially held by the component from queue
+        size_t numDiscardedInputBuffers = 0;
+        std::shared_ptr<Codec2Client::Component> strongComponent = component.lock();
+        if (strongComponent) {
+            numDiscardedInputBuffers = strongComponent->handleOnWorkDone(workItems);
+        }
+        if (std::shared_ptr<Codec2Client::Listener> listener = base.lock()) {
+            listener->onWorkDone(component, workItems, numDiscardedInputBuffers);
+        } else {
+            ALOGD("onWorkDone -- listener died.");
+        }
+        return Void();
+    }
+
+    virtual Return<void> onTripped(
+            const hidl_vec<SettingResult>& settingResults) override {
+        std::vector<std::shared_ptr<C2SettingResult>> c2SettingResults(
+                settingResults.size());
+        c2_status_t status;
+        for (size_t i = 0; i < settingResults.size(); ++i) {
+            std::unique_ptr<C2SettingResult> c2SettingResult;
+            status = objcpy(&c2SettingResult, settingResults[i]);
+            if (status != C2_OK) {
+                ALOGI("onTripped -- received corrupted SettingResult. "
+                        "status = %d.", static_cast<int>(status));
+                return Void();
+            }
+            c2SettingResults[i] = std::move(c2SettingResult);
+        }
+        if (std::shared_ptr<Codec2Client::Listener> listener = base.lock()) {
+            listener->onTripped(component, c2SettingResults);
+        } else {
+            ALOGD("onTripped -- listener died.");
+        }
+        return Void();
+    }
+
+    virtual Return<void> onError(Status s, uint32_t errorCode) override {
+        ALOGD("onError -- status = %d, errorCode = %u.",
+                static_cast<int>(s),
+                static_cast<unsigned>(errorCode));
+        if (std::shared_ptr<Listener> listener = base.lock()) {
+            listener->onError(component, s == Status::OK ?
+                    errorCode : static_cast<c2_status_t>(s));
+        } else {
+            ALOGD("onError -- listener died.");
+        }
+        return Void();
+    }
+
+    virtual Return<void> onFramesRendered(
+            const hidl_vec<RenderedFrame>& renderedFrames) override {
+        std::shared_ptr<Listener> listener = base.lock();
+        std::vector<Codec2Client::Listener::RenderedFrame> rfs;
+        rfs.reserve(renderedFrames.size());
+        for (const RenderedFrame& rf : renderedFrames) {
+            if (rf.slotId >= 0) {
+                if (listener) {
+                    rfs.emplace_back(rf.bufferQueueId,
+                                     rf.slotId,
+                                     rf.timestampNs);
+                }
+            } else {
+                std::shared_ptr<Codec2Client::Component> strongComponent =
+                        component.lock();
+                if (strongComponent) {
+                    uint64_t frameIndex = rf.bufferQueueId;
+                    size_t bufferIndex = static_cast<size_t>(~rf.slotId);
+                    ALOGV("Received death notification of input buffer: "
+                          "frameIndex = %llu, bufferIndex = %zu.",
+                          static_cast<long long unsigned>(frameIndex),
+                          bufferIndex);
+                    std::shared_ptr<C2Buffer> buffer =
+                            strongComponent->freeInputBuffer(
+                                frameIndex, bufferIndex);
+                    if (buffer) {
+                        listener->onInputBufferDone(buffer);
+                    }
+                }
+            }
+        }
+        if (!rfs.empty()) {
+            if (listener) {
+                listener->onFramesRendered(rfs);
+            } else {
+                ALOGD("onFramesRendered -- listener died.");
+            }
+        }
+        return Void();
+    }
+};
+
+// Codec2Client
+Codec2Client::Base* Codec2Client::base() const {
+    return static_cast<Base*>(mBase.get());
+}
+
+Codec2Client::Codec2Client(const sp<Codec2Client::Base>& base, std::string instanceName) :
+    Codec2ConfigurableClient(base), mListed(false), mInstanceName(instanceName) {
+    Return<sp<IClientManager>> transResult = base->getPoolClientManager();
+    if (!transResult.isOk()) {
+        ALOGE("getPoolClientManager -- failed transaction.");
+    } else {
+        mHostPoolManager = static_cast<sp<IClientManager>>(transResult);
+    }
+}
+
+c2_status_t Codec2Client::createComponent(
+        const C2String& name,
+        const std::shared_ptr<Codec2Client::Listener>& listener,
+        std::shared_ptr<Codec2Client::Component>* const component) {
+
+    // TODO: Add support for Bufferpool
+
+
+    c2_status_t status;
+    sp<Component::HidlListener> hidlListener = new Component::HidlListener();
+    hidlListener->base = listener;
+    Return<void> transStatus = base()->createComponent(
+            name,
+            hidlListener,
+            ClientManager::getInstance(),
+            [&status, component, hidlListener](
+                    Status s,
+                    const sp<IComponent>& c) {
+                status = static_cast<c2_status_t>(s);
+                if (status != C2_OK) {
+                    return;
+                }
+                *component = std::make_shared<Codec2Client::Component>(c);
+                hidlListener->component = *component;
+            });
+    if (!transStatus.isOk()) {
+        ALOGE("createComponent -- failed transaction.");
+        return C2_TRANSACTION_FAILED;
+    }
+
+    if (status != C2_OK) {
+        return status;
+    }
+
+    if (!*component) {
+        ALOGE("createComponent -- null component.");
+        return C2_CORRUPTED;
+    }
+
+    status = (*component)->setDeathListener(*component, listener);
+    if (status != C2_OK) {
+        ALOGE("createComponent -- setDeathListener returned error: %d.",
+                static_cast<int>(status));
+    }
+
+    (*component)->mBufferPoolSender.setReceiver(mHostPoolManager);
+    return status;
+}
+
+c2_status_t Codec2Client::createInterface(
+        const C2String& name,
+        std::shared_ptr<Codec2Client::Interface>* const interface) {
+    c2_status_t status;
+    Return<void> transStatus = base()->createInterface(
+            name,
+            [&status, interface](
+                    Status s,
+                    const sp<IComponentInterface>& i) {
+                status = static_cast<c2_status_t>(s);
+                if (status != C2_OK) {
+                    ALOGE("createInterface -- call failed. "
+                            "Error code = %d", static_cast<int>(status));
+                    return;
+                }
+                *interface = std::make_shared<Codec2Client::Interface>(i);
+            });
+    if (!transStatus.isOk()) {
+        ALOGE("createInterface -- failed transaction.");
+        return C2_TRANSACTION_FAILED;
+    }
+    return status;
+}
+
+c2_status_t Codec2Client::createInputSurface(
+        std::shared_ptr<Codec2Client::InputSurface>* const inputSurface) {
+    Return<sp<IInputSurface>> transResult = base()->createInputSurface();
+    if (!transResult.isOk()) {
+        ALOGE("createInputSurface -- failed transaction.");
+        return C2_TRANSACTION_FAILED;
+    }
+    sp<IInputSurface> result = static_cast<sp<IInputSurface>>(transResult);
+    if (!result) {
+        *inputSurface = nullptr;
+        return C2_OK;
+    }
+    *inputSurface = std::make_shared<InputSurface>(result);
+    if (!*inputSurface) {
+        ALOGE("createInputSurface -- unknown error.");
+        return C2_CORRUPTED;
+    }
+    return C2_OK;
+}
+
+const std::vector<C2Component::Traits>& Codec2Client::listComponents() const {
+    std::lock_guard<std::mutex> lock(mMutex);
+    if (mListed) {
+        return mTraitsList;
+    }
+    Return<void> transStatus = base()->listComponents(
+            [this](const hidl_vec<IComponentStore::ComponentTraits>& t) {
+                mTraitsList.resize(t.size());
+                mAliasesBuffer.resize(t.size());
+                for (size_t i = 0; i < t.size(); ++i) {
+                    c2_status_t status = objcpy(
+                            &mTraitsList[i], &mAliasesBuffer[i], t[i]);
+                    if (status != C2_OK) {
+                        ALOGE("listComponents -- corrupted output.");
+                        return;
+                    }
+                }
+            });
+    if (!transStatus.isOk()) {
+        ALOGE("listComponents -- failed transaction.");
+    }
+    mListed = true;
+    return mTraitsList;
+}
+
+c2_status_t Codec2Client::copyBuffer(
+        const std::shared_ptr<C2Buffer>& src,
+        const std::shared_ptr<C2Buffer>& dst) {
+    // TODO: Implement?
+    (void)src;
+    (void)dst;
+    ALOGE("copyBuffer not implemented");
+    return C2_OMITTED;
+}
+
+std::shared_ptr<C2ParamReflector>
+        Codec2Client::getParamReflector() {
+    // TODO: this is not meant to be exposed as C2ParamReflector on the client side; instead, it
+    // should reflect the HAL API.
+    struct SimpleParamReflector : public C2ParamReflector {
+        virtual std::unique_ptr<C2StructDescriptor> describe(C2Param::CoreIndex coreIndex) const {
+            hidl_vec<ParamIndex> indices(1);
+            indices[0] = static_cast<ParamIndex>(coreIndex.coreIndex());
+            std::unique_ptr<C2StructDescriptor> descriptor;
+            Return<void> transStatus = mBase->getStructDescriptors(
+                    indices,
+                    [&descriptor](
+                            Status s,
+                            const hidl_vec<StructDescriptor>& sd) {
+                        c2_status_t status = static_cast<c2_status_t>(s);
+                        if (status != C2_OK) {
+                            ALOGE("getStructDescriptors -- call failed. "
+                                    "Error code = %d", static_cast<int>(status));
+                            descriptor.reset();
+                            return;
+                        }
+                        if (sd.size() != 1) {
+                            ALOGD("getStructDescriptors -- returned vector of size %zu.",
+                                    sd.size());
+                            descriptor.reset();
+                            return;
+                        }
+                        status = objcpy(&descriptor, sd[0]);
+                        if (status != C2_OK) {
+                            ALOGD("getStructDescriptors -- failed to convert. "
+                                    "Error code = %d", static_cast<int>(status));
+                            descriptor.reset();
+                            return;
+                        }
+                    });
+            return descriptor;
+        }
+
+        SimpleParamReflector(sp<Base> base)
+            : mBase(base) { }
+
+        sp<Base> mBase;
+    };
+
+    return std::make_shared<SimpleParamReflector>(base());
+};
+
+std::shared_ptr<Codec2Client> Codec2Client::CreateFromService(
+        const char* instanceName, bool waitForService) {
+    if (!instanceName) {
+        return nullptr;
+    }
+    sp<Base> baseStore = waitForService ?
+            Base::getService(instanceName) :
+            Base::tryGetService(instanceName);
+    if (!baseStore) {
+        if (waitForService) {
+            ALOGE("Codec2.0 service inaccessible. Check the device manifest.");
+        } else {
+            ALOGW("Codec2.0 service not available right now. Try again later.");
+        }
+        return nullptr;
+    }
+    return std::make_shared<Codec2Client>(baseStore, instanceName);
+}
+
+c2_status_t Codec2Client::ForAllStores(
+        const std::string &key,
+        std::function<c2_status_t(const std::shared_ptr<Codec2Client>&)> predicate) {
+    c2_status_t status = C2_NO_INIT;  // no IComponentStores present
+
+    // Cache the mapping key -> index of Codec2Client in getClient().
+    static std::mutex key2IndexMutex;
+    static std::map<std::string, size_t> key2Index;
+
+    // By default try all stores. However, try the last known client first. If the last known
+    // client fails, retry once. We do this by pushing the last known client in front of the
+    // list of all clients.
+    std::deque<size_t> indices;
+    for (size_t index = kNumClients; index > 0; ) {
+        indices.push_front(--index);
+    }
+
+    bool wasMapped = false;
+    std::unique_lock<std::mutex> lock(key2IndexMutex);
+    auto it = key2Index.find(key);
+    if (it != key2Index.end()) {
+        indices.push_front(it->second);
+        wasMapped = true;
+    }
+    lock.unlock();
+
+    for (size_t index : indices) {
+        std::shared_ptr<Codec2Client> client = getClient(index);
+        if (client) {
+            status = predicate(client);
+            if (status == C2_OK) {
+                lock.lock();
+                key2Index[key] = index; // update last known client index
+                return status;
+            }
+        }
+        if (wasMapped) {
+            ALOGI("Could not find '%s' in last instance. Retrying...", key.c_str());
+            wasMapped = false;
+        }
+    }
+    return status;  // return the last status from a valid client
+}
+
+std::shared_ptr<Codec2Client::Component>
+        Codec2Client::CreateComponentByName(
+        const char* componentName,
+        const std::shared_ptr<Listener>& listener,
+        std::shared_ptr<Codec2Client>* owner) {
+    std::shared_ptr<Component> component;
+    c2_status_t status = ForAllStores(
+            componentName,
+            [owner, &component, componentName, &listener](
+                    const std::shared_ptr<Codec2Client> &client) -> c2_status_t {
+                c2_status_t status = client->createComponent(componentName, listener, &component);
+                if (status == C2_OK) {
+                    if (owner) {
+                        *owner = client;
+                    }
+                } else if (status != C2_NOT_FOUND) {
+                    ALOGD("IComponentStore(%s)::createComponent('%s') returned %s",
+                            client->getInstanceName().c_str(), componentName, asString(status));
+                }
+                return status;
+            });
+    if (status != C2_OK) {
+        ALOGI("Could not create component '%s' (%s)", componentName, asString(status));
+    }
+    return component;
+}
+
+std::shared_ptr<Codec2Client::Interface>
+        Codec2Client::CreateInterfaceByName(
+        const char* interfaceName,
+        std::shared_ptr<Codec2Client>* owner) {
+    std::shared_ptr<Interface> interface;
+    c2_status_t status = ForAllStores(
+            interfaceName,
+            [owner, &interface, interfaceName](
+                    const std::shared_ptr<Codec2Client> &client) -> c2_status_t {
+                c2_status_t status = client->createInterface(interfaceName, &interface);
+                if (status == C2_OK) {
+                    if (owner) {
+                        *owner = client;
+                    }
+                } else if (status != C2_NOT_FOUND) {
+                    ALOGD("IComponentStore(%s)::createInterface('%s') returned %s",
+                            client->getInstanceName().c_str(), interfaceName, asString(status));
+                }
+                return status;
+            });
+    if (status != C2_OK) {
+        ALOGI("Could not create interface '%s' (%s)", interfaceName, asString(status));
+    }
+    return interface;
+}
+
+std::shared_ptr<Codec2Client::InputSurface> Codec2Client::CreateInputSurface() {
+    uint32_t serviceMask = ::android::base::GetUintProperty(
+            "debug.stagefright.c2inputsurface", uint32_t(0));
+    for (size_t i = 0; i < kNumClients; ++i) {
+        if ((1 << i) & serviceMask) {
+            std::shared_ptr<Codec2Client> client = getClient(i);
+            std::shared_ptr<Codec2Client::InputSurface> inputSurface;
+            if (client &&
+                    client->createInputSurface(&inputSurface) == C2_OK &&
+                    inputSurface) {
+                return inputSurface;
+            }
+        }
+    }
+    ALOGW("Could not create an input surface from any Codec2.0 services.");
+    return nullptr;
+}
+
+const std::vector<C2Component::Traits>& Codec2Client::ListComponents() {
+    static std::vector<C2Component::Traits> traitsList = [](){
+        std::vector<C2Component::Traits> list;
+        size_t listSize = 0;
+        ClientList clientList = getClientList();
+        for (const std::shared_ptr<Codec2Client>& client : clientList) {
+            if (!client) {
+                continue;
+            }
+            listSize += client->listComponents().size();
+        }
+        list.reserve(listSize);
+        for (const std::shared_ptr<Codec2Client>& client : clientList) {
+            if (!client) {
+                continue;
+            }
+            list.insert(
+                    list.end(),
+                    client->listComponents().begin(),
+                    client->listComponents().end());
+        }
+        return list;
+    }();
+
+    return traitsList;
+}
+
+// Codec2Client::Listener
+
+Codec2Client::Listener::~Listener() {
+}
+
+// Codec2Client::Component
+
+Codec2Client::Component::Base* Codec2Client::Component::base() const {
+    return static_cast<Base*>(mBase.get());
+}
+
+Codec2Client::Component::Component(const sp<Codec2Client::Component::Base>& base) :
+    Codec2Client::Configurable(base),
+    mBufferPoolSender(nullptr) {
+}
+
+Codec2Client::Component::~Component() {
+}
+
+c2_status_t Codec2Client::Component::createBlockPool(
+        C2Allocator::id_t id,
+        C2BlockPool::local_id_t* blockPoolId,
+        std::shared_ptr<Codec2Client::Configurable>* configurable) {
+    c2_status_t status;
+    Return<void> transStatus = base()->createBlockPool(
+            static_cast<uint32_t>(id),
+            [&status, blockPoolId, configurable](
+                    Status s,
+                    uint64_t pId,
+                    const sp<IConfigurable>& c) {
+                status = static_cast<c2_status_t>(s);
+                configurable->reset();
+                if (status != C2_OK) {
+                    ALOGE("createBlockPool -- call failed. "
+                            "Error code = %d", static_cast<int>(status));
+                    return;
+                }
+                *blockPoolId = static_cast<C2BlockPool::local_id_t>(pId);
+                *configurable = std::make_shared<Codec2Client::Configurable>(c);
+            });
+    if (!transStatus.isOk()) {
+        ALOGE("createBlockPool -- transaction failed.");
+        return C2_TRANSACTION_FAILED;
+    }
+    return status;
+}
+
+c2_status_t Codec2Client::Component::destroyBlockPool(
+        C2BlockPool::local_id_t localId) {
+    Return<Status> transResult = base()->destroyBlockPool(
+            static_cast<uint64_t>(localId));
+    if (!transResult.isOk()) {
+        ALOGE("destroyBlockPool -- transaction failed.");
+        return C2_TRANSACTION_FAILED;
+    }
+    return static_cast<c2_status_t>(static_cast<Status>(transResult));
+}
+
+size_t Codec2Client::Component::handleOnWorkDone(
+        const std::list<std::unique_ptr<C2Work>> &workItems) {
+    // Input buffers' lifetime management
+    std::vector<uint64_t> inputDone;
+    for (const std::unique_ptr<C2Work> &work : workItems) {
+        if (work) {
+            if (work->worklets.empty()
+                    || !work->worklets.back()
+                    || (work->worklets.back()->output.flags & C2FrameData::FLAG_INCOMPLETE) == 0) {
+                // input is complete
+                inputDone.emplace_back(work->input.ordinal.frameIndex.peeku());
+            }
+        }
+    }
+
+    size_t numDiscardedInputBuffers = 0;
+    {
+        std::lock_guard<std::mutex> lock(mInputBuffersMutex);
+        for (uint64_t inputIndex : inputDone) {
+            auto it = mInputBuffers.find(inputIndex);
+            if (it == mInputBuffers.end()) {
+                ALOGV("onWorkDone -- returned consumed/unknown "
+                      "input frame: index %llu",
+                        (long long)inputIndex);
+            } else {
+                ALOGV("onWorkDone -- processed input frame: "
+                      "index %llu (containing %zu buffers)",
+                        (long long)inputIndex, it->second.size());
+                mInputBuffers.erase(it);
+                mInputBufferCount.erase(inputIndex);
+                ++numDiscardedInputBuffers;
+            }
+        }
+    }
+
+    // Output bufferqueue-based blocks' lifetime management
+    mOutputBufferQueueMutex.lock();
+    sp<IGraphicBufferProducer> igbp = mOutputIgbp;
+    uint64_t bqId = mOutputBqId;
+    uint32_t generation = mOutputGeneration;
+    mOutputBufferQueueMutex.unlock();
+
+    if (igbp) {
+        holdBufferQueueBlocks(workItems, igbp, bqId, generation);
+    }
+    return numDiscardedInputBuffers;
+}
+
+std::shared_ptr<C2Buffer> Codec2Client::Component::freeInputBuffer(
+        uint64_t frameIndex,
+        size_t bufferIndex) {
+    std::shared_ptr<C2Buffer> buffer;
+    std::lock_guard<std::mutex> lock(mInputBuffersMutex);
+    auto it = mInputBuffers.find(frameIndex);
+    if (it == mInputBuffers.end()) {
+        ALOGI("freeInputBuffer -- Unrecognized input frame index %llu.",
+              static_cast<long long unsigned>(frameIndex));
+        return nullptr;
+    }
+    if (bufferIndex >= it->second.size()) {
+        ALOGI("freeInputBuffer -- Input buffer no. %zu is invalid in "
+              "input frame index %llu.",
+              bufferIndex, static_cast<long long unsigned>(frameIndex));
+        return nullptr;
+    }
+    buffer = it->second[bufferIndex];
+    if (!buffer) {
+        ALOGI("freeInputBuffer -- Input buffer no. %zu in "
+              "input frame index %llu has already been freed.",
+              bufferIndex, static_cast<long long unsigned>(frameIndex));
+        return nullptr;
+    }
+    it->second[bufferIndex] = nullptr;
+    if (--mInputBufferCount[frameIndex] == 0) {
+        mInputBuffers.erase(it);
+        mInputBufferCount.erase(frameIndex);
+    }
+    return buffer;
+}
+
+c2_status_t Codec2Client::Component::queue(
+        std::list<std::unique_ptr<C2Work>>* const items) {
+    // remember input buffers queued to hold reference to them
+    {
+        std::lock_guard<std::mutex> lock(mInputBuffersMutex);
+        for (const std::unique_ptr<C2Work> &work : *items) {
+            if (!work) {
+                continue;
+            }
+            if (work->input.buffers.size() == 0) {
+                continue;
+            }
+
+            uint64_t inputIndex = work->input.ordinal.frameIndex.peeku();
+            auto res = mInputBuffers.emplace(inputIndex, work->input.buffers);
+            if (!res.second) {
+                // TODO: append? - for now we are replacing
+                res.first->second = work->input.buffers;
+                ALOGI("queue -- duplicate input frame: index %llu. "
+                      "Discarding the old input frame...",
+                        (long long)inputIndex);
+            }
+            mInputBufferCount[inputIndex] = work->input.buffers.size();
+            ALOGV("queue -- queueing input frame: "
+                  "index %llu (containing %zu buffers)",
+                    (long long)inputIndex, work->input.buffers.size());
+        }
+    }
+
+    WorkBundle workBundle;
+    Status hidlStatus = objcpy(&workBundle, *items, &mBufferPoolSender);
+    if (hidlStatus != Status::OK) {
+        ALOGE("queue -- bad input.");
+        return C2_TRANSACTION_FAILED;
+    }
+    Return<Status> transStatus = base()->queue(workBundle);
+    if (!transStatus.isOk()) {
+        ALOGE("queue -- transaction failed.");
+        return C2_TRANSACTION_FAILED;
+    }
+    c2_status_t status =
+            static_cast<c2_status_t>(static_cast<Status>(transStatus));
+    if (status != C2_OK) {
+        ALOGE("queue -- call failed. "
+                "Error code = %d", static_cast<int>(status));
+    }
+    return status;
+}
+
+c2_status_t Codec2Client::Component::flush(
+        C2Component::flush_mode_t mode,
+        std::list<std::unique_ptr<C2Work>>* const flushedWork) {
+    (void)mode; // Flush mode isn't supported in HIDL yet.
+    c2_status_t status;
+    Return<void> transStatus = base()->flush(
+            [&status, flushedWork](
+                    Status s, const WorkBundle& wb) {
+                status = static_cast<c2_status_t>(s);
+                if (status != C2_OK) {
+                    ALOGE("flush -- call failed. "
+                            "Error code = %d", static_cast<int>(status));
+                    return;
+                }
+                status = objcpy(flushedWork, wb);
+            });
+    if (!transStatus.isOk()) {
+        ALOGE("flush -- transaction failed.");
+        return C2_TRANSACTION_FAILED;
+    }
+
+    // Indices of flushed work items.
+    std::vector<uint64_t> flushedIndices;
+    for (const std::unique_ptr<C2Work> &work : *flushedWork) {
+        if (work) {
+            if (work->worklets.empty()
+                    || !work->worklets.back()
+                    || (work->worklets.back()->output.flags &
+                        C2FrameData::FLAG_INCOMPLETE) == 0) {
+                // input is complete
+                flushedIndices.emplace_back(
+                        work->input.ordinal.frameIndex.peeku());
+            }
+        }
+    }
+
+    // Input buffers' lifetime management
+    for (uint64_t flushedIndex : flushedIndices) {
+        std::lock_guard<std::mutex> lock(mInputBuffersMutex);
+        auto it = mInputBuffers.find(flushedIndex);
+        if (it == mInputBuffers.end()) {
+            ALOGV("flush -- returned consumed/unknown input frame: "
+                  "index %llu",
+                    (long long)flushedIndex);
+        } else {
+            ALOGV("flush -- returned unprocessed input frame: "
+                  "index %llu (containing %zu buffers)",
+                    (long long)flushedIndex, mInputBufferCount[flushedIndex]);
+            mInputBuffers.erase(it);
+            mInputBufferCount.erase(flushedIndex);
+        }
+    }
+
+    // Output bufferqueue-based blocks' lifetime management
+    mOutputBufferQueueMutex.lock();
+    sp<IGraphicBufferProducer> igbp = mOutputIgbp;
+    uint64_t bqId = mOutputBqId;
+    uint32_t generation = mOutputGeneration;
+    mOutputBufferQueueMutex.unlock();
+
+    if (igbp) {
+        holdBufferQueueBlocks(*flushedWork, igbp, bqId, generation);
+    }
+
+    return status;
+}
+
+c2_status_t Codec2Client::Component::drain(C2Component::drain_mode_t mode) {
+    Return<Status> transStatus = base()->drain(
+            mode == C2Component::DRAIN_COMPONENT_WITH_EOS);
+    if (!transStatus.isOk()) {
+        ALOGE("drain -- transaction failed.");
+        return C2_TRANSACTION_FAILED;
+    }
+    c2_status_t status =
+            static_cast<c2_status_t>(static_cast<Status>(transStatus));
+    if (status != C2_OK) {
+        ALOGE("drain -- call failed. "
+                "Error code = %d", static_cast<int>(status));
+    }
+    return status;
+}
+
+c2_status_t Codec2Client::Component::start() {
+    Return<Status> transStatus = base()->start();
+    if (!transStatus.isOk()) {
+        ALOGE("start -- transaction failed.");
+        return C2_TRANSACTION_FAILED;
+    }
+    c2_status_t status =
+            static_cast<c2_status_t>(static_cast<Status>(transStatus));
+    if (status != C2_OK) {
+        ALOGE("start -- call failed. "
+                "Error code = %d", static_cast<int>(status));
+    }
+    return status;
+}
+
+c2_status_t Codec2Client::Component::stop() {
+    Return<Status> transStatus = base()->stop();
+    if (!transStatus.isOk()) {
+        ALOGE("stop -- transaction failed.");
+        return C2_TRANSACTION_FAILED;
+    }
+    c2_status_t status =
+            static_cast<c2_status_t>(static_cast<Status>(transStatus));
+    if (status != C2_OK) {
+        ALOGE("stop -- call failed. "
+                "Error code = %d", static_cast<int>(status));
+    }
+    mInputBuffersMutex.lock();
+    mInputBuffers.clear();
+    mInputBufferCount.clear();
+    mInputBuffersMutex.unlock();
+    return status;
+}
+
+c2_status_t Codec2Client::Component::reset() {
+    Return<Status> transStatus = base()->reset();
+    if (!transStatus.isOk()) {
+        ALOGE("reset -- transaction failed.");
+        return C2_TRANSACTION_FAILED;
+    }
+    c2_status_t status =
+            static_cast<c2_status_t>(static_cast<Status>(transStatus));
+    if (status != C2_OK) {
+        ALOGE("reset -- call failed. "
+                "Error code = %d", static_cast<int>(status));
+    }
+    mInputBuffersMutex.lock();
+    mInputBuffers.clear();
+    mInputBufferCount.clear();
+    mInputBuffersMutex.unlock();
+    return status;
+}
+
+c2_status_t Codec2Client::Component::release() {
+    Return<Status> transStatus = base()->release();
+    if (!transStatus.isOk()) {
+        ALOGE("release -- transaction failed.");
+        return C2_TRANSACTION_FAILED;
+    }
+    c2_status_t status =
+            static_cast<c2_status_t>(static_cast<Status>(transStatus));
+    if (status != C2_OK) {
+        ALOGE("release -- call failed. "
+                "Error code = %d", static_cast<int>(status));
+    }
+    mInputBuffersMutex.lock();
+    mInputBuffers.clear();
+    mInputBufferCount.clear();
+    mInputBuffersMutex.unlock();
+    return status;
+}
+
+c2_status_t Codec2Client::Component::setOutputSurface(
+        C2BlockPool::local_id_t blockPoolId,
+        const sp<IGraphicBufferProducer>& surface,
+        uint32_t generation) {
+    sp<HGraphicBufferProducer> igbp = surface->getHalInterface();
+    if (!igbp) {
+        igbp = new TWGraphicBufferProducer<HGraphicBufferProducer>(surface);
+    }
+
+    Return<Status> transStatus = base()->setOutputSurface(
+            static_cast<uint64_t>(blockPoolId), igbp);
+    if (!transStatus.isOk()) {
+        ALOGE("setOutputSurface -- transaction failed.");
+        return C2_TRANSACTION_FAILED;
+    }
+    c2_status_t status =
+            static_cast<c2_status_t>(static_cast<Status>(transStatus));
+    if (status != C2_OK) {
+        ALOGE("setOutputSurface -- call failed. "
+                "Error code = %d", static_cast<int>(status));
+    } else {
+        std::lock_guard<std::mutex> lock(mOutputBufferQueueMutex);
+        if (mOutputIgbp != surface) {
+            mOutputIgbp = surface;
+            if (!surface) {
+                mOutputBqId = 0;
+            } else if (surface->getUniqueId(&mOutputBqId) != OK) {
+                ALOGE("setOutputSurface -- cannot obtain bufferqueue id.");
+            }
+        }
+        mOutputGeneration = generation;
+    }
+    return status;
+}
+
+status_t Codec2Client::Component::queueToOutputSurface(
+        const C2ConstGraphicBlock& block,
+        const QueueBufferInput& input,
+        QueueBufferOutput* output) {
+    uint32_t generation;
+    uint64_t bqId;
+    int32_t bqSlot;
+    if (!getBufferQueueAssignment(block, &generation, &bqId, &bqSlot) ||
+            bqId == 0) {
+        // Block not from bufferqueue -- it must be attached before queuing.
+
+        mOutputBufferQueueMutex.lock();
+        sp<IGraphicBufferProducer> outputIgbp = mOutputIgbp;
+        uint32_t outputGeneration = mOutputGeneration;
+        mOutputBufferQueueMutex.unlock();
+
+        status_t status = !attachToBufferQueue(block,
+                                               outputIgbp,
+                                               outputGeneration,
+                                               &bqSlot);
+        if (status != OK) {
+            ALOGW("queueToOutputSurface -- attaching failed.");
+            return INVALID_OPERATION;
+        }
+
+        status = outputIgbp->queueBuffer(static_cast<int>(bqSlot),
+                                         input, output);
+        if (status != OK) {
+            ALOGE("queueToOutputSurface -- queueBuffer() failed "
+                    "on non-bufferqueue-based block. "
+                    "Error code = %d.",
+                    static_cast<int>(status));
+            return status;
+        }
+        return OK;
+    }
+
+    mOutputBufferQueueMutex.lock();
+    sp<IGraphicBufferProducer> outputIgbp = mOutputIgbp;
+    uint64_t outputBqId = mOutputBqId;
+    uint32_t outputGeneration = mOutputGeneration;
+    mOutputBufferQueueMutex.unlock();
+
+    if (!outputIgbp) {
+        ALOGV("queueToOutputSurface -- output surface is null.");
+        return NO_INIT;
+    }
+
+    if (bqId != outputBqId) {
+        ALOGV("queueToOutputSurface -- bufferqueue ids mismatch.");
+        return DEAD_OBJECT;
+    }
+
+    if (generation != outputGeneration) {
+        ALOGV("queueToOutputSurface -- generation numbers mismatch.");
+        return DEAD_OBJECT;
+    }
+
+    status_t status = outputIgbp->queueBuffer(static_cast<int>(bqSlot),
+                                              input, output);
+    if (status != OK) {
+        ALOGD("queueToOutputSurface -- queueBuffer() failed "
+                "on bufferqueue-based block. "
+                "Error code = %d.",
+                static_cast<int>(status));
+        return status;
+    }
+    if (!yieldBufferQueueBlock(block)) {
+        ALOGD("queueToOutputSurface -- cannot yield bufferqueue-based block "
+                "to the bufferqueue.");
+        return UNKNOWN_ERROR;
+    }
+    return OK;
+}
+
+c2_status_t Codec2Client::Component::connectToOmxInputSurface(
+        const sp<HGraphicBufferProducer>& producer,
+        const sp<HGraphicBufferSource>& source) {
+    Return<Status> transStatus = base()->connectToOmxInputSurface(
+            producer, source);
+    if (!transStatus.isOk()) {
+        ALOGE("connectToOmxInputSurface -- transaction failed.");
+        return C2_TRANSACTION_FAILED;
+    }
+    c2_status_t status =
+            static_cast<c2_status_t>(static_cast<Status>(transStatus));
+    if (status != C2_OK) {
+        ALOGE("connectToOmxInputSurface -- call failed. "
+                "Error code = %d", static_cast<int>(status));
+    }
+    return status;
+}
+
+c2_status_t Codec2Client::Component::disconnectFromInputSurface() {
+    Return<Status> transStatus = base()->disconnectFromInputSurface();
+    if (!transStatus.isOk()) {
+        ALOGE("disconnectToInputSurface -- transaction failed.");
+        return C2_TRANSACTION_FAILED;
+    }
+    c2_status_t status =
+            static_cast<c2_status_t>(static_cast<Status>(transStatus));
+    if (status != C2_OK) {
+        ALOGE("disconnectFromInputSurface -- call failed. "
+                "Error code = %d", static_cast<int>(status));
+    }
+    return status;
+}
+
+c2_status_t Codec2Client::Component::setDeathListener(
+        const std::shared_ptr<Component>& component,
+        const std::shared_ptr<Listener>& listener) {
+
+    struct HidlDeathRecipient : public hardware::hidl_death_recipient {
+        std::weak_ptr<Component> component;
+        std::weak_ptr<Listener> base;
+
+        virtual void serviceDied(
+                uint64_t /* cookie */,
+                const wp<::android::hidl::base::V1_0::IBase>& /* who */
+                ) override {
+            if (std::shared_ptr<Codec2Client::Listener> listener = base.lock()) {
+                listener->onDeath(component);
+            } else {
+                ALOGW("onDeath -- listener died.");
+            }
+        }
+    };
+
+    sp<HidlDeathRecipient> deathRecipient = new HidlDeathRecipient();
+    deathRecipient->base = listener;
+    deathRecipient->component = component;
+
+    component->mDeathRecipient = deathRecipient;
+    Return<bool> transResult = component->base()->linkToDeath(
+            component->mDeathRecipient, 0);
+    if (!transResult.isOk()) {
+        ALOGE("setDeathListener -- failed transaction: linkToDeath.");
+        return C2_TRANSACTION_FAILED;
+    }
+    if (!static_cast<bool>(transResult)) {
+        ALOGE("setDeathListener -- linkToDeath call failed.");
+        return C2_CORRUPTED;
+    }
+    return C2_OK;
+}
+
+// Codec2Client::InputSurface
+
+Codec2Client::InputSurface::Base* Codec2Client::InputSurface::base() const {
+    return static_cast<Base*>(mBase.get());
+}
+
+Codec2Client::InputSurface::InputSurface(const sp<IInputSurface>& base) :
+    mBase(base),
+    mGraphicBufferProducer(new
+            ::android::hardware::graphics::bufferqueue::V1_0::utils::
+            H2BGraphicBufferProducer(base)) {
+}
+
+c2_status_t Codec2Client::InputSurface::connectToComponent(
+        const std::shared_ptr<Codec2Client::Component>& component,
+        std::shared_ptr<Connection>* connection) {
+    c2_status_t status;
+    Return<void> transStatus = base()->connectToComponent(
+        component->base(),
+        [&status, connection](
+                Status s,
+                const sp<IInputSurfaceConnection>& c) {
+            status = static_cast<c2_status_t>(s);
+            if (status != C2_OK) {
+                ALOGE("connectToComponent -- call failed. "
+                        "Error code = %d", static_cast<int>(status));
+                return;
+            }
+            *connection = std::make_shared<Connection>(c);
+        });
+    if (!transStatus.isOk()) {
+        ALOGE("connect -- transaction failed.");
+        return C2_TRANSACTION_FAILED;
+    }
+    return status;
+}
+
+std::shared_ptr<Codec2Client::Configurable>
+        Codec2Client::InputSurface::getConfigurable() const {
+    Return<sp<IConfigurable>> transResult = base()->getConfigurable();
+    if (!transResult.isOk()) {
+        ALOGW("getConfigurable -- transaction failed.");
+        return nullptr;
+    }
+    if (!static_cast<sp<IConfigurable>>(transResult)) {
+        ALOGW("getConfigurable -- null pointer.");
+        return nullptr;
+    }
+    return std::make_shared<Configurable>(transResult);
+}
+
+const sp<IGraphicBufferProducer>&
+        Codec2Client::InputSurface::getGraphicBufferProducer() const {
+    return mGraphicBufferProducer;
+}
+
+const sp<IInputSurface>& Codec2Client::InputSurface::getHalInterface() const {
+    return mBase;
+}
+
+// Codec2Client::InputSurfaceConnection
+
+Codec2Client::InputSurfaceConnection::Base*
+        Codec2Client::InputSurfaceConnection::base() const {
+    return static_cast<Base*>(mBase.get());
+}
+
+Codec2Client::InputSurfaceConnection::InputSurfaceConnection(
+        const sp<Codec2Client::InputSurfaceConnection::Base>& base) :
+    mBase(base) {
+}
+
+c2_status_t Codec2Client::InputSurfaceConnection::disconnect() {
+    Return<Status> transResult = base()->disconnect();
+    return static_cast<c2_status_t>(static_cast<Status>(transResult));
+}
+
+}  // namespace android
+
diff --git a/media/codec2/hidl/client/include/codec2/hidl/client.h b/media/codec2/hidl/client/include/codec2/hidl/client.h
new file mode 100644
index 0000000..fb59ad6
--- /dev/null
+++ b/media/codec2/hidl/client/include/codec2/hidl/client.h
@@ -0,0 +1,485 @@
+/*
+ * 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 CODEC2_HIDL_CLIENT_H_
+#define CODEC2_HIDL_CLIENT_H_
+
+#include <gui/IGraphicBufferProducer.h>
+#include <codec2/hidl/1.0/types.h>
+
+#include <C2PlatformSupport.h>
+#include <C2Component.h>
+#include <C2Buffer.h>
+#include <C2Param.h>
+#include <C2.h>
+
+#include <hidl/HidlSupport.h>
+#include <utils/StrongPointer.h>
+
+#include <functional>
+#include <map>
+#include <memory>
+#include <mutex>
+
+/**
+ * This file contains minimal interfaces for the framework to access Codec2.0.
+ *
+ * Codec2Client is the main class that contains the following inner classes:
+ * - Listener
+ * - Configurable
+ * - Interface
+ * - Component
+ *
+ * Classes in Codec2Client, interfaces in Codec2.0, and  HIDL interfaces are
+ * related as follows:
+ * - Codec2Client <==> C2ComponentStore <==> IComponentStore
+ * - Codec2Client::Listener <==> C2Component::Listener <==> IComponentListener
+ * - Codec2Client::Configurable <==> [No equivalent] <==> IConfigurable
+ * - Codec2Client::Interface <==> C2ComponentInterface <==> IComponentInterface
+ * - Codec2Client::Component <==> C2Component <==> IComponent
+ *
+ * The entry point is Codec2Client::CreateFromService(), which creates a
+ * Codec2Client object. From Codec2Client, Interface and Component objects can
+ * be created by calling createComponent() and createInterface().
+ *
+ * createComponent() takes a Listener object, which must be implemented by the
+ * user.
+ *
+ * At the present, createBlockPool() is the only method that yields a
+ * Configurable object. Note, however, that Interface, Component and
+ * Codec2Client are all subclasses of Configurable.
+ */
+
+// Forward declaration of Codec2.0 HIDL interfaces
+namespace hardware {
+namespace google {
+namespace media {
+namespace c2 {
+namespace V1_0 {
+struct IConfigurable;
+struct IComponentInterface;
+struct IComponent;
+struct IComponentStore;
+struct IInputSurface;
+struct IInputSurfaceConnection;
+} // namespace V1_0
+} // namespace c2
+} // namespace media
+} // namespace google
+} // namespace hardware
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace bufferpool {
+namespace V1_0 {
+struct IClientManager;
+} // namespace V1_0
+} // namespace bufferpool
+} // namespace media
+} // namespace hardware
+} // namespace android
+
+// Forward declarations of other classes
+namespace android {
+namespace hardware {
+namespace graphics {
+namespace bufferqueue {
+namespace V1_0 {
+struct IGraphicBufferProducer;
+} // namespace V1_0
+} // namespace bufferqueue
+} // namespace graphics
+namespace media {
+namespace omx {
+namespace V1_0 {
+struct IGraphicBufferSource;
+} // namespace V1_0
+} // namespace omx
+} // namespace media
+} // namespace hardware
+} // namespace android
+
+namespace android {
+
+// This class is supposed to be called Codec2Client::Configurable, but forward
+// declaration of an inner class is not possible.
+struct Codec2ConfigurableClient {
+
+    typedef ::hardware::google::media::c2::V1_0::IConfigurable Base;
+
+    const C2String& getName() const;
+
+    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;
+
+    c2_status_t config(
+            const std::vector<C2Param*> &params,
+            c2_blocking_t mayBlock,
+            std::vector<std::unique_ptr<C2SettingResult>>* const failures);
+
+    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;
+
+    // base cannot be null.
+    Codec2ConfigurableClient(const sp<Base>& base);
+
+protected:
+    C2String mName;
+    sp<Base> mBase;
+
+    Base* base() const;
+
+    friend struct Codec2Client;
+};
+
+struct Codec2Client : public Codec2ConfigurableClient {
+
+    typedef ::hardware::google::media::c2::V1_0::IComponentStore Base;
+
+    struct Listener;
+
+    typedef Codec2ConfigurableClient Configurable;
+
+    typedef Configurable Interface; // These two types may diverge in the future.
+
+    struct Component;
+
+    struct InputSurface;
+
+    struct InputSurfaceConnection;
+
+    typedef Codec2Client Store;
+
+    std::string getInstanceName() const { return mInstanceName; }
+
+    c2_status_t createComponent(
+            const C2String& name,
+            const std::shared_ptr<Listener>& listener,
+            std::shared_ptr<Component>* const component);
+
+    c2_status_t createInterface(
+            const C2String& name,
+            std::shared_ptr<Interface>* const interface);
+
+    c2_status_t createInputSurface(
+            std::shared_ptr<InputSurface>* const inputSurface);
+
+    const std::vector<C2Component::Traits>& listComponents() const;
+
+    c2_status_t copyBuffer(
+            const std::shared_ptr<C2Buffer>& src,
+            const std::shared_ptr<C2Buffer>& dst);
+
+    std::shared_ptr<C2ParamReflector> getParamReflector();
+
+    static std::shared_ptr<Codec2Client> CreateFromService(
+            const char* instanceName,
+            bool waitForService = true);
+
+    // Try to create a component with a given name from all known
+    // IComponentStore services.
+    static std::shared_ptr<Component> CreateComponentByName(
+            const char* componentName,
+            const std::shared_ptr<Listener>& listener,
+            std::shared_ptr<Codec2Client>* owner = nullptr);
+
+    // Try to create a component interface with a given name from all known
+    // IComponentStore services.
+    static std::shared_ptr<Interface> CreateInterfaceByName(
+            const char* interfaceName,
+            std::shared_ptr<Codec2Client>* owner = nullptr);
+
+    // List traits from all known IComponentStore services.
+    static const std::vector<C2Component::Traits>& ListComponents();
+
+    // Create an input surface.
+    static std::shared_ptr<InputSurface> CreateInputSurface();
+
+    // base cannot be null.
+    Codec2Client(const sp<Base>& base, std::string instanceName);
+
+protected:
+    Base* base() const;
+
+    // Finds the first store where the predicate returns OK, and returns the last
+    // predicate result. Uses key to remember the last store found, and if cached,
+    // it tries that store before trying all stores (one retry).
+    static c2_status_t ForAllStores(
+            const std::string& key,
+            std::function<c2_status_t(const std::shared_ptr<Codec2Client>&)> predicate);
+
+    mutable std::mutex mMutex;
+    mutable bool mListed;
+    std::string mInstanceName;
+    mutable std::vector<C2Component::Traits> mTraitsList;
+    mutable std::vector<std::unique_ptr<std::vector<std::string>>>
+            mAliasesBuffer;
+
+    sp<::android::hardware::media::bufferpool::V1_0::IClientManager>
+            mHostPoolManager;
+};
+
+struct Codec2Client::Listener {
+
+    // This is called when the component produces some output.
+    //
+    // numDiscardedInputBuffers is the number of input buffers contained in
+    // workItems that have just become unused. Note that workItems may contain
+    // more input buffers than numDiscardedInputBuffers because buffers that
+    // have been previously reported by onInputBufferDone() are not counted
+    // towards numDiscardedInputBuffers, but may still show up in workItems.
+    virtual void onWorkDone(
+            const std::weak_ptr<Component>& comp,
+            std::list<std::unique_ptr<C2Work>>& workItems,
+            size_t numDiscardedInputBuffers) = 0;
+
+    // This is called when the component goes into a tripped state.
+    virtual void onTripped(
+            const std::weak_ptr<Component>& comp,
+            const std::vector<std::shared_ptr<C2SettingResult>>& settingResults
+            ) = 0;
+
+    // This is called when the component encounters an error.
+    virtual void onError(
+            const std::weak_ptr<Component>& comp,
+            uint32_t errorCode) = 0;
+
+    // This is called when the process that hosts the component shuts down
+    // unexpectedly.
+    virtual void onDeath(
+            const std::weak_ptr<Component>& comp) = 0;
+
+    // This is called when an input buffer is no longer in use by the codec.
+    // Input buffers that have been returned by onWorkDone() or flush() will not
+    // trigger a call to this function.
+    virtual void onInputBufferDone(
+            const std::shared_ptr<C2Buffer>& buffer) = 0;
+
+    // This structure is used for transporting onFramesRendered() event to the
+    // client in the case where the output buffers are obtained from a
+    // bufferqueue.
+    struct RenderedFrame {
+        // The id of the bufferqueue.
+        uint64_t bufferQueueId;
+        // The slot of the buffer inside the bufferqueue.
+        int32_t slotId;
+        // The timestamp.
+        int64_t timestampNs;
+
+        RenderedFrame(uint64_t bufferQueueId, int32_t slotId,
+                      int64_t timestampNs)
+              : bufferQueueId(bufferQueueId),
+                slotId(slotId),
+                timestampNs(timestampNs) {}
+        RenderedFrame(const RenderedFrame&) = default;
+    };
+
+    // This is called when the component becomes aware of frames being rendered.
+    virtual void onFramesRendered(
+            const std::vector<RenderedFrame>& renderedFrames) = 0;
+
+    virtual ~Listener();
+
+};
+
+struct Codec2Client::Component : public Codec2Client::Configurable {
+
+    typedef ::hardware::google::media::c2::V1_0::IComponent Base;
+
+    c2_status_t createBlockPool(
+            C2Allocator::id_t id,
+            C2BlockPool::local_id_t* blockPoolId,
+            std::shared_ptr<Configurable>* configurable);
+
+    c2_status_t destroyBlockPool(
+            C2BlockPool::local_id_t localId);
+
+    c2_status_t queue(
+            std::list<std::unique_ptr<C2Work>>* const items);
+
+    c2_status_t flush(
+            C2Component::flush_mode_t mode,
+            std::list<std::unique_ptr<C2Work>>* const flushedWork);
+
+    c2_status_t drain(C2Component::drain_mode_t mode);
+
+    c2_status_t start();
+
+    c2_status_t stop();
+
+    c2_status_t reset();
+
+    c2_status_t release();
+
+    typedef ::android::
+            IGraphicBufferProducer IGraphicBufferProducer;
+    typedef IGraphicBufferProducer::
+            QueueBufferInput QueueBufferInput;
+    typedef IGraphicBufferProducer::
+            QueueBufferOutput QueueBufferOutput;
+
+    typedef ::android::hardware::graphics::bufferqueue::V1_0::
+            IGraphicBufferProducer HGraphicBufferProducer;
+    typedef ::android::hardware::media::omx::V1_0::
+            IGraphicBufferSource HGraphicBufferSource;
+
+    // Set the output surface to be used with a blockpool previously created by
+    // createBlockPool().
+    c2_status_t setOutputSurface(
+            C2BlockPool::local_id_t blockPoolId,
+            const sp<IGraphicBufferProducer>& surface,
+            uint32_t generation);
+
+    // Extract a slot number from of the block, then call
+    // IGraphicBufferProducer::queueBuffer().
+    //
+    // If the output surface has not been set, NO_INIT will be returned.
+    //
+    // If the block does not come from a bufferqueue-based blockpool,
+    // attachBuffer() will be called, followed by queueBuffer().
+    //
+    // If the block has a bqId that does not match the id of the output surface,
+    // DEAD_OBJECT will be returned.
+    //
+    // If the call to queueBuffer() is successful but the block cannot be
+    // associated to the output surface for automatic cancellation upon
+    // destruction, UNKNOWN_ERROR will be returned.
+    //
+    // Otherwise, the return value from queueBuffer() will be returned.
+    status_t queueToOutputSurface(
+            const C2ConstGraphicBlock& block,
+            const QueueBufferInput& input,
+            QueueBufferOutput* output);
+
+    c2_status_t connectToOmxInputSurface(
+            const sp<HGraphicBufferProducer>& producer,
+            const sp<HGraphicBufferSource>& source);
+
+    c2_status_t disconnectFromInputSurface();
+
+    // base cannot be null.
+    Component(const sp<Base>& base);
+
+    ~Component();
+
+protected:
+    Base* base() const;
+
+    // Mutex for mInputBuffers and mInputBufferCount.
+    mutable std::mutex mInputBuffersMutex;
+
+    // Map: frameIndex -> vector of bufferIndices
+    //
+    // mInputBuffers[frameIndex][bufferIndex] may be null if the buffer in that
+    // slot has been freed.
+    mutable std::map<uint64_t, std::vector<std::shared_ptr<C2Buffer>>>
+            mInputBuffers;
+
+    // Map: frameIndex -> number of bufferIndices that have not been freed
+    //
+    // mInputBufferCount[frameIndex] keeps track of the number of non-null
+    // elements in mInputBuffers[frameIndex]. When mInputBufferCount[frameIndex]
+    // decreases to 0, frameIndex can be removed from both mInputBuffers and
+    // mInputBufferCount.
+    mutable std::map<uint64_t, size_t> mInputBufferCount;
+
+    ::hardware::google::media::c2::V1_0::utils::DefaultBufferPoolSender
+            mBufferPoolSender;
+
+    std::mutex mOutputBufferQueueMutex;
+    sp<IGraphicBufferProducer> mOutputIgbp;
+    uint64_t mOutputBqId;
+    uint32_t mOutputGeneration;
+
+    static c2_status_t setDeathListener(
+            const std::shared_ptr<Component>& component,
+            const std::shared_ptr<Listener>& listener);
+    sp<::android::hardware::hidl_death_recipient> mDeathRecipient;
+
+    friend struct Codec2Client;
+
+    struct HidlListener;
+    // Return the number of input buffers that should be discarded.
+    size_t handleOnWorkDone(const std::list<std::unique_ptr<C2Work>> &workItems);
+    // Remove an input buffer from mInputBuffers and return it.
+    std::shared_ptr<C2Buffer> freeInputBuffer(uint64_t frameIndex, size_t bufferIndex);
+
+};
+
+struct Codec2Client::InputSurface {
+public:
+    typedef ::hardware::google::media::c2::V1_0::IInputSurface Base;
+
+    typedef ::hardware::google::media::c2::V1_0::IInputSurfaceConnection
+            ConnectionBase;
+
+    typedef Codec2Client::InputSurfaceConnection Connection;
+
+    typedef ::android::IGraphicBufferProducer IGraphicBufferProducer;
+
+    c2_status_t connectToComponent(
+            const std::shared_ptr<Component>& component,
+            std::shared_ptr<Connection>* connection);
+
+    std::shared_ptr<Configurable> getConfigurable() const;
+
+    const sp<IGraphicBufferProducer>& getGraphicBufferProducer() const;
+
+    // Return the underlying IInputSurface.
+    const sp<Base>& getHalInterface() const;
+
+    // base cannot be null.
+    InputSurface(const sp<Base>& base);
+
+protected:
+    Base* base() const;
+    sp<Base> mBase;
+
+    sp<IGraphicBufferProducer> mGraphicBufferProducer;
+
+    friend struct Codec2Client;
+    friend struct Component;
+};
+
+struct Codec2Client::InputSurfaceConnection {
+
+    typedef ::hardware::google::media::c2::V1_0::IInputSurfaceConnection Base;
+
+    c2_status_t disconnect();
+
+    // base cannot be null.
+    InputSurfaceConnection(const sp<Base>& base);
+
+protected:
+    Base* base() const;
+    sp<Base> mBase;
+
+    friend struct Codec2Client::InputSurface;
+};
+
+}  // namespace android
+
+#endif  // CODEC2_HIDL_CLIENT_H_
+