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

//#define LOG_NDEBUG 0
#define LOG_TAG "Codec2-types"
#include <android-base/logging.h>

#include <codec2/hidl/1.0/types.h>

#include <media/stagefright/bqhelper/WGraphicBufferProducer.h>
#include <media/stagefright/foundation/AUtils.h>

#include <C2AllocatorIon.h>
#include <C2AllocatorGralloc.h>
#include <C2BlockInternal.h>
#include <C2Buffer.h>
#include <C2Component.h>
#include <C2Param.h>
#include <C2ParamInternal.h>
#include <C2PlatformSupport.h>
#include <C2Work.h>
#include <util/C2ParamUtils.h>

#include <algorithm>
#include <functional>
#include <iomanip>
#include <unordered_map>

namespace android {
namespace hardware {
namespace media {
namespace c2 {
namespace V1_0 {
namespace utils {

using namespace ::android;
using ::android::hardware::Return;
using ::android::hardware::media::bufferpool::BufferPoolData;
using ::android::hardware::media::bufferpool::V2_0::BufferStatusMessage;
using ::android::hardware::media::bufferpool::V2_0::ResultStatus;
using ::android::hardware::media::bufferpool::V2_0::implementation::
        ClientManager;
using ::android::hardware::media::bufferpool::V2_0::implementation::
        TransactionId;
using ::android::TWGraphicBufferProducer;

const char* asString(Status status, const char* def) {
    return asString(static_cast<c2_status_t>(status), def);
}

namespace /* unnamed */ {

template <typename EnumClass>
typename std::underlying_type<EnumClass>::type underlying_value(
        EnumClass x) {
    return static_cast<typename std::underlying_type<EnumClass>::type>(x);
}

template <typename Common, typename DstVector, typename SrcVector>
void copyVector(DstVector* d, const SrcVector& s) {
    static_assert(sizeof(Common) == sizeof(decltype((*d)[0])),
            "DstVector's component size does not match Common");
    static_assert(sizeof(Common) == sizeof(decltype(s[0])),
            "SrcVector's component size does not match Common");
    d->resize(s.size());
    std::copy(
            reinterpret_cast<const Common*>(&s[0]),
            reinterpret_cast<const Common*>(&s[0] + s.size()),
            reinterpret_cast<Common*>(&(*d)[0]));
}

// C2ParamField -> ParamField
bool objcpy(ParamField *d, const C2ParamField &s) {
    d->index = static_cast<ParamIndex>(_C2ParamInspector::GetIndex(s));
    d->fieldId.offset = static_cast<uint32_t>(_C2ParamInspector::GetOffset(s));
    d->fieldId.size = static_cast<uint32_t>(_C2ParamInspector::GetSize(s));
    return true;
}

struct C2ParamFieldBuilder : public C2ParamField {
    C2ParamFieldBuilder() : C2ParamField(
            static_cast<C2Param::Index>(static_cast<uint32_t>(0)), 0, 0) {
    }
    // ParamField -> C2ParamField
    C2ParamFieldBuilder(const ParamField& s) : C2ParamField(
            static_cast<C2Param::Index>(static_cast<uint32_t>(s.index)),
            static_cast<uint32_t>(s.fieldId.offset),
            static_cast<uint32_t>(s.fieldId.size)) {
    }
};

// C2WorkOrdinalStruct -> WorkOrdinal
bool objcpy(WorkOrdinal *d, const C2WorkOrdinalStruct &s) {
    d->frameIndex = static_cast<uint64_t>(s.frameIndex.peeku());
    d->timestampUs = static_cast<uint64_t>(s.timestamp.peeku());
    d->customOrdinal = static_cast<uint64_t>(s.customOrdinal.peeku());
    return true;
}

// WorkOrdinal -> C2WorkOrdinalStruct
bool objcpy(C2WorkOrdinalStruct *d, const WorkOrdinal &s) {
    d->frameIndex = c2_cntr64_t(s.frameIndex);
    d->timestamp = c2_cntr64_t(s.timestampUs);
    d->customOrdinal = c2_cntr64_t(s.customOrdinal);
    return true;
}

// C2FieldSupportedValues::range's type -> FieldSupportedValues::Range
bool objcpy(
        FieldSupportedValues::Range* d,
        const decltype(C2FieldSupportedValues::range)& s) {
    d->min = static_cast<PrimitiveValue>(s.min.u64);
    d->max = static_cast<PrimitiveValue>(s.max.u64);
    d->step = static_cast<PrimitiveValue>(s.step.u64);
    d->num = static_cast<PrimitiveValue>(s.num.u64);
    d->denom = static_cast<PrimitiveValue>(s.denom.u64);
    return true;
}

// C2FieldSupportedValues -> FieldSupportedValues
bool objcpy(FieldSupportedValues *d, const C2FieldSupportedValues &s) {
    switch (s.type) {
    case C2FieldSupportedValues::EMPTY:
        d->type = FieldSupportedValues::Type::EMPTY;
        d->values.resize(0);
        break;
    case C2FieldSupportedValues::RANGE:
        d->type = FieldSupportedValues::Type::RANGE;
        if (!objcpy(&d->range, s.range)) {
            LOG(ERROR) << "Invalid C2FieldSupportedValues::range.";
            return false;
        }
        d->values.resize(0);
        break;
    default:
        switch (s.type) {
        case C2FieldSupportedValues::VALUES:
            d->type = FieldSupportedValues::Type::VALUES;
            break;
        case C2FieldSupportedValues::FLAGS:
            d->type = FieldSupportedValues::Type::FLAGS;
            break;
        default:
            LOG(DEBUG) << "Unrecognized C2FieldSupportedValues::type_t "
                       << "with underlying value " << underlying_value(s.type)
                       << ".";
            d->type = static_cast<FieldSupportedValues::Type>(s.type);
            if (!objcpy(&d->range, s.range)) {
                LOG(ERROR) << "Invalid C2FieldSupportedValues::range.";
                return false;
            }
        }
        copyVector<uint64_t>(&d->values, s.values);
    }
    return true;
}

// FieldSupportedValues::Range -> C2FieldSupportedValues::range's type
bool objcpy(
        decltype(C2FieldSupportedValues::range)* d,
        const FieldSupportedValues::Range& s) {
    d->min.u64 = static_cast<uint64_t>(s.min);
    d->max.u64 = static_cast<uint64_t>(s.max);
    d->step.u64 = static_cast<uint64_t>(s.step);
    d->num.u64 = static_cast<uint64_t>(s.num);
    d->denom.u64 = static_cast<uint64_t>(s.denom);
    return true;
}

// FieldSupportedValues -> C2FieldSupportedValues
bool objcpy(C2FieldSupportedValues *d, const FieldSupportedValues &s) {
    switch (s.type) {
    case FieldSupportedValues::Type::EMPTY:
        d->type = C2FieldSupportedValues::EMPTY;
        break;
    case FieldSupportedValues::Type::RANGE:
        d->type = C2FieldSupportedValues::RANGE;
        if (!objcpy(&d->range, s.range)) {
            LOG(ERROR) << "Invalid FieldSupportedValues::range.";
            return false;
        }
        d->values.resize(0);
        break;
    default:
        switch (s.type) {
        case FieldSupportedValues::Type::VALUES:
            d->type = C2FieldSupportedValues::VALUES;
            break;
        case FieldSupportedValues::Type::FLAGS:
            d->type = C2FieldSupportedValues::FLAGS;
            break;
        default:
            LOG(DEBUG) << "Unrecognized FieldSupportedValues::Type "
                       << "with underlying value " << underlying_value(s.type)
                       << ".";
            d->type = static_cast<C2FieldSupportedValues::type_t>(s.type);
            if (!objcpy(&d->range, s.range)) {
                LOG(ERROR) << "Invalid FieldSupportedValues::range.";
                return false;
            }
        }
        copyVector<uint64_t>(&d->values, s.values);
    }
    return true;
}

} // unnamed namespace

// C2FieldSupportedValuesQuery -> FieldSupportedValuesQuery
bool objcpy(
        FieldSupportedValuesQuery* d,
        const C2FieldSupportedValuesQuery& s) {
    if (!objcpy(&d->field, s.field())) {
        LOG(ERROR) << "Invalid C2FieldSupportedValuesQuery::field.";
        return false;
    }
    switch (s.type()) {
    case C2FieldSupportedValuesQuery::POSSIBLE:
        d->type = FieldSupportedValuesQuery::Type::POSSIBLE;
        break;
    case C2FieldSupportedValuesQuery::CURRENT:
        d->type = FieldSupportedValuesQuery::Type::CURRENT;
        break;
    default:
        LOG(DEBUG) << "Unrecognized C2FieldSupportedValuesQuery::type_t "
                   << "with underlying value " << underlying_value(s.type())
                   << ".";
        d->type = static_cast<FieldSupportedValuesQuery::Type>(s.type());
    }
    return true;
}

// FieldSupportedValuesQuery -> C2FieldSupportedValuesQuery
bool objcpy(
        C2FieldSupportedValuesQuery* d,
        const FieldSupportedValuesQuery& s) {
    C2FieldSupportedValuesQuery::type_t dType;
    switch (s.type) {
    case FieldSupportedValuesQuery::Type::POSSIBLE:
        dType = C2FieldSupportedValuesQuery::POSSIBLE;
        break;
    case FieldSupportedValuesQuery::Type::CURRENT:
        dType = C2FieldSupportedValuesQuery::CURRENT;
        break;
    default:
        LOG(DEBUG) << "Unrecognized FieldSupportedValuesQuery::Type "
                   << "with underlying value " << underlying_value(s.type)
                   << ".";
        dType = static_cast<C2FieldSupportedValuesQuery::type_t>(s.type);
    }
    *d = C2FieldSupportedValuesQuery(C2ParamFieldBuilder(s.field), dType);
    return true;
}

// C2FieldSupportedValuesQuery -> FieldSupportedValuesQueryResult
bool objcpy(
        FieldSupportedValuesQueryResult* d,
        const C2FieldSupportedValuesQuery& s) {
    d->status = static_cast<Status>(s.status);
    return objcpy(&d->values, s.values);
}

// FieldSupportedValuesQuery, FieldSupportedValuesQueryResult ->
// C2FieldSupportedValuesQuery
bool objcpy(
        C2FieldSupportedValuesQuery* d,
        const FieldSupportedValuesQuery& sq,
        const FieldSupportedValuesQueryResult& sr) {
    if (!objcpy(d, sq)) {
        LOG(ERROR) << "Invalid FieldSupportedValuesQuery.";
        return false;
    }
    d->status = static_cast<c2_status_t>(sr.status);
    if (!objcpy(&d->values, sr.values)) {
        LOG(ERROR) << "Invalid FieldSupportedValuesQueryResult::values.";
        return false;
    }
    return true;
}

// C2Component::Traits -> IComponentStore::ComponentTraits
bool objcpy(
        IComponentStore::ComponentTraits *d,
        const C2Component::Traits &s) {
    d->name = s.name;

    switch (s.domain) {
    case C2Component::DOMAIN_VIDEO:
        d->domain = IComponentStore::ComponentTraits::Domain::VIDEO;
        break;
    case C2Component::DOMAIN_AUDIO:
        d->domain = IComponentStore::ComponentTraits::Domain::AUDIO;
        break;
    case C2Component::DOMAIN_IMAGE:
        d->domain = IComponentStore::ComponentTraits::Domain::IMAGE;
        break;
    case C2Component::DOMAIN_OTHER:
        d->domain = IComponentStore::ComponentTraits::Domain::OTHER;
        break;
    default:
        LOG(DEBUG) << "Unrecognized C2Component::domain_t "
                   << "with underlying value " << underlying_value(s.domain)
                   << ".";
        d->domain = static_cast<IComponentStore::ComponentTraits::Domain>(
                s.domain);
    }

    switch (s.kind) {
    case C2Component::KIND_DECODER:
        d->kind = IComponentStore::ComponentTraits::Kind::DECODER;
        break;
    case C2Component::KIND_ENCODER:
        d->kind = IComponentStore::ComponentTraits::Kind::ENCODER;
        break;
    case C2Component::KIND_OTHER:
        d->kind = IComponentStore::ComponentTraits::Kind::OTHER;
        break;
    default:
        LOG(DEBUG) << "Unrecognized C2Component::kind_t "
                   << "with underlying value " << underlying_value(s.kind)
                   << ".";
        d->kind = static_cast<IComponentStore::ComponentTraits::Kind>(
                s.kind);
    }

    d->rank = static_cast<uint32_t>(s.rank);

    d->mediaType = s.mediaType;

    d->aliases.resize(s.aliases.size());
    for (size_t ix = s.aliases.size(); ix > 0; ) {
        --ix;
        d->aliases[ix] = s.aliases[ix];
    }
    return true;
}

// ComponentTraits -> C2Component::Traits, std::unique_ptr<std::vector<std::string>>
bool objcpy(
        C2Component::Traits* d,
        const IComponentStore::ComponentTraits& s) {
    d->name = s.name.c_str();

    switch (s.domain) {
    case IComponentStore::ComponentTraits::Domain::VIDEO:
        d->domain = C2Component::DOMAIN_VIDEO;
        break;
    case IComponentStore::ComponentTraits::Domain::AUDIO:
        d->domain = C2Component::DOMAIN_AUDIO;
        break;
    case IComponentStore::ComponentTraits::Domain::IMAGE:
        d->domain = C2Component::DOMAIN_IMAGE;
        break;
    case IComponentStore::ComponentTraits::Domain::OTHER:
        d->domain = C2Component::DOMAIN_OTHER;
        break;
    default:
        LOG(DEBUG) << "Unrecognized ComponentTraits::Domain "
                   << "with underlying value " << underlying_value(s.domain)
                   << ".";
        d->domain = static_cast<C2Component::domain_t>(s.domain);
    }

    switch (s.kind) {
    case IComponentStore::ComponentTraits::Kind::DECODER:
        d->kind = C2Component::KIND_DECODER;
        break;
    case IComponentStore::ComponentTraits::Kind::ENCODER:
        d->kind = C2Component::KIND_ENCODER;
        break;
    case IComponentStore::ComponentTraits::Kind::OTHER:
        d->kind = C2Component::KIND_OTHER;
        break;
    default:
        LOG(DEBUG) << "Unrecognized ComponentTraits::Kind "
                   << "with underlying value " << underlying_value(s.kind)
                   << ".";
        d->kind = static_cast<C2Component::kind_t>(s.kind);
    }

    d->rank = static_cast<C2Component::rank_t>(s.rank);
    d->mediaType = s.mediaType.c_str();
    d->aliases.resize(s.aliases.size());
    for (size_t i = 0; i < s.aliases.size(); ++i) {
        d->aliases[i] = s.aliases[i];
    }
    return true;
}

namespace /* unnamed */ {

// C2ParamFieldValues -> ParamFieldValues
bool objcpy(ParamFieldValues *d, const C2ParamFieldValues &s) {
    if (!objcpy(&d->paramOrField, s.paramOrField)) {
        LOG(ERROR) << "Invalid C2ParamFieldValues::paramOrField.";
        return false;
    }
    if (s.values) {
        d->values.resize(1);
        if (!objcpy(&d->values[0], *s.values)) {
            LOG(ERROR) << "Invalid C2ParamFieldValues::values.";
            return false;
        }
        return true;
    }
    d->values.resize(0);
    return true;
}

// ParamFieldValues -> C2ParamFieldValues
bool objcpy(C2ParamFieldValues *d, const ParamFieldValues &s) {
    d->paramOrField = C2ParamFieldBuilder(s.paramOrField);
    if (s.values.size() == 1) {
        d->values = std::make_unique<C2FieldSupportedValues>();
        if (!objcpy(d->values.get(), s.values[0])) {
            LOG(ERROR) << "Invalid ParamFieldValues::values.";
            return false;
        }
        return true;
    } else if (s.values.size() == 0) {
        d->values.reset();
        return true;
    }
    LOG(ERROR) << "Invalid ParamFieldValues: "
                  "Two or more FieldSupportedValues objects exist in "
                  "ParamFieldValues. "
                  "Only zero or one is allowed.";
    return false;
}

} // unnamed namespace

// C2SettingResult -> SettingResult
bool objcpy(SettingResult *d, const C2SettingResult &s) {
    switch (s.failure) {
    case C2SettingResult::BAD_TYPE:
        d->failure = SettingResult::Failure::BAD_TYPE;
        break;
    case C2SettingResult::BAD_PORT:
        d->failure = SettingResult::Failure::BAD_PORT;
        break;
    case C2SettingResult::BAD_INDEX:
        d->failure = SettingResult::Failure::BAD_INDEX;
        break;
    case C2SettingResult::READ_ONLY:
        d->failure = SettingResult::Failure::READ_ONLY;
        break;
    case C2SettingResult::MISMATCH:
        d->failure = SettingResult::Failure::MISMATCH;
        break;
    case C2SettingResult::BAD_VALUE:
        d->failure = SettingResult::Failure::BAD_VALUE;
        break;
    case C2SettingResult::CONFLICT:
        d->failure = SettingResult::Failure::CONFLICT;
        break;
    case C2SettingResult::UNSUPPORTED:
        d->failure = SettingResult::Failure::UNSUPPORTED;
        break;
    case C2SettingResult::INFO_BAD_VALUE:
        d->failure = SettingResult::Failure::INFO_BAD_VALUE;
        break;
    case C2SettingResult::INFO_CONFLICT:
        d->failure = SettingResult::Failure::INFO_CONFLICT;
        break;
    default:
        LOG(DEBUG) << "Unrecognized C2SettingResult::Failure "
                   << "with underlying value " << underlying_value(s.failure)
                   << ".";
        d->failure = static_cast<SettingResult::Failure>(s.failure);
    }
    if (!objcpy(&d->field, s.field)) {
        LOG(ERROR) << "Invalid C2SettingResult::field.";
        return false;
    }
    d->conflicts.resize(s.conflicts.size());
    size_t i = 0;
    for (const C2ParamFieldValues& sConflict : s.conflicts) {
        ParamFieldValues &dConflict = d->conflicts[i++];
        if (!objcpy(&dConflict, sConflict)) {
            LOG(ERROR) << "Invalid C2SettingResult::conflicts["
                       << i - 1 << "].";
            return false;
        }
    }
    return true;
}

// SettingResult -> std::unique_ptr<C2SettingResult>
bool objcpy(std::unique_ptr<C2SettingResult> *d, const SettingResult &s) {
    *d = std::unique_ptr<C2SettingResult>(new C2SettingResult {
            .field = C2ParamFieldValues(C2ParamFieldBuilder()) });
    if (!*d) {
        LOG(ERROR) << "No memory for C2SettingResult.";
        return false;
    }

    // failure
    switch (s.failure) {
    case SettingResult::Failure::BAD_TYPE:
        (*d)->failure = C2SettingResult::BAD_TYPE;
        break;
    case SettingResult::Failure::BAD_PORT:
        (*d)->failure = C2SettingResult::BAD_PORT;
        break;
    case SettingResult::Failure::BAD_INDEX:
        (*d)->failure = C2SettingResult::BAD_INDEX;
        break;
    case SettingResult::Failure::READ_ONLY:
        (*d)->failure = C2SettingResult::READ_ONLY;
        break;
    case SettingResult::Failure::MISMATCH:
        (*d)->failure = C2SettingResult::MISMATCH;
        break;
    case SettingResult::Failure::BAD_VALUE:
        (*d)->failure = C2SettingResult::BAD_VALUE;
        break;
    case SettingResult::Failure::CONFLICT:
        (*d)->failure = C2SettingResult::CONFLICT;
        break;
    case SettingResult::Failure::UNSUPPORTED:
        (*d)->failure = C2SettingResult::UNSUPPORTED;
        break;
    case SettingResult::Failure::INFO_BAD_VALUE:
        (*d)->failure = C2SettingResult::INFO_BAD_VALUE;
        break;
    case SettingResult::Failure::INFO_CONFLICT:
        (*d)->failure = C2SettingResult::INFO_CONFLICT;
        break;
    default:
        LOG(DEBUG) << "Unrecognized SettingResult::Failure "
                   << "with underlying value " << underlying_value(s.failure)
                   << ".";
        (*d)->failure = static_cast<C2SettingResult::Failure>(s.failure);
    }

    // field
    if (!objcpy(&(*d)->field, s.field)) {
        LOG(ERROR) << "Invalid SettingResult::field.";
        return false;
    }

    // conflicts
    (*d)->conflicts.clear();
    (*d)->conflicts.reserve(s.conflicts.size());
    for (const ParamFieldValues& sConflict : s.conflicts) {
        (*d)->conflicts.emplace_back(
                C2ParamFieldValues{ C2ParamFieldBuilder(), nullptr });
        if (!objcpy(&(*d)->conflicts.back(), sConflict)) {
            LOG(ERROR) << "Invalid SettingResult::conflicts.";
            return false;
        }
    }
    return true;
}

// C2ParamDescriptor -> ParamDescriptor
bool objcpy(ParamDescriptor *d, const C2ParamDescriptor &s) {
    d->index = static_cast<ParamIndex>(s.index());
    d->attrib = static_cast<hidl_bitfield<ParamDescriptor::Attrib>>(
            _C2ParamInspector::GetAttrib(s));
    d->name = s.name();
    copyVector<uint32_t>(&d->dependencies, s.dependencies());
    return true;
}

// ParamDescriptor -> C2ParamDescriptor
bool objcpy(std::shared_ptr<C2ParamDescriptor> *d, const ParamDescriptor &s) {
    std::vector<C2Param::Index> dDependencies;
    dDependencies.reserve(s.dependencies.size());
    for (const ParamIndex& sDependency : s.dependencies) {
        dDependencies.emplace_back(static_cast<uint32_t>(sDependency));
    }
    *d = std::make_shared<C2ParamDescriptor>(
            C2Param::Index(static_cast<uint32_t>(s.index)),
            static_cast<C2ParamDescriptor::attrib_t>(s.attrib),
            C2String(s.name.c_str()),
            std::move(dDependencies));
    return true;
}

// C2StructDescriptor -> StructDescriptor
bool objcpy(StructDescriptor *d, const C2StructDescriptor &s) {
    d->type = static_cast<ParamIndex>(s.coreIndex().coreIndex());
    d->fields.resize(s.numFields());
    size_t i = 0;
    for (const auto& sField : s) {
        FieldDescriptor& dField = d->fields[i++];
        dField.fieldId.offset = static_cast<uint32_t>(
                _C2ParamInspector::GetOffset(sField));
        dField.fieldId.size = static_cast<uint32_t>(
                _C2ParamInspector::GetSize(sField));
        dField.type = static_cast<hidl_bitfield<FieldDescriptor::Type>>(
                sField.type());
        dField.extent = static_cast<uint32_t>(sField.extent());
        dField.name = static_cast<hidl_string>(sField.name());
        const auto& sNamedValues = sField.namedValues();
        dField.namedValues.resize(sNamedValues.size());
        size_t j = 0;
        for (const auto& sNamedValue : sNamedValues) {
            FieldDescriptor::NamedValue& dNamedValue = dField.namedValues[j++];
            dNamedValue.name = static_cast<hidl_string>(sNamedValue.first);
            dNamedValue.value = static_cast<PrimitiveValue>(
                    sNamedValue.second.u64);
        }
    }
    return true;
}

// StructDescriptor -> C2StructDescriptor
bool objcpy(std::unique_ptr<C2StructDescriptor> *d, const StructDescriptor &s) {
    C2Param::CoreIndex dIndex = C2Param::CoreIndex(static_cast<uint32_t>(s.type));
    std::vector<C2FieldDescriptor> dFields;
    dFields.reserve(s.fields.size());
    for (const auto &sField : s.fields) {
        C2FieldDescriptor dField = {
            static_cast<uint32_t>(sField.type),
            sField.extent,
            sField.name,
            sField.fieldId.offset,
            sField.fieldId.size };
        C2FieldDescriptor::NamedValuesType namedValues;
        namedValues.reserve(sField.namedValues.size());
        for (const auto& sNamedValue : sField.namedValues) {
            namedValues.emplace_back(
                sNamedValue.name,
                C2Value::Primitive(static_cast<uint64_t>(sNamedValue.value)));
        }
        _C2ParamInspector::AddNamedValues(dField, std::move(namedValues));
        dFields.emplace_back(dField);
    }
    *d = std::make_unique<C2StructDescriptor>(
            _C2ParamInspector::CreateStructDescriptor(dIndex, std::move(dFields)));
    return true;
}

namespace /* unnamed */ {

// Find or add a hidl BaseBlock object from a given C2Handle* to a list and an
// associated map.
// Note: The handle is not cloned.
bool _addBaseBlock(
        uint32_t* index,
        const C2Handle* handle,
        std::list<BaseBlock>* baseBlocks,
        std::map<const void*, uint32_t>* baseBlockIndices) {
    if (!handle) {
        LOG(ERROR) << "addBaseBlock called on a null C2Handle.";
        return false;
    }
    auto it = baseBlockIndices->find(handle);
    if (it != baseBlockIndices->end()) {
        *index = it->second;
    } else {
        *index = baseBlocks->size();
        baseBlockIndices->emplace(handle, *index);
        baseBlocks->emplace_back();

        BaseBlock &dBaseBlock = baseBlocks->back();
        // This does not clone the handle.
        dBaseBlock.nativeBlock(
                reinterpret_cast<const native_handle_t*>(handle));

    }
    return true;
}

// Find or add a hidl BaseBlock object from a given BufferPoolData to a list and
// an associated map.
bool _addBaseBlock(
        uint32_t* index,
        const std::shared_ptr<BufferPoolData> bpData,
        BufferPoolSender* bufferPoolSender,
        std::list<BaseBlock>* baseBlocks,
        std::map<const void*, uint32_t>* baseBlockIndices) {
    if (!bpData) {
        LOG(ERROR) << "addBaseBlock called on a null BufferPoolData.";
        return false;
    }
    auto it = baseBlockIndices->find(bpData.get());
    if (it != baseBlockIndices->end()) {
        *index = it->second;
    } else {
        *index = baseBlocks->size();
        baseBlockIndices->emplace(bpData.get(), *index);
        baseBlocks->emplace_back();

        BaseBlock &dBaseBlock = baseBlocks->back();

        if (bufferPoolSender) {
            BufferStatusMessage pooledBlock;
            ResultStatus bpStatus = bufferPoolSender->send(
                    bpData,
                    &pooledBlock);

            if (bpStatus != ResultStatus::OK) {
                LOG(ERROR) << "Failed to send buffer with BufferPool. Error: "
                           << static_cast<int32_t>(bpStatus)
                           << ".";
                return false;
            }
            dBaseBlock.pooledBlock(pooledBlock);
        }
    }
    return true;
}

bool addBaseBlock(
        uint32_t* index,
        const C2Handle* handle,
        const std::shared_ptr<const _C2BlockPoolData>& blockPoolData,
        BufferPoolSender* bufferPoolSender,
        std::list<BaseBlock>* baseBlocks,
        std::map<const void*, uint32_t>* baseBlockIndices) {
    if (!blockPoolData) {
        // No BufferPoolData ==> NATIVE block.
        return _addBaseBlock(
                index, handle,
                baseBlocks, baseBlockIndices);
    }
    switch (blockPoolData->getType()) {
    case _C2BlockPoolData::TYPE_BUFFERPOOL: {
            // BufferPoolData
            std::shared_ptr<BufferPoolData> bpData;
            if (!_C2BlockFactory::GetBufferPoolData(blockPoolData, &bpData)
                    || !bpData) {
                LOG(ERROR) << "BufferPoolData unavailable in a block.";
                return false;
            }
            return _addBaseBlock(
                    index, bpData,
                    bufferPoolSender, baseBlocks, baseBlockIndices);
        }
    case _C2BlockPoolData::TYPE_BUFFERQUEUE:
        // Do the same thing as a NATIVE block.
        return _addBaseBlock(
                index, handle,
                baseBlocks, baseBlockIndices);
    default:
        LOG(ERROR) << "Unknown C2BlockPoolData type.";
        return false;
    }
}

// C2Fence -> hidl_handle
// Note: File descriptors are not duplicated. The original file descriptor must
// not be closed before the transaction is complete.
bool objcpy(hidl_handle* d, const C2Fence& s) {
    (void)s; // TODO: implement s.fd()
    int fenceFd = -1;
    d->setTo(nullptr);
    if (fenceFd >= 0) {
        native_handle_t *handle = native_handle_create(1, 0);
        if (!handle) {
            LOG(ERROR) << "Failed to create a native handle.";
            return false;
        }
        handle->data[0] = fenceFd;
        d->setTo(handle, true /* owns */);
    }
    return true;
}

// C2ConstLinearBlock -> Block
// Note: Native handles are not duplicated. The original handles must not be
// closed before the transaction is complete.
bool objcpy(Block* d, const C2ConstLinearBlock& s,
        BufferPoolSender* bufferPoolSender,
        std::list<BaseBlock>* baseBlocks,
        std::map<const void*, uint32_t>* baseBlockIndices) {
    std::shared_ptr<const _C2BlockPoolData> bpData =
            _C2BlockFactory::GetLinearBlockPoolData(s);
    if (!addBaseBlock(&d->index, s.handle(), bpData,
            bufferPoolSender, baseBlocks, baseBlockIndices)) {
        LOG(ERROR) << "Invalid block data in C2ConstLinearBlock.";
        return false;
    }

    // Create the metadata.
    C2Hidl_RangeInfo dRangeInfo;
    dRangeInfo.offset = static_cast<uint32_t>(s.offset());
    dRangeInfo.length = static_cast<uint32_t>(s.size());
    if (!createParamsBlob(&d->meta, std::vector<C2Param*>{ &dRangeInfo })) {
        LOG(ERROR) << "Invalid range info in C2ConstLinearBlock.";
        return false;
    }

    // Copy the fence
    if (!objcpy(&d->fence, s.fence())) {
        LOG(ERROR) << "Invalid C2ConstLinearBlock::fence.";
        return false;
    }
    return true;
}

// C2ConstGraphicBlock -> Block
// Note: Native handles are not duplicated. The original handles must not be
// closed before the transaction is complete.
bool objcpy(Block* d, const C2ConstGraphicBlock& s,
        BufferPoolSender* bufferPoolSender,
        std::list<BaseBlock>* baseBlocks,
        std::map<const void*, uint32_t>* baseBlockIndices) {
    std::shared_ptr<const _C2BlockPoolData> bpData =
            _C2BlockFactory::GetGraphicBlockPoolData(s);
    if (!addBaseBlock(&d->index, s.handle(), bpData,
            bufferPoolSender, baseBlocks, baseBlockIndices)) {
        LOG(ERROR) << "Invalid block data in C2ConstGraphicBlock.";
        return false;
    }

    // Create the metadata.
    C2Hidl_RectInfo dRectInfo;
    C2Rect sRect = s.crop();
    dRectInfo.left = static_cast<uint32_t>(sRect.left);
    dRectInfo.top = static_cast<uint32_t>(sRect.top);
    dRectInfo.width = static_cast<uint32_t>(sRect.width);
    dRectInfo.height = static_cast<uint32_t>(sRect.height);
    if (!createParamsBlob(&d->meta, std::vector<C2Param*>{ &dRectInfo })) {
        LOG(ERROR) << "Invalid rect info in C2ConstGraphicBlock.";
        return false;
    }

    // Copy the fence
    if (!objcpy(&d->fence, s.fence())) {
        LOG(ERROR) << "Invalid C2ConstGraphicBlock::fence.";
        return false;
    }
    return true;
}

// C2BufferData -> Buffer
// This function only fills in d->blocks.
bool objcpy(Buffer* d, const C2BufferData& s,
        BufferPoolSender* bufferPoolSender,
        std::list<BaseBlock>* baseBlocks,
        std::map<const void*, uint32_t>* baseBlockIndices) {
    d->blocks.resize(
            s.linearBlocks().size() +
            s.graphicBlocks().size());
    size_t i = 0;
    for (const C2ConstLinearBlock& linearBlock : s.linearBlocks()) {
        Block& dBlock = d->blocks[i++];
        if (!objcpy(
                &dBlock, linearBlock,
                bufferPoolSender, baseBlocks, baseBlockIndices)) {
            LOG(ERROR) << "Invalid C2BufferData::linearBlocks. "
                       << "(Destination index = " << i - 1 << ".)";
            return false;
        }
    }
    for (const C2ConstGraphicBlock& graphicBlock : s.graphicBlocks()) {
        Block& dBlock = d->blocks[i++];
        if (!objcpy(
                &dBlock, graphicBlock,
                bufferPoolSender, baseBlocks, baseBlockIndices)) {
            LOG(ERROR) << "Invalid C2BufferData::graphicBlocks. "
                       << "(Destination index = " << i - 1 << ".)";
            return false;
        }
    }
    return true;
}

// C2Buffer -> Buffer
bool objcpy(Buffer* d, const C2Buffer& s,
        BufferPoolSender* bufferPoolSender,
        std::list<BaseBlock>* baseBlocks,
        std::map<const void*, uint32_t>* baseBlockIndices) {
    if (!createParamsBlob(&d->info, s.info())) {
        LOG(ERROR) << "Invalid C2Buffer::info.";
        return false;
    }
    if (!objcpy(d, s.data(), bufferPoolSender, baseBlocks, baseBlockIndices)) {
        LOG(ERROR) << "Invalid C2Buffer::data.";
        return false;
    }
    return true;
}

// C2InfoBuffer -> InfoBuffer
bool objcpy(InfoBuffer* d, const C2InfoBuffer& s,
        BufferPoolSender* bufferPoolSender,
        std::list<BaseBlock>* baseBlocks,
        std::map<const void*, uint32_t>* baseBlockIndices) {
    // TODO: C2InfoBuffer is not implemented.
    (void)d;
    (void)s;
    (void)bufferPoolSender;
    (void)baseBlocks;
    (void)baseBlockIndices;
    LOG(INFO) << "InfoBuffer not implemented.";
    return true;
}

// C2FrameData -> FrameData
bool objcpy(FrameData* d, const C2FrameData& s,
        BufferPoolSender* bufferPoolSender,
        std::list<BaseBlock>* baseBlocks,
        std::map<const void*, uint32_t>* baseBlockIndices) {
    d->flags = static_cast<hidl_bitfield<FrameData::Flags>>(s.flags);
    if (!objcpy(&d->ordinal, s.ordinal)) {
        LOG(ERROR) << "Invalid C2FrameData::ordinal.";
        return false;
    }

    d->buffers.resize(s.buffers.size());
    size_t i = 0;
    for (const std::shared_ptr<C2Buffer>& sBuffer : s.buffers) {
        Buffer& dBuffer = d->buffers[i++];
        if (!sBuffer) {
            // A null (pointer to) C2Buffer corresponds to a Buffer with empty
            // info and blocks.
            dBuffer.info.resize(0);
            dBuffer.blocks.resize(0);
            continue;
        }
        if (!objcpy(
                &dBuffer, *sBuffer,
                bufferPoolSender, baseBlocks, baseBlockIndices)) {
            LOG(ERROR) << "Invalid C2FrameData::buffers["
                       << i - 1 << "].";
            return false;
        }
    }

    if (!createParamsBlob(&d->configUpdate, s.configUpdate)) {
        LOG(ERROR) << "Invalid C2FrameData::configUpdate.";
        return false;
    }

    d->infoBuffers.resize(s.infoBuffers.size());
    i = 0;
    for (const std::shared_ptr<C2InfoBuffer>& sInfoBuffer : s.infoBuffers) {
        InfoBuffer& dInfoBuffer = d->infoBuffers[i++];
        if (!sInfoBuffer) {
            LOG(ERROR) << "Null C2FrameData::infoBuffers["
                       << i - 1 << "].";
            return false;
        }
        if (!objcpy(&dInfoBuffer, *sInfoBuffer,
                bufferPoolSender, baseBlocks, baseBlockIndices)) {
            LOG(ERROR) << "Invalid C2FrameData::infoBuffers["
                       << i - 1 << "].";
            return false;
        }
    }

    return true;
}

} // unnamed namespace

// DefaultBufferPoolSender's implementation

DefaultBufferPoolSender::DefaultBufferPoolSender(
        const sp<IClientManager>& receiverManager,
        std::chrono::steady_clock::duration refreshInterval)
    : mReceiverManager(receiverManager),
      mSourceConnectionId(0),
      mLastSent(std::chrono::steady_clock::now()),
      mRefreshInterval(refreshInterval) {
}

void DefaultBufferPoolSender::setReceiver(
        const sp<IClientManager>& receiverManager,
        std::chrono::steady_clock::duration refreshInterval) {
    std::lock_guard<std::mutex> lock(mMutex);
    if (mReceiverManager != receiverManager) {
        mReceiverManager = receiverManager;
    }
    mRefreshInterval = refreshInterval;
}

ResultStatus DefaultBufferPoolSender::send(
        const std::shared_ptr<BufferPoolData>& bpData,
        BufferStatusMessage* bpMessage) {
    if (!mReceiverManager) {
        LOG(ERROR) << "No access to receiver's BufferPool.";
        return ResultStatus::NOT_FOUND;
    }
    ResultStatus rs;
    std::lock_guard<std::mutex> lock(mMutex);
    if (!mSenderManager) {
        mSenderManager = ClientManager::getInstance();
        if (!mSenderManager) {
            LOG(ERROR) << "Failed to retrieve local BufferPool ClientManager.";
            return ResultStatus::CRITICAL_ERROR;
        }
    }
    int64_t connectionId = bpData->mConnectionId;
    std::chrono::steady_clock::time_point now =
            std::chrono::steady_clock::now();
    std::chrono::steady_clock::duration interval = now - mLastSent;
    if (mSourceConnectionId == 0 ||
            mSourceConnectionId != connectionId ||
            interval > mRefreshInterval) {
        // Initialize the bufferpool connection.
        mSourceConnectionId = connectionId;
        if (mSourceConnectionId == 0) {
            return ResultStatus::CRITICAL_ERROR;
        }

        int64_t receiverConnectionId;
        rs = mSenderManager->registerSender(mReceiverManager,
                                            connectionId,
                                            &receiverConnectionId);
        if ((rs != ResultStatus::OK) && (rs != ResultStatus::ALREADY_EXISTS)) {
            LOG(WARNING) << "registerSender -- returned error: "
                         << static_cast<int32_t>(rs)
                         << ".";
            return rs;
        } else {
            mReceiverConnectionId = receiverConnectionId;
        }
    }

    uint64_t transactionId;
    int64_t timestampUs;
    rs = mSenderManager->postSend(
            mReceiverConnectionId, bpData, &transactionId, &timestampUs);
    if (rs != ResultStatus::OK) {
        LOG(ERROR) << "ClientManager::postSend -- returned error: "
                   << static_cast<int32_t>(rs)
                   << ".";
        return rs;
    }
    if (!bpMessage) {
        LOG(ERROR) << "Null output parameter for BufferStatusMessage.";
        return ResultStatus::CRITICAL_ERROR;
    }
    bpMessage->connectionId = mReceiverConnectionId;
    bpMessage->bufferId = bpData->mId;
    bpMessage->transactionId = transactionId;
    bpMessage->timestampUs = timestampUs;
    mLastSent = now;
    return rs;
}

// std::list<std::unique_ptr<C2Work>> -> WorkBundle
bool objcpy(
        WorkBundle* d,
        const std::list<std::unique_ptr<C2Work>>& s,
        BufferPoolSender* bufferPoolSender) {
    // baseBlocks holds a list of BaseBlock objects that Blocks can refer to.
    std::list<BaseBlock> baseBlocks;

    // baseBlockIndices maps a raw pointer to native_handle_t or BufferPoolData
    // inside baseBlocks to the corresponding index into baseBlocks. The keys
    // (pointers) are used to identify blocks that have the same "base block" in
    // s, a list of C2Work objects. Because baseBlocks will be copied into a
    // hidl_vec eventually, the values of baseBlockIndices are zero-based
    // integer indices instead of list iterators.
    //
    // Note that the pointers can be raw because baseBlockIndices has a shorter
    // lifespan than all of base blocks.
    std::map<const void*, uint32_t> baseBlockIndices;

    d->works.resize(s.size());
    size_t i = 0;
    for (const std::unique_ptr<C2Work>& sWork : s) {
        Work &dWork = d->works[i++];
        if (!sWork) {
            LOG(WARNING) << "Null C2Work encountered.";
            continue;
        }

        // chain info is not in use currently.

        // input
        if (!objcpy(&dWork.input, sWork->input,
                bufferPoolSender, &baseBlocks, &baseBlockIndices)) {
            LOG(ERROR) << "Invalid C2Work::input.";
            return false;
        }

        // worklets
        if (sWork->worklets.size() == 0) {
            LOG(DEBUG) << "Work with no worklets.";
        } else {
            // Parcel the worklets.
            hidl_vec<Worklet> &dWorklets = dWork.worklets;
            dWorklets.resize(sWork->worklets.size());
            size_t j = 0;
            for (const std::unique_ptr<C2Worklet>& sWorklet : sWork->worklets)
            {
                if (!sWorklet) {
                    LOG(WARNING) << "Null C2Work::worklets["
                                 << j << "].";
                    continue;
                }
                Worklet &dWorklet = dWorklets[j++];

                // component id
                dWorklet.componentId = static_cast<uint32_t>(
                        sWorklet->component);

                // tunings
                if (!createParamsBlob(&dWorklet.tunings, sWorklet->tunings)) {
                    LOG(ERROR) << "Invalid C2Work::worklets["
                               << j - 1 << "]->tunings.";
                    return false;
                }

                // failures
                dWorklet.failures.resize(sWorklet->failures.size());
                size_t k = 0;
                for (const std::unique_ptr<C2SettingResult>& sFailure :
                        sWorklet->failures) {
                    if (!sFailure) {
                        LOG(WARNING) << "Null C2Work::worklets["
                                     << j - 1 << "]->failures["
                                     << k << "].";
                        continue;
                    }
                    if (!objcpy(&dWorklet.failures[k++], *sFailure)) {
                        LOG(ERROR) << "Invalid C2Work::worklets["
                                   << j - 1 << "]->failures["
                                   << k - 1 << "].";
                        return false;
                    }
                }

                // output
                if (!objcpy(&dWorklet.output, sWorklet->output,
                        bufferPoolSender, &baseBlocks, &baseBlockIndices)) {
                    LOG(ERROR) << "Invalid C2Work::worklets["
                               << j - 1 << "]->output.";
                    return false;
                }
            }
        }

        // worklets processed
        dWork.workletsProcessed = sWork->workletsProcessed;

        // result
        dWork.result = static_cast<Status>(sWork->result);
    }

    // Copy std::list<BaseBlock> to hidl_vec<BaseBlock>.
    {
        d->baseBlocks.resize(baseBlocks.size());
        size_t i = 0;
        for (const BaseBlock& baseBlock : baseBlocks) {
            d->baseBlocks[i++] = baseBlock;
        }
    }

    return true;
}

namespace /* unnamed */ {

struct C2BaseBlock {
    enum type_t {
        LINEAR,
        GRAPHIC,
    };
    type_t type;
    std::shared_ptr<C2LinearBlock> linear;
    std::shared_ptr<C2GraphicBlock> graphic;
};

// hidl_handle -> C2Fence
// Note: File descriptors are not duplicated. The original file descriptor must
// not be closed before the transaction is complete.
bool objcpy(C2Fence* d, const hidl_handle& s) {
    // TODO: Implement.
    (void)s;
    *d = C2Fence();
    return true;
}

// C2LinearBlock, vector<C2Param*>, C2Fence -> C2Buffer
bool createLinearBuffer(
        std::shared_ptr<C2Buffer>* buffer,
        const std::shared_ptr<C2LinearBlock>& block,
        const std::vector<C2Param*>& meta,
        const C2Fence& fence) {
    // Check the block meta. It should have exactly 1 C2Info:
    // C2Hidl_RangeInfo.
    if ((meta.size() != 1) || !meta[0]) {
        LOG(ERROR) << "Invalid C2LinearBlock::meta.";
        return false;
    }
    if (meta[0]->size() != sizeof(C2Hidl_RangeInfo)) {
        LOG(ERROR) << "Invalid range info in C2LinearBlock.";
        return false;
    }
    C2Hidl_RangeInfo *rangeInfo =
            reinterpret_cast<C2Hidl_RangeInfo*>(meta[0]);

    // Create C2Buffer from C2LinearBlock.
    *buffer = C2Buffer::CreateLinearBuffer(block->share(
            rangeInfo->offset, rangeInfo->length,
            fence));
    if (!(*buffer)) {
        LOG(ERROR) << "CreateLinearBuffer failed.";
        return false;
    }
    return true;
}

// C2GraphicBlock, vector<C2Param*>, C2Fence -> C2Buffer
bool createGraphicBuffer(
        std::shared_ptr<C2Buffer>* buffer,
        const std::shared_ptr<C2GraphicBlock>& block,
        const std::vector<C2Param*>& meta,
        const C2Fence& fence) {
    // Check the block meta. It should have exactly 1 C2Info:
    // C2Hidl_RectInfo.
    if ((meta.size() != 1) || !meta[0]) {
        LOG(ERROR) << "Invalid C2GraphicBlock::meta.";
        return false;
    }
    if (meta[0]->size() != sizeof(C2Hidl_RectInfo)) {
        LOG(ERROR) << "Invalid rect info in C2GraphicBlock.";
        return false;
    }
    C2Hidl_RectInfo *rectInfo =
            reinterpret_cast<C2Hidl_RectInfo*>(meta[0]);

    // Create C2Buffer from C2GraphicBlock.
    *buffer = C2Buffer::CreateGraphicBuffer(block->share(
            C2Rect(rectInfo->width, rectInfo->height).
            at(rectInfo->left, rectInfo->top),
            fence));
    if (!(*buffer)) {
        LOG(ERROR) << "CreateGraphicBuffer failed.";
        return false;
    }
    return true;
}

// Buffer -> C2Buffer
// Note: The native handles will be cloned.
bool objcpy(std::shared_ptr<C2Buffer>* d, const Buffer& s,
        const std::vector<C2BaseBlock>& baseBlocks) {
    *d = nullptr;

    // Currently, a non-null C2Buffer must contain exactly 1 block.
    if (s.blocks.size() == 0) {
        return true;
    } else if (s.blocks.size() != 1) {
        LOG(ERROR) << "Invalid Buffer: "
                      "Currently, a C2Buffer must contain exactly 1 block.";
        return false;
    }

    const Block &sBlock = s.blocks[0];
    if (sBlock.index >= baseBlocks.size()) {
        LOG(ERROR) << "Invalid Buffer::blocks[0].index: "
                      "Array index out of range.";
        return false;
    }
    const C2BaseBlock &baseBlock = baseBlocks[sBlock.index];

    // Parse meta.
    std::vector<C2Param*> sBlockMeta;
    if (!parseParamsBlob(&sBlockMeta, sBlock.meta)) {
        LOG(ERROR) << "Invalid Buffer::blocks[0].meta.";
        return false;
    }

    // Copy fence.
    C2Fence dFence;
    if (!objcpy(&dFence, sBlock.fence)) {
        LOG(ERROR) << "Invalid Buffer::blocks[0].fence.";
        return false;
    }

    // Construct a block.
    switch (baseBlock.type) {
    case C2BaseBlock::LINEAR:
        if (!createLinearBuffer(d, baseBlock.linear, sBlockMeta, dFence)) {
            LOG(ERROR) << "Invalid C2BaseBlock::linear.";
            return false;
        }
        break;
    case C2BaseBlock::GRAPHIC:
        if (!createGraphicBuffer(d, baseBlock.graphic, sBlockMeta, dFence)) {
            LOG(ERROR) << "Invalid C2BaseBlock::graphic.";
            return false;
        }
        break;
    default:
        LOG(ERROR) << "Invalid C2BaseBlock::type.";
        return false;
    }

    // Parse info
    std::vector<C2Param*> params;
    if (!parseParamsBlob(&params, s.info)) {
        LOG(ERROR) << "Invalid Buffer::info.";
        return false;
    }
    for (C2Param* param : params) {
        if (param == nullptr) {
            LOG(ERROR) << "Null param in Buffer::info.";
            return false;
        }
        std::shared_ptr<C2Param> c2param{
                C2Param::Copy(*param).release()};
        if (!c2param) {
            LOG(ERROR) << "Invalid param in Buffer::info.";
            return false;
        }
        c2_status_t status =
                (*d)->setInfo(std::static_pointer_cast<C2Info>(c2param));
        if (status != C2_OK) {
            LOG(ERROR) << "C2Buffer::setInfo failed.";
            return false;
        }
    }

    return true;
}

// FrameData -> C2FrameData
bool objcpy(C2FrameData* d, const FrameData& s,
        const std::vector<C2BaseBlock>& baseBlocks) {
    d->flags = static_cast<C2FrameData::flags_t>(s.flags);
    if (!objcpy(&d->ordinal, s.ordinal)) {
        LOG(ERROR) << "Invalid FrameData::ordinal.";
        return false;
    }
    d->buffers.clear();
    d->buffers.reserve(s.buffers.size());
    for (const Buffer& sBuffer : s.buffers) {
        std::shared_ptr<C2Buffer> dBuffer;
        if (!objcpy(&dBuffer, sBuffer, baseBlocks)) {
            LOG(ERROR) << "Invalid FrameData::buffers.";
            return false;
        }
        d->buffers.emplace_back(dBuffer);
    }

    std::vector<C2Param*> params;
    if (!parseParamsBlob(&params, s.configUpdate)) {
        LOG(ERROR) << "Invalid FrameData::configUpdate.";
        return false;
    }
    d->configUpdate.clear();
    for (C2Param* param : params) {
        d->configUpdate.emplace_back(C2Param::Copy(*param));
        if (!d->configUpdate.back()) {
            LOG(ERROR) << "Unexpected error while parsing "
                          "FrameData::configUpdate.";
            return false;
        }
    }

    // TODO: Implement this once C2InfoBuffer has constructors.
    d->infoBuffers.clear();
    return true;
}

// BaseBlock -> C2BaseBlock
bool objcpy(C2BaseBlock* d, const BaseBlock& s) {
    switch (s.getDiscriminator()) {
    case BaseBlock::hidl_discriminator::nativeBlock: {
            native_handle_t* sHandle =
                    native_handle_clone(s.nativeBlock());
            if (sHandle == nullptr) {
                LOG(ERROR) << "Null BaseBlock::nativeBlock.";
                return false;
            }
            const C2Handle *sC2Handle =
                    reinterpret_cast<const C2Handle*>(sHandle);

            d->linear = _C2BlockFactory::CreateLinearBlock(sC2Handle);
            if (d->linear) {
                d->type = C2BaseBlock::LINEAR;
                return true;
            }

            d->graphic = _C2BlockFactory::CreateGraphicBlock(sC2Handle);
            if (d->graphic) {
                d->type = C2BaseBlock::GRAPHIC;
                return true;
            }

            LOG(ERROR) << "Unknown handle type in BaseBlock::nativeBlock.";
            if (sHandle) {
                native_handle_close(sHandle);
                native_handle_delete(sHandle);
            }
            return false;
        }
    case BaseBlock::hidl_discriminator::pooledBlock: {
            const BufferStatusMessage &bpMessage =
                    s.pooledBlock();
            sp<ClientManager> bp = ClientManager::getInstance();
            std::shared_ptr<BufferPoolData> bpData;
            native_handle_t *cHandle;
            ResultStatus bpStatus = bp->receive(
                    bpMessage.connectionId,
                    bpMessage.transactionId,
                    bpMessage.bufferId,
                    bpMessage.timestampUs,
                    &cHandle,
                    &bpData);
            if (bpStatus != ResultStatus::OK) {
                LOG(ERROR) << "Failed to receive buffer from bufferpool -- "
                           << "resultStatus = " << underlying_value(bpStatus)
                           << ".";
                return false;
            } else if (!bpData) {
                LOG(ERROR) << "No data in bufferpool transaction.";
                return false;
            }

            d->linear = _C2BlockFactory::CreateLinearBlock(cHandle, bpData);
            if (d->linear) {
                d->type = C2BaseBlock::LINEAR;
                return true;
            }

            d->graphic = _C2BlockFactory::CreateGraphicBlock(cHandle, bpData);
            if (d->graphic) {
                d->type = C2BaseBlock::GRAPHIC;
                return true;
            }

            LOG(ERROR) << "Unknown handle type in BaseBlock::pooledBlock.";
            return false;
        }
    default:
        LOG(ERROR) << "Unrecognized BaseBlock's discriminator with "
                   << "underlying value "
                   << underlying_value(s.getDiscriminator()) << ".";
        return false;
    }
}

} // unnamed namespace

// WorkBundle -> std::list<std::unique_ptr<C2Work>>
bool objcpy(std::list<std::unique_ptr<C2Work>>* d, const WorkBundle& s) {
    // Convert BaseBlocks to C2BaseBlocks.
    std::vector<C2BaseBlock> dBaseBlocks(s.baseBlocks.size());
    for (size_t i = 0; i < s.baseBlocks.size(); ++i) {
        if (!objcpy(&dBaseBlocks[i], s.baseBlocks[i])) {
            LOG(ERROR) << "Invalid WorkBundle::baseBlocks["
                       << i << "].";
            return false;
        }
    }

    d->clear();
    for (const Work& sWork : s.works) {
        d->emplace_back(std::make_unique<C2Work>());
        C2Work& dWork = *d->back();

        // chain info is not in use currently.

        // input
        if (!objcpy(&dWork.input, sWork.input, dBaseBlocks)) {
            LOG(ERROR) << "Invalid Work::input.";
            return false;
        }

        // worklet(s)
        dWork.worklets.clear();
        for (const Worklet& sWorklet : sWork.worklets) {
            std::unique_ptr<C2Worklet> dWorklet = std::make_unique<C2Worklet>();

            // component id
            dWorklet->component = static_cast<c2_node_id_t>(
                    sWorklet.componentId);

            // tunings
            if (!copyParamsFromBlob(&dWorklet->tunings, sWorklet.tunings)) {
                LOG(ERROR) << "Invalid Worklet::tunings";
                return false;
            }

            // failures
            dWorklet->failures.clear();
            dWorklet->failures.reserve(sWorklet.failures.size());
            for (const SettingResult& sFailure : sWorklet.failures) {
                std::unique_ptr<C2SettingResult> dFailure;
                if (!objcpy(&dFailure, sFailure)) {
                    LOG(ERROR) << "Invalid Worklet::failures.";
                    return false;
                }
                dWorklet->failures.emplace_back(std::move(dFailure));
            }

            // output
            if (!objcpy(&dWorklet->output, sWorklet.output, dBaseBlocks)) {
                LOG(ERROR) << "Invalid Worklet::output.";
                return false;
            }

            dWork.worklets.emplace_back(std::move(dWorklet));
        }

        // workletsProcessed
        dWork.workletsProcessed = sWork.workletsProcessed;

        // result
        dWork.result = static_cast<c2_status_t>(sWork.result);
    }

    return true;
}

constexpr size_t PARAMS_ALIGNMENT = 8;  // 64-bit alignment
static_assert(PARAMS_ALIGNMENT % alignof(C2Param) == 0, "C2Param alignment mismatch");
static_assert(PARAMS_ALIGNMENT % alignof(C2Info) == 0, "C2Param alignment mismatch");
static_assert(PARAMS_ALIGNMENT % alignof(C2Tuning) == 0, "C2Param alignment mismatch");

// Params -> std::vector<C2Param*>
bool parseParamsBlob(std::vector<C2Param*> *params, const hidl_vec<uint8_t> &blob) {
    // assuming blob is const here
    size_t size = blob.size();
    size_t ix = 0;
    const uint8_t *data = blob.data();
    C2Param *p = nullptr;

    do {
        p = C2ParamUtils::ParseFirst(data + ix, size - ix);
        if (p) {
            params->emplace_back(p);
            ix += p->size();
            ix = align(ix, PARAMS_ALIGNMENT);
        }
    } while (p);

    if (ix != size) {
        LOG(ERROR) << "parseParamsBlob -- inconsistent sizes.";
        return false;
    }
    return true;
}

namespace /* unnamed */ {

/**
 * Concatenates a list of C2Params into a params blob. T is a container type
 * whose member type is compatible with C2Param*.
 *
 * \param[out] blob target blob
 * \param[in] params parameters to concatenate
 * \retval C2_OK if the blob was successfully created
 * \retval C2_BAD_VALUE if the blob was not successful created (this only
 *         happens if the parameters were not const)
 */
template <typename T>
bool _createParamsBlob(hidl_vec<uint8_t> *blob, const T &params) {
    // assuming the parameter values are const
    size_t size = 0;
    for (const auto &p : params) {
        if (!p) {
            continue;
        }
        size += p->size();
        size = align(size, PARAMS_ALIGNMENT);
    }
    blob->resize(size);
    size_t ix = 0;
    for (const auto &p : params) {
        if (!p) {
            continue;
        }
        // NEVER overwrite even if param values (e.g. size) changed
        size_t paramSize = std::min(p->size(), size - ix);
        std::copy(
                reinterpret_cast<const uint8_t*>(&*p),
                reinterpret_cast<const uint8_t*>(&*p) + paramSize,
                &(*blob)[ix]);
        ix += paramSize;
        ix = align(ix, PARAMS_ALIGNMENT);
    }
    blob->resize(ix);
    if (ix != size) {
        LOG(ERROR) << "createParamsBlob -- inconsistent sizes.";
        return false;
    }
    return true;
}

/**
 * Parses a params blob and create a vector of new T objects that contain copies
 * of the params in the blob. T is C2Param or its compatible derived class.
 *
 * \param[out] params the resulting vector
 * \param[in] blob parameter blob to parse
 * \retval C2_OK if the full blob was parsed and params was constructed
 * \retval C2_BAD_VALUE otherwise
 */
template <typename T>
bool _copyParamsFromBlob(
        std::vector<std::unique_ptr<T>>* params,
        Params blob) {

    std::vector<C2Param*> paramPointers;
    if (!parseParamsBlob(&paramPointers, blob)) {
        LOG(ERROR) << "copyParamsFromBlob -- failed to parse.";
        return false;
    }

    params->resize(paramPointers.size());
    size_t i = 0;
    for (C2Param* const& paramPointer : paramPointers) {
        if (!paramPointer) {
            LOG(ERROR) << "copyParamsFromBlob -- null paramPointer.";
            return false;
        }
        (*params)[i++].reset(reinterpret_cast<T*>(
                C2Param::Copy(*paramPointer).release()));
    }
    return true;
}

} // unnamed namespace

// std::vector<const C2Param*> -> Params
bool createParamsBlob(
        hidl_vec<uint8_t> *blob,
        const std::vector<const C2Param*> &params) {
    return _createParamsBlob(blob, params);
}

// std::vector<C2Param*> -> Params
bool createParamsBlob(
        hidl_vec<uint8_t> *blob,
        const std::vector<C2Param*> &params) {
    return _createParamsBlob(blob, params);
}

// std::vector<std::unique_ptr<C2Param>> -> Params
bool createParamsBlob(
        hidl_vec<uint8_t> *blob,
        const std::vector<std::unique_ptr<C2Param>> &params) {
    return _createParamsBlob(blob, params);
}

// std::vector<std::unique_ptr<C2Tuning>> -> Params
bool createParamsBlob(
        hidl_vec<uint8_t> *blob,
        const std::vector<std::unique_ptr<C2Tuning>> &params) {
    return _createParamsBlob(blob, params);
}

// std::vector<std::shared_ptr<const C2Info>> -> Params
bool createParamsBlob(
        hidl_vec<uint8_t> *blob,
        const std::vector<std::shared_ptr<const C2Info>> &params) {
    return _createParamsBlob(blob, params);
}

// Params -> std::vector<std::unique_ptr<C2Param>>
bool copyParamsFromBlob(
        std::vector<std::unique_ptr<C2Param>>* params,
        Params blob) {
    return _copyParamsFromBlob(params, blob);
}

// Params -> std::vector<std::unique_ptr<C2Tuning>>
bool copyParamsFromBlob(
        std::vector<std::unique_ptr<C2Tuning>>* params,
        Params blob) {
    return _copyParamsFromBlob(params, blob);
}

// Params -> update std::vector<std::unique_ptr<C2Param>>
bool updateParamsFromBlob(
        const std::vector<C2Param*>& params,
        const Params& blob) {
    std::unordered_map<uint32_t, C2Param*> index2param;
    for (C2Param* const& param : params) {
        if (!param) {
            LOG(ERROR) << "updateParamsFromBlob -- null output param.";
            return false;
        }
        if (index2param.find(param->index()) == index2param.end()) {
            index2param.emplace(param->index(), param);
        }
    }

    std::vector<C2Param*> paramPointers;
    if (!parseParamsBlob(&paramPointers, blob)) {
        LOG(ERROR) << "updateParamsFromBlob -- failed to parse.";
        return false;
    }

    for (C2Param* const& paramPointer : paramPointers) {
        if (!paramPointer) {
            LOG(ERROR) << "updateParamsFromBlob -- null input param.";
            return false;
        }
        decltype(index2param)::iterator i = index2param.find(
                paramPointer->index());
        if (i == index2param.end()) {
            LOG(DEBUG) << "updateParamsFromBlob -- index "
                       << paramPointer->index() << " not found. Skipping...";
            continue;
        }
        if (!i->second->updateFrom(*paramPointer)) {
            LOG(ERROR) << "updateParamsFromBlob -- size mismatch: "
                       << params.size() << " vs " << paramPointer->size()
                       << " (index = " << i->first << ").";
            return false;
        }
    }
    return true;
}

// Convert BufferPool ResultStatus to c2_status_t.
c2_status_t toC2Status(ResultStatus rs) {
    switch (rs) {
    case ResultStatus::OK:
        return C2_OK;
    case ResultStatus::NO_MEMORY:
        return C2_NO_MEMORY;
    case ResultStatus::ALREADY_EXISTS:
        return C2_DUPLICATE;
    case ResultStatus::NOT_FOUND:
        return C2_NOT_FOUND;
    case ResultStatus::CRITICAL_ERROR:
        return C2_CORRUPTED;
    default:
        LOG(WARNING) << "Unrecognized BufferPool ResultStatus: "
                     << static_cast<int32_t>(rs) << ".";
        return C2_CORRUPTED;
    }
}

namespace /* unnamed */ {

// Create a GraphicBuffer object from a graphic block.
sp<GraphicBuffer> createGraphicBuffer(const C2ConstGraphicBlock& block) {
    uint32_t width;
    uint32_t height;
    uint32_t format;
    uint64_t usage;
    uint32_t stride;
    uint32_t generation;
    uint64_t bqId;
    int32_t bqSlot;
    _UnwrapNativeCodec2GrallocMetadata(
            block.handle(), &width, &height, &format, &usage,
            &stride, &generation, &bqId, reinterpret_cast<uint32_t*>(&bqSlot));
    native_handle_t *grallocHandle =
            UnwrapNativeCodec2GrallocHandle(block.handle());
    sp<GraphicBuffer> graphicBuffer =
            new GraphicBuffer(grallocHandle,
                              GraphicBuffer::CLONE_HANDLE,
                              width, height, format,
                              1, usage, stride);
    native_handle_delete(grallocHandle);
    return graphicBuffer;
}

template <typename BlockProcessor>
void forEachBlock(C2FrameData& frameData,
                  BlockProcessor process) {
    for (const std::shared_ptr<C2Buffer>& buffer : frameData.buffers) {
        if (buffer) {
            for (const C2ConstGraphicBlock& block :
                    buffer->data().graphicBlocks()) {
                process(block);
            }
        }
    }
}

template <typename BlockProcessor>
void forEachBlock(const std::list<std::unique_ptr<C2Work>>& workList,
                  BlockProcessor process,
                  bool processInput, bool processOutput) {
    for (const std::unique_ptr<C2Work>& work : workList) {
        if (!work) {
            continue;
        }
        if (processInput) {
            forEachBlock(work->input, process);
        }
        if (processOutput) {
            for (const std::unique_ptr<C2Worklet>& worklet : work->worklets) {
                if (worklet) {
                    forEachBlock(worklet->output,
                                 process);
                }
            }
        }
    }
}

sp<HGraphicBufferProducer> getHgbp(const sp<IGraphicBufferProducer>& igbp) {
    sp<HGraphicBufferProducer> hgbp = igbp->getHalInterface();
    return hgbp ? hgbp :
            new TWGraphicBufferProducer<HGraphicBufferProducer>(igbp);
}

} // unnamed namespace

status_t attachToBufferQueue(const C2ConstGraphicBlock& block,
                             const sp<IGraphicBufferProducer>& igbp,
                             uint32_t generation,
                             int32_t* bqSlot) {
    if (!igbp) {
        LOG(WARNING) << "attachToBufferQueue -- null producer.";
        return NO_INIT;
    }

    sp<GraphicBuffer> graphicBuffer = createGraphicBuffer(block);
    graphicBuffer->setGenerationNumber(generation);

    LOG(VERBOSE) << "attachToBufferQueue -- attaching buffer:"
            << " block dimension " << block.width() << "x"
                                   << block.height()
            << ", graphicBuffer dimension " << graphicBuffer->getWidth() << "x"
                                           << graphicBuffer->getHeight()
            << std::hex << std::setfill('0')
            << ", format 0x" << std::setw(8) << graphicBuffer->getPixelFormat()
            << ", usage 0x" << std::setw(16) << graphicBuffer->getUsage()
            << std::dec << std::setfill(' ')
            << ", stride " << graphicBuffer->getStride()
            << ", generation " << graphicBuffer->getGenerationNumber();

    status_t result = igbp->attachBuffer(bqSlot, graphicBuffer);
    if (result != OK) {
        LOG(WARNING) << "attachToBufferQueue -- attachBuffer failed: "
                        "status = " << result << ".";
        return result;
    }
    LOG(VERBOSE) << "attachToBufferQueue -- attachBuffer returned slot #"
                 << *bqSlot << ".";
    return OK;
}

bool getBufferQueueAssignment(const C2ConstGraphicBlock& block,
                              uint32_t* generation,
                              uint64_t* bqId,
                              int32_t* bqSlot) {
    return _C2BlockFactory::GetBufferQueueData(
            _C2BlockFactory::GetGraphicBlockPoolData(block),
            generation, bqId, bqSlot);
}

bool yieldBufferQueueBlock(const C2ConstGraphicBlock& block) {
    std::shared_ptr<_C2BlockPoolData> data =
            _C2BlockFactory::GetGraphicBlockPoolData(block);
    if (data && _C2BlockFactory::GetBufferQueueData(data)) {
        _C2BlockFactory::YieldBlockToBufferQueue(data);
        return true;
    }
    return false;
}

void yieldBufferQueueBlocks(
        const std::list<std::unique_ptr<C2Work>>& workList,
        bool processInput, bool processOutput) {
    forEachBlock(workList, yieldBufferQueueBlock, processInput, processOutput);
}

bool holdBufferQueueBlock(const C2ConstGraphicBlock& block,
                            const sp<IGraphicBufferProducer>& igbp,
                            uint64_t bqId,
                            uint32_t generation) {
    std::shared_ptr<_C2BlockPoolData> data =
            _C2BlockFactory::GetGraphicBlockPoolData(block);
    if (!data) {
        return false;
    }

    uint32_t oldGeneration;
    uint64_t oldId;
    int32_t oldSlot;
    // If the block is not bufferqueue-based, do nothing.
    if (!_C2BlockFactory::GetBufferQueueData(
            data, &oldGeneration, &oldId, &oldSlot) ||
            (oldId == 0)) {
        return false;
    }

    // If the block's bqId is the same as the desired bqId, just hold.
    if ((oldId == bqId) && (oldGeneration == generation)) {
        LOG(VERBOSE) << "holdBufferQueueBlock -- import without attaching:"
                     << " bqId " << oldId
                     << ", bqSlot " << oldSlot
                     << ", generation " << generation
                     << ".";
        _C2BlockFactory::HoldBlockFromBufferQueue(data, getHgbp(igbp));
        return true;
    }

    // Otherwise, attach to the given igbp, which must not be null.
    if (!igbp) {
        return false;
    }

    int32_t bqSlot;
    status_t result = attachToBufferQueue(block, igbp, generation, &bqSlot);

    if (result != OK) {
        LOG(ERROR) << "holdBufferQueueBlock -- fail to attach:"
                   << " target bqId " << bqId
                   << ", generation " << generation
                   << ".";
        return false;
    }

    LOG(VERBOSE) << "holdBufferQueueBlock -- attached:"
                 << " bqId " << bqId
                 << ", bqSlot " << bqSlot
                 << ", generation " << generation
                 << ".";
    _C2BlockFactory::AssignBlockToBufferQueue(
            data, getHgbp(igbp), generation, bqId, bqSlot, true);
    return true;
}

void holdBufferQueueBlocks(const std::list<std::unique_ptr<C2Work>>& workList,
                           const sp<IGraphicBufferProducer>& igbp,
                           uint64_t bqId,
                           uint32_t generation,
                           bool forInput) {
    forEachBlock(workList,
                 std::bind(holdBufferQueueBlock,
                           std::placeholders::_1, igbp, bqId, generation),
                 forInput, !forInput);
}

}  // namespace utils
}  // namespace V1_0
}  // namespace c2
}  // namespace media
}  // namespace hardware
}  // namespace android

