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

#define LOG_TAG "Gralloc5"
#define ATRACE_TAG ATRACE_TAG_GRAPHICS

#include <ui/Gralloc5.h>

#include <aidl/android/hardware/graphics/allocator/AllocationError.h>
#include <aidlcommonsupport/NativeHandle.h>
#include <android/binder_manager.h>
#include <android/hardware/graphics/mapper/utils/IMapperMetadataTypes.h>
#include <android/llndk-versioning.h>
#include <binder/IPCThreadState.h>
#include <dlfcn.h>
#include <ui/FatVector.h>
#include <vndksupport/linker.h>

using namespace aidl::android::hardware::graphics::allocator;
using namespace aidl::android::hardware::graphics::common;
using namespace ::android::hardware::graphics::mapper;

using ADataspace = aidl::android::hardware::graphics::common::Dataspace;
using APixelFormat = aidl::android::hardware::graphics::common::PixelFormat;

namespace android {

static const auto kIAllocatorServiceName = IAllocator::descriptor + std::string("/default");
static const auto kIAllocatorMinimumVersion = 2;
constexpr const char* kStandardMetadataName =
        "android.hardware.graphics.common.StandardMetadataType";

// TODO(b/72323293, b/72703005): Remove these invalid bits from callers
static constexpr uint64_t kRemovedUsageBits = static_cast<uint64_t>((1 << 10) | (1 << 13));

typedef AIMapper_Error (*AIMapper_loadIMapperFn)(AIMapper *_Nullable *_Nonnull outImplementation);

struct Gralloc5 {
    std::shared_ptr<IAllocator> allocator;
    AIMapper *mapper = nullptr;
};

static std::shared_ptr<IAllocator> waitForAllocator() {
    if (__builtin_available(android 31, *)) {
        if (!AServiceManager_isDeclared(kIAllocatorServiceName.c_str())) {
            return nullptr;
        }
        auto allocator = IAllocator::fromBinder(
                ndk::SpAIBinder(AServiceManager_waitForService(kIAllocatorServiceName.c_str())));
        if (!allocator) {
            ALOGE("AIDL IAllocator declared but failed to get service");
            return nullptr;
        }

        int32_t version = 0;
        if (!allocator->getInterfaceVersion(&version).isOk()) {
            ALOGE("Failed to query interface version");
            return nullptr;
        }
        if (version < kIAllocatorMinimumVersion) {
            return nullptr;
        }
        return allocator;
    } else {
        // TODO: LOG_ALWAYS_FATAL("libui is not backwards compatible");
        return nullptr;
    }
}

static void *loadIMapperLibrary() {
    static void *imapperLibrary = []() -> void * {
        auto allocator = waitForAllocator();
        std::string mapperSuffix;
        auto status = allocator->getIMapperLibrarySuffix(&mapperSuffix);
        if (!status.isOk()) {
            ALOGE("Failed to get IMapper library suffix");
            return nullptr;
        }

        void* so = nullptr;
        if API_LEVEL_AT_LEAST (__ANDROID_API_V__, 202404) {
            so = AServiceManager_openDeclaredPassthroughHal("mapper", mapperSuffix.c_str(),
                                                            RTLD_LOCAL | RTLD_NOW);
        } else {
            std::string lib_name = "mapper." + mapperSuffix + ".so";
            so = android_load_sphal_library(lib_name.c_str(), RTLD_LOCAL | RTLD_NOW);
        }
        if (!so) {
            ALOGE("Failed to load mapper.%s.so", mapperSuffix.c_str());
        }
        return so;
    }();
    return imapperLibrary;
}

static const Gralloc5 &getInstance() {
    static Gralloc5 instance = []() {
        auto allocator = waitForAllocator();
        if (!allocator) {
            return Gralloc5{};
        }
        void *so = loadIMapperLibrary();
        if (!so) {
            return Gralloc5{};
        }
        auto loadIMapper = (AIMapper_loadIMapperFn)dlsym(so, "AIMapper_loadIMapper");
        AIMapper *mapper = nullptr;
        AIMapper_Error error = loadIMapper(&mapper);
        if (error != AIMAPPER_ERROR_NONE) {
            ALOGE("AIMapper_loadIMapper failed %d", error);
            return Gralloc5{};
        }
        return Gralloc5{std::move(allocator), mapper};
    }();
    return instance;
}

template <StandardMetadataType T>
static auto getStandardMetadata(AIMapper *mapper, buffer_handle_t bufferHandle)
        -> decltype(StandardMetadata<T>::value::decode(nullptr, 0)) {
    using Value = typename StandardMetadata<T>::value;
    // TODO: Tune for common-case better
    FatVector<uint8_t, 128> buffer;
    int32_t sizeRequired = mapper->v5.getStandardMetadata(bufferHandle, static_cast<int64_t>(T),
                                                          buffer.data(), buffer.size());
    if (sizeRequired < 0) {
        ALOGW_IF(-AIMAPPER_ERROR_UNSUPPORTED != sizeRequired,
                 "Unexpected error %d from valid getStandardMetadata call", -sizeRequired);
        return std::nullopt;
    }
    if ((size_t)sizeRequired > buffer.size()) {
        buffer.resize(sizeRequired);
        sizeRequired = mapper->v5.getStandardMetadata(bufferHandle, static_cast<int64_t>(T),
                                                      buffer.data(), buffer.size());
    }
    if (sizeRequired < 0 || (size_t)sizeRequired > buffer.size()) {
        ALOGW("getStandardMetadata failed, received %d with buffer size %zd", sizeRequired,
              buffer.size());
        // Generate a fail type
        return std::nullopt;
    }
    return Value::decode(buffer.data(), sizeRequired);
}

template <StandardMetadataType T>
static AIMapper_Error setStandardMetadata(AIMapper *mapper, buffer_handle_t bufferHandle,
                                          const typename StandardMetadata<T>::value_type &value) {
    using Value = typename StandardMetadata<T>::value;
    int32_t sizeRequired = Value::encode(value, nullptr, 0);
    if (sizeRequired < 0) {
        ALOGW("Failed to calculate required size");
        return static_cast<AIMapper_Error>(-sizeRequired);
    }
    FatVector<uint8_t, 128> buffer;
    buffer.resize(sizeRequired);
    sizeRequired = Value::encode(value, buffer.data(), buffer.size());
    if (sizeRequired < 0 || (size_t)sizeRequired > buffer.size()) {
        ALOGW("Failed to encode with calculated size %d; buffer size %zd", sizeRequired,
              buffer.size());
        return static_cast<AIMapper_Error>(-sizeRequired);
    }
    return mapper->v5.setStandardMetadata(bufferHandle, static_cast<int64_t>(T), buffer.data(),
                                          sizeRequired);
}

Gralloc5Allocator::Gralloc5Allocator(const Gralloc5Mapper &mapper) : mMapper(mapper) {
    mAllocator = getInstance().allocator;
}

bool Gralloc5Allocator::isLoaded() const {
    return mAllocator != nullptr;
}

static uint64_t getValidUsageBits() {
    static const uint64_t validUsageBits = []() -> uint64_t {
        uint64_t bits = 0;
        for (const auto bit : ndk::enum_range<BufferUsage>{}) {
            bits |= static_cast<int64_t>(bit);
        }
        return bits;
    }();
    return validUsageBits | kRemovedUsageBits;
}

static std::optional<BufferDescriptorInfo> makeDescriptor(std::string requestorName, uint32_t width,
                                                          uint32_t height, PixelFormat format,
                                                          uint32_t layerCount, uint64_t usage) {
    uint64_t validUsageBits = getValidUsageBits();
    if (usage & ~validUsageBits) {
        ALOGE("buffer descriptor contains invalid usage bits 0x%" PRIx64, usage & ~validUsageBits);
        return std::nullopt;
    }

    BufferDescriptorInfo descriptorInfo{
            .width = static_cast<int32_t>(width),
            .height = static_cast<int32_t>(height),
            .layerCount = static_cast<int32_t>(layerCount),
            .format = static_cast<::aidl::android::hardware::graphics::common::PixelFormat>(format),
            .usage = static_cast<BufferUsage>(usage),
    };
    auto nameLength = std::min(requestorName.length(), descriptorInfo.name.size() - 1);
    memcpy(descriptorInfo.name.data(), requestorName.data(), nameLength);
    requestorName.data()[nameLength] = 0;
    return descriptorInfo;
}

std::string Gralloc5Allocator::dumpDebugInfo(bool less) const {
    return mMapper.dumpBuffers(less);
}

status_t Gralloc5Allocator::allocate(std::string requestorName, uint32_t width, uint32_t height,
                                     android::PixelFormat format, uint32_t layerCount,
                                     uint64_t usage, uint32_t* outStride,
                                     buffer_handle_t* outBufferHandles, bool importBuffers) const {
    auto result = allocate(GraphicBufferAllocator::AllocationRequest{
            .importBuffer = importBuffers,
            .width = width,
            .height = height,
            .format = format,
            .layerCount = layerCount,
            .usage = usage,
            .requestorName = requestorName,
    });

    *outStride = result.stride;
    outBufferHandles[0] = result.handle;
    return result.status;
}

GraphicBufferAllocator::AllocationResult Gralloc5Allocator::allocate(
        const GraphicBufferAllocator::AllocationRequest& request) const {
    auto descriptorInfo = makeDescriptor(request.requestorName, request.width, request.height,
                                         request.format, request.layerCount, request.usage);
    if (!descriptorInfo) {
        return GraphicBufferAllocator::AllocationResult{BAD_VALUE};
    }

    descriptorInfo->additionalOptions.reserve(request.extras.size());
    for (const auto& option : request.extras) {
        ExtendableType type;
        type.name = option.name;
        type.value = option.value;
        descriptorInfo->additionalOptions.push_back(std::move(type));
    }

    AllocationResult result;
    auto status = mAllocator->allocate2(*descriptorInfo, 1, &result);
    if (!status.isOk()) {
        auto error = status.getExceptionCode();
        if (error == EX_SERVICE_SPECIFIC) {
            switch (static_cast<AllocationError>(status.getServiceSpecificError())) {
                case AllocationError::BAD_DESCRIPTOR:
                    error = BAD_VALUE;
                    break;
                case AllocationError::NO_RESOURCES:
                    error = NO_MEMORY;
                    break;
                default:
                    error = UNKNOWN_ERROR;
                    break;
            }
        }
        return GraphicBufferAllocator::AllocationResult{error};
    }

    GraphicBufferAllocator::AllocationResult ret{OK};
    if (request.importBuffer) {
        auto handle = makeFromAidl(result.buffers[0]);
        auto error = mMapper.importBuffer(handle, &ret.handle);
        native_handle_delete(handle);
        if (error != NO_ERROR) {
            return GraphicBufferAllocator::AllocationResult{error};
        }
    } else {
        ret.handle = dupFromAidl(result.buffers[0]);
        if (!ret.handle) {
            return GraphicBufferAllocator::AllocationResult{NO_MEMORY};
        }
    }

    ret.stride = result.stride;

    // Release all the resources held by AllocationResult (specifically any remaining FDs)
    result = {};
    // make sure the kernel driver sees BC_FREE_BUFFER and closes the fds now
    // TODO: Re-enable this at some point if it's necessary. We can't do it now because libui
    // is marked apex_available (b/214400477) and libbinder isn't (which of course is correct)
    // IPCThreadState::self()->flushCommands();

    return ret;
}

void Gralloc5Mapper::preload() {
    // TODO(b/261858155): Implement. We can't bounce off of IAllocator for this because zygote can't
    // use binder. So when an alternate strategy of retrieving the library prefix is available,
    // use that here.
}

Gralloc5Mapper::Gralloc5Mapper() {
    mMapper = getInstance().mapper;
}

bool Gralloc5Mapper::isLoaded() const {
    return mMapper != nullptr && mMapper->version >= AIMAPPER_VERSION_5;
}

static bool isStandardMetadata(AIMapper_MetadataType metadataType) {
    return strcmp(kStandardMetadataName, metadataType.name) == 0;
}

struct DumpBufferResult {
    uint64_t bufferId;
    std::string name;
    uint64_t width;
    uint64_t height;
    uint64_t layerCount;
    APixelFormat pixelFormatRequested;
    uint32_t pixelFormatFourCC;
    uint64_t pixelFormatModifier;
    BufferUsage usage;
    ADataspace dataspace;
    uint64_t allocationSize;
    uint64_t protectedContent;
    ExtendableType compression;
    ExtendableType interlaced;
    ExtendableType chromaSiting;
    std::vector<ui::PlaneLayout> planeLayouts;
};

#define DECODE_TO(name, output)                                                                 \
    case StandardMetadataType::name:                                                            \
        output = StandardMetadata<StandardMetadataType::name>::value ::decode(value, valueSize) \
                         .value();                                                              \
        break

static void dumpBufferCommon(DumpBufferResult* outResult, AIMapper_MetadataType metadataType,
                             const void* value, size_t valueSize) {
    if (!isStandardMetadata(metadataType)) {
        return;
    }
    StandardMetadataType type = (StandardMetadataType)metadataType.value;
    switch (type) {
        DECODE_TO(BUFFER_ID, outResult->bufferId);
        DECODE_TO(NAME, outResult->name);
        DECODE_TO(WIDTH, outResult->width);
        DECODE_TO(HEIGHT, outResult->height);
        DECODE_TO(LAYER_COUNT, outResult->layerCount);
        DECODE_TO(PIXEL_FORMAT_REQUESTED, outResult->pixelFormatRequested);
        DECODE_TO(PIXEL_FORMAT_FOURCC, outResult->pixelFormatFourCC);
        DECODE_TO(PIXEL_FORMAT_MODIFIER, outResult->pixelFormatModifier);
        DECODE_TO(USAGE, outResult->usage);
        DECODE_TO(DATASPACE, outResult->dataspace);
        DECODE_TO(ALLOCATION_SIZE, outResult->allocationSize);
        DECODE_TO(PROTECTED_CONTENT, outResult->protectedContent);
        DECODE_TO(COMPRESSION, outResult->compression);
        DECODE_TO(INTERLACED, outResult->interlaced);
        DECODE_TO(CHROMA_SITING, outResult->chromaSiting);
        DECODE_TO(PLANE_LAYOUTS, outResult->planeLayouts);
        default:
            break;
    }
}

#undef DECODE_TO

template <typename EnumT, typename = std::enable_if_t<std::is_enum<EnumT>{}>>
constexpr std::underlying_type_t<EnumT> to_underlying(EnumT e) noexcept {
    return static_cast<std::underlying_type_t<EnumT>>(e);
}

static void writeDumpToStream(const DumpBufferResult& bufferDump, std::ostream& outDump,
                              bool less) {
    double allocationSizeKiB = static_cast<double>(bufferDump.allocationSize) / 1024;

    outDump << "+ name:" << bufferDump.name << ", id:" << bufferDump.bufferId
            << ", size:" << std::fixed << allocationSizeKiB << "KiB, w/h:" << bufferDump.width
            << "x" << bufferDump.height << ", usage: 0x" << std::hex
            << to_underlying(bufferDump.usage) << std::dec
            << ", req fmt:" << to_underlying(bufferDump.pixelFormatRequested)
            << ", fourcc/mod:" << bufferDump.pixelFormatFourCC << "/"
            << bufferDump.pixelFormatModifier << ", dataspace: 0x" << std::hex
            << to_underlying(bufferDump.dataspace) << std::dec << ", compressed: ";

    if (less) {
        bool isCompressed = !gralloc4::isStandardCompression(bufferDump.compression) ||
                (gralloc4::getStandardCompressionValue(bufferDump.compression) !=
                 ui::Compression::NONE);
        outDump << std::boolalpha << isCompressed << "\n";
    } else {
        outDump << gralloc4::getCompressionName(bufferDump.compression) << "\n";
    }

    if (!less) {
        bool firstPlane = true;
        for (const auto& planeLayout : bufferDump.planeLayouts) {
            if (firstPlane) {
                firstPlane = false;
                outDump << "\tplanes: ";
            } else {
                outDump << "\t        ";
            }

            for (size_t i = 0; i < planeLayout.components.size(); i++) {
                const auto& planeLayoutComponent = planeLayout.components[i];
                outDump << gralloc4::getPlaneLayoutComponentTypeName(planeLayoutComponent.type);
                if (i < planeLayout.components.size() - 1) {
                    outDump << "/";
                } else {
                    outDump << ":\t";
                }
            }
            outDump << " w/h:" << planeLayout.widthInSamples << "x" << planeLayout.heightInSamples
                    << ", stride:" << planeLayout.strideInBytes
                    << " bytes, size:" << planeLayout.totalSizeInBytes;
            outDump << ", inc:" << planeLayout.sampleIncrementInBits
                    << " bits, subsampling w/h:" << planeLayout.horizontalSubsampling << "x"
                    << planeLayout.verticalSubsampling;
            outDump << "\n";
        }

        outDump << "\tlayer cnt: " << bufferDump.layerCount
                << ", protected content: " << bufferDump.protectedContent
                << ", interlaced: " << gralloc4::getInterlacedName(bufferDump.interlaced)
                << ", chroma siting:" << gralloc4::getChromaSitingName(bufferDump.chromaSiting)
                << "\n";
    }
}

std::string Gralloc5Mapper::dumpBuffer(buffer_handle_t bufferHandle, bool less) const {
    DumpBufferResult bufferInfo;
    AIMapper_DumpBufferCallback dumpBuffer = [](void* contextPtr,
                                                AIMapper_MetadataType metadataType,
                                                const void* _Nonnull value, size_t valueSize) {
        DumpBufferResult* context = reinterpret_cast<DumpBufferResult*>(contextPtr);
        dumpBufferCommon(context, metadataType, value, valueSize);
    };
    AIMapper_Error error = mMapper->v5.dumpBuffer(bufferHandle, dumpBuffer, &bufferInfo);
    if (error != AIMAPPER_ERROR_NONE) {
        ALOGE("Error dumping buffer: %d", error);
        return std::string{};
    }
    std::ostringstream stream;
    stream.precision(2);
    writeDumpToStream(bufferInfo, stream, less);
    return stream.str();
}

std::string Gralloc5Mapper::dumpBuffers(bool less) const {
    class DumpAllBuffersContext {
    private:
        bool mHasPending = false;
        DumpBufferResult mPending;
        std::vector<DumpBufferResult> mResults;

    public:
        DumpAllBuffersContext() { mResults.reserve(10); }

        void commit() {
            if (mHasPending) {
                mResults.push_back(mPending);
                mHasPending = false;
            }
        }

        DumpBufferResult* write() {
            mHasPending = true;
            return &mPending;
        }

        const std::vector<DumpBufferResult>& results() {
            commit();
            return mResults;
        }
    } context;

    AIMapper_BeginDumpBufferCallback beginCallback = [](void* contextPtr) {
        DumpAllBuffersContext* context = reinterpret_cast<DumpAllBuffersContext*>(contextPtr);
        context->commit();
    };

    AIMapper_DumpBufferCallback dumpBuffer = [](void* contextPtr,
                                                AIMapper_MetadataType metadataType,
                                                const void* _Nonnull value, size_t valueSize) {
        DumpAllBuffersContext* context = reinterpret_cast<DumpAllBuffersContext*>(contextPtr);
        dumpBufferCommon(context->write(), metadataType, value, valueSize);
    };

    AIMapper_Error error = mMapper->v5.dumpAllBuffers(beginCallback, dumpBuffer, &context);
    if (error != AIMAPPER_ERROR_NONE) {
        ALOGE("Error dumping buffers: %d", error);
        return std::string{};
    }
    uint64_t totalAllocationSize = 0;
    std::ostringstream stream;
    stream.precision(2);
    stream << "Imported gralloc buffers:\n";

    for (const auto& bufferDump : context.results()) {
        writeDumpToStream(bufferDump, stream, less);
        totalAllocationSize += bufferDump.allocationSize;
    }

    double totalAllocationSizeKiB = static_cast<double>(totalAllocationSize) / 1024;
    stream << "Total imported by gralloc: " << totalAllocationSizeKiB << "KiB\n";
    return stream.str();
}

status_t Gralloc5Mapper::importBuffer(const native_handle_t *rawHandle,
                                      buffer_handle_t *outBufferHandle) const {
    return mMapper->v5.importBuffer(rawHandle, outBufferHandle);
}

void Gralloc5Mapper::freeBuffer(buffer_handle_t bufferHandle) const {
    mMapper->v5.freeBuffer(bufferHandle);
}

status_t Gralloc5Mapper::validateBufferSize(buffer_handle_t bufferHandle, uint32_t width,
                                            uint32_t height, PixelFormat format,
                                            uint32_t layerCount, uint64_t usage,
                                            uint32_t stride) const {
    {
        auto value = getStandardMetadata<StandardMetadataType::WIDTH>(mMapper, bufferHandle);
        if (width != value) {
            ALOGW("Width didn't match, expected %d got %" PRId64, width, value.value_or(-1));
            return BAD_VALUE;
        }
    }
    {
        auto value = getStandardMetadata<StandardMetadataType::HEIGHT>(mMapper, bufferHandle);
        if (height != value) {
            ALOGW("Height didn't match, expected %d got %" PRId64, height, value.value_or(-1));
            return BAD_VALUE;
        }
    }
    {
        auto expected = static_cast<APixelFormat>(format);
        if (expected != APixelFormat::IMPLEMENTATION_DEFINED) {
            auto value =
                    getStandardMetadata<StandardMetadataType::PIXEL_FORMAT_REQUESTED>(mMapper,
                                                                                      bufferHandle);
            if (expected != value) {
                ALOGW("Format didn't match, expected %d got %s", format,
                      value.has_value() ? toString(*value).c_str() : "<null>");
                return BAD_VALUE;
            }
        }
    }
    {
        auto value = getStandardMetadata<StandardMetadataType::LAYER_COUNT>(mMapper, bufferHandle);
        if (layerCount != value) {
            ALOGW("Layer count didn't match, expected %d got %" PRId64, layerCount,
                  value.value_or(-1));
            return BAD_VALUE;
        }
    }
    // TODO: This can false-positive fail if the allocator adjusted the USAGE bits internally
    //       Investigate further & re-enable or remove, but for now ignoring usage should be OK
    (void)usage;
    // {
    //     auto value = getStandardMetadata<StandardMetadataType::USAGE>(mMapper, bufferHandle);
    //     if (static_cast<BufferUsage>(usage) != value) {
    //         ALOGW("Usage didn't match, expected %" PRIu64 " got %" PRId64, usage,
    //               static_cast<int64_t>(value.value_or(BufferUsage::CPU_READ_NEVER)));
    //         return BAD_VALUE;
    //     }
    // }
    {
        auto value = getStandardMetadata<StandardMetadataType::STRIDE>(mMapper, bufferHandle);
        if (stride != value) {
            ALOGW("Stride didn't match, expected %" PRIu32 " got %" PRId32, stride,
                  value.value_or(-1));
            return BAD_VALUE;
        }
    }
    return OK;
}

void Gralloc5Mapper::getTransportSize(buffer_handle_t bufferHandle, uint32_t *outNumFds,
                                      uint32_t *outNumInts) const {
    mMapper->v5.getTransportSize(bufferHandle, outNumFds, outNumInts);
}

status_t Gralloc5Mapper::lock(buffer_handle_t bufferHandle, uint64_t usage, const Rect &bounds,
                              int acquireFence, void **outData, int32_t *outBytesPerPixel,
                              int32_t *outBytesPerStride) const {
    if (outBytesPerPixel) *outBytesPerPixel = -1;
    if (outBytesPerStride) *outBytesPerStride = -1;

    auto status = mMapper->v5.lock(bufferHandle, usage, bounds, acquireFence, outData);

    ALOGW_IF(status != AIMAPPER_ERROR_NONE, "lock(%p, ...) failed: %d", bufferHandle, status);
    return static_cast<status_t>(status);
}

status_t Gralloc5Mapper::lock(buffer_handle_t bufferHandle, uint64_t usage, const Rect &bounds,
                              int acquireFence, android_ycbcr *outYcbcr) const {
    if (!outYcbcr) {
        return BAD_VALUE;
    }

    // TODO(b/262279301): Change the return type of ::unlock to unique_fd instead of int so that
    //  ignoring the return value "just works" instead
    auto unlock = [this](buffer_handle_t bufferHandle) {
        int fence = this->unlock(bufferHandle);
        if (fence != -1) {
            ::close(fence);
        }
    };

    std::vector<ui::PlaneLayout> planeLayouts;
    status_t error = getPlaneLayouts(bufferHandle, &planeLayouts);
    if (error != NO_ERROR) {
        return error;
    }

    void *data = nullptr;
    error = lock(bufferHandle, usage, bounds, acquireFence, &data, nullptr, nullptr);
    if (error != NO_ERROR) {
        return error;
    }

    android_ycbcr ycbcr;

    ycbcr.y = nullptr;
    ycbcr.cb = nullptr;
    ycbcr.cr = nullptr;
    ycbcr.ystride = 0;
    ycbcr.cstride = 0;
    ycbcr.chroma_step = 0;

    for (const auto &planeLayout : planeLayouts) {
        for (const auto &planeLayoutComponent : planeLayout.components) {
            if (!gralloc4::isStandardPlaneLayoutComponentType(planeLayoutComponent.type)) {
                continue;
            }

            uint8_t *tmpData = static_cast<uint8_t *>(data) + planeLayout.offsetInBytes;

            // Note that `offsetInBits` may not be a multiple of 8 for packed formats (e.g. P010)
            // but we still want to point to the start of the first byte.
            tmpData += (planeLayoutComponent.offsetInBits / 8);

            uint64_t sampleIncrementInBytes;

            auto type = static_cast<PlaneLayoutComponentType>(planeLayoutComponent.type.value);
            switch (type) {
                case PlaneLayoutComponentType::Y:
                    if ((ycbcr.y != nullptr) || (planeLayout.sampleIncrementInBits % 8 != 0)) {
                        unlock(bufferHandle);
                        return BAD_VALUE;
                    }
                    ycbcr.y = tmpData;
                    ycbcr.ystride = planeLayout.strideInBytes;
                    break;

                case PlaneLayoutComponentType::CB:
                case PlaneLayoutComponentType::CR:
                    if (planeLayout.sampleIncrementInBits % 8 != 0) {
                        unlock(bufferHandle);
                        return BAD_VALUE;
                    }

                    sampleIncrementInBytes = planeLayout.sampleIncrementInBits / 8;
                    if ((sampleIncrementInBytes != 1) && (sampleIncrementInBytes != 2) &&
                        (sampleIncrementInBytes != 4)) {
                        unlock(bufferHandle);
                        return BAD_VALUE;
                    }

                    if (ycbcr.cstride == 0 && ycbcr.chroma_step == 0) {
                        ycbcr.cstride = planeLayout.strideInBytes;
                        ycbcr.chroma_step = sampleIncrementInBytes;
                    } else {
                        if ((static_cast<int64_t>(ycbcr.cstride) != planeLayout.strideInBytes) ||
                            (ycbcr.chroma_step != sampleIncrementInBytes)) {
                            unlock(bufferHandle);
                            return BAD_VALUE;
                        }
                    }

                    if (type == PlaneLayoutComponentType::CB) {
                        if (ycbcr.cb != nullptr) {
                            unlock(bufferHandle);
                            return BAD_VALUE;
                        }
                        ycbcr.cb = tmpData;
                    } else {
                        if (ycbcr.cr != nullptr) {
                            unlock(bufferHandle);
                            return BAD_VALUE;
                        }
                        ycbcr.cr = tmpData;
                    }
                    break;
                default:
                    break;
            };
        }
    }

    *outYcbcr = ycbcr;
    return OK;
}

int Gralloc5Mapper::unlock(buffer_handle_t bufferHandle) const {
    int fence = -1;
    AIMapper_Error error = mMapper->v5.unlock(bufferHandle, &fence);
    if (error != AIMAPPER_ERROR_NONE) {
        ALOGW("unlock failed with error %d", error);
    }
    return fence;
}

status_t Gralloc5Mapper::isSupported(uint32_t width, uint32_t height, PixelFormat format,
                                     uint32_t layerCount, uint64_t usage,
                                     bool *outSupported) const {
    auto descriptorInfo = makeDescriptor("", width, height, format, layerCount, usage);
    if (!descriptorInfo) {
        *outSupported = false;
        return OK;
    }
    auto status = getInstance().allocator->isSupported(*descriptorInfo, outSupported);
    if (!status.isOk()) {
        ALOGW("IAllocator::isSupported error %d (%s)", status.getStatus(), status.getMessage());
        *outSupported = false;
    }
    return OK;
}

status_t Gralloc5Mapper::getBufferId(buffer_handle_t bufferHandle, uint64_t *outBufferId) const {
    auto value = getStandardMetadata<StandardMetadataType::BUFFER_ID>(mMapper, bufferHandle);
    if (value.has_value()) {
        *outBufferId = *value;
        return OK;
    }
    return UNKNOWN_TRANSACTION;
}

status_t Gralloc5Mapper::getName(buffer_handle_t bufferHandle, std::string *outName) const {
    auto value = getStandardMetadata<StandardMetadataType::NAME>(mMapper, bufferHandle);
    if (value.has_value()) {
        *outName = *value;
        return OK;
    }
    return UNKNOWN_TRANSACTION;
}

status_t Gralloc5Mapper::getWidth(buffer_handle_t bufferHandle, uint64_t *outWidth) const {
    auto value = getStandardMetadata<StandardMetadataType::WIDTH>(mMapper, bufferHandle);
    if (value.has_value()) {
        *outWidth = *value;
        return OK;
    }
    return UNKNOWN_TRANSACTION;
}

status_t Gralloc5Mapper::getHeight(buffer_handle_t bufferHandle, uint64_t *outHeight) const {
    auto value = getStandardMetadata<StandardMetadataType::HEIGHT>(mMapper, bufferHandle);
    if (value.has_value()) {
        *outHeight = *value;
        return OK;
    }
    return UNKNOWN_TRANSACTION;
}

status_t Gralloc5Mapper::getLayerCount(buffer_handle_t bufferHandle,
                                       uint64_t *outLayerCount) const {
    auto value = getStandardMetadata<StandardMetadataType::LAYER_COUNT>(mMapper, bufferHandle);
    if (value.has_value()) {
        *outLayerCount = *value;
        return OK;
    }
    return UNKNOWN_TRANSACTION;
}

status_t Gralloc5Mapper::getPixelFormatRequested(buffer_handle_t bufferHandle,
                                                 ui::PixelFormat *outPixelFormatRequested) const {
    auto value = getStandardMetadata<StandardMetadataType::PIXEL_FORMAT_REQUESTED>(mMapper,
                                                                                   bufferHandle);
    if (value.has_value()) {
        *outPixelFormatRequested = static_cast<ui::PixelFormat>(*value);
        return OK;
    }
    return UNKNOWN_TRANSACTION;
}

status_t Gralloc5Mapper::getPixelFormatFourCC(buffer_handle_t bufferHandle,
                                              uint32_t *outPixelFormatFourCC) const {
    auto value =
            getStandardMetadata<StandardMetadataType::PIXEL_FORMAT_FOURCC>(mMapper, bufferHandle);
    if (value.has_value()) {
        *outPixelFormatFourCC = *value;
        return OK;
    }
    return UNKNOWN_TRANSACTION;
}

status_t Gralloc5Mapper::getPixelFormatModifier(buffer_handle_t bufferHandle,
                                                uint64_t *outPixelFormatModifier) const {
    auto value =
            getStandardMetadata<StandardMetadataType::PIXEL_FORMAT_MODIFIER>(mMapper, bufferHandle);
    if (value.has_value()) {
        *outPixelFormatModifier = *value;
        return OK;
    }
    return UNKNOWN_TRANSACTION;
}

status_t Gralloc5Mapper::getUsage(buffer_handle_t bufferHandle, uint64_t *outUsage) const {
    auto value = getStandardMetadata<StandardMetadataType::USAGE>(mMapper, bufferHandle);
    if (value.has_value()) {
        *outUsage = static_cast<uint64_t>(*value);
        return OK;
    }
    return UNKNOWN_TRANSACTION;
}

status_t Gralloc5Mapper::getAllocationSize(buffer_handle_t bufferHandle,
                                           uint64_t *outAllocationSize) const {
    auto value = getStandardMetadata<StandardMetadataType::ALLOCATION_SIZE>(mMapper, bufferHandle);
    if (value.has_value()) {
        *outAllocationSize = *value;
        return OK;
    }
    return UNKNOWN_TRANSACTION;
}

status_t Gralloc5Mapper::getProtectedContent(buffer_handle_t bufferHandle,
                                             uint64_t *outProtectedContent) const {
    auto value =
            getStandardMetadata<StandardMetadataType::PROTECTED_CONTENT>(mMapper, bufferHandle);
    if (value.has_value()) {
        *outProtectedContent = *value;
        return OK;
    }
    return UNKNOWN_TRANSACTION;
}

status_t Gralloc5Mapper::getCompression(
        buffer_handle_t bufferHandle,
        aidl::android::hardware::graphics::common::ExtendableType *outCompression) const {
    auto value = getStandardMetadata<StandardMetadataType::COMPRESSION>(mMapper, bufferHandle);
    if (value.has_value()) {
        *outCompression = *value;
        return OK;
    }
    return UNKNOWN_TRANSACTION;
}

status_t Gralloc5Mapper::getCompression(buffer_handle_t bufferHandle,
                                        ui::Compression *outCompression) const {
    auto value = getStandardMetadata<StandardMetadataType::COMPRESSION>(mMapper, bufferHandle);
    if (!value.has_value()) {
        return UNKNOWN_TRANSACTION;
    }
    if (!gralloc4::isStandardCompression(*value)) {
        return BAD_TYPE;
    }
    *outCompression = gralloc4::getStandardCompressionValue(*value);
    return OK;
}

status_t Gralloc5Mapper::getInterlaced(
        buffer_handle_t bufferHandle,
        aidl::android::hardware::graphics::common::ExtendableType *outInterlaced) const {
    auto value = getStandardMetadata<StandardMetadataType::INTERLACED>(mMapper, bufferHandle);
    if (value.has_value()) {
        *outInterlaced = *value;
        return OK;
    }
    return UNKNOWN_TRANSACTION;
}

status_t Gralloc5Mapper::getInterlaced(buffer_handle_t bufferHandle,
                                       ui::Interlaced *outInterlaced) const {
    if (!outInterlaced) {
        return BAD_VALUE;
    }
    ExtendableType interlaced;
    status_t error = getInterlaced(bufferHandle, &interlaced);
    if (error) {
        return error;
    }
    if (!gralloc4::isStandardInterlaced(interlaced)) {
        return BAD_TYPE;
    }
    *outInterlaced = gralloc4::getStandardInterlacedValue(interlaced);
    return NO_ERROR;
}

status_t Gralloc5Mapper::getChromaSiting(
        buffer_handle_t bufferHandle,
        aidl::android::hardware::graphics::common::ExtendableType *outChromaSiting) const {
    auto value = getStandardMetadata<StandardMetadataType::CHROMA_SITING>(mMapper, bufferHandle);
    if (value.has_value()) {
        *outChromaSiting = *value;
        return OK;
    }
    return UNKNOWN_TRANSACTION;
}

status_t Gralloc5Mapper::getChromaSiting(buffer_handle_t bufferHandle,
                                         ui::ChromaSiting *outChromaSiting) const {
    if (!outChromaSiting) {
        return BAD_VALUE;
    }
    ExtendableType chromaSiting;
    status_t error = getChromaSiting(bufferHandle, &chromaSiting);
    if (error) {
        return error;
    }
    if (!gralloc4::isStandardChromaSiting(chromaSiting)) {
        return BAD_TYPE;
    }
    *outChromaSiting = gralloc4::getStandardChromaSitingValue(chromaSiting);
    return NO_ERROR;
}

status_t Gralloc5Mapper::getPlaneLayouts(buffer_handle_t bufferHandle,
                                         std::vector<ui::PlaneLayout> *outPlaneLayouts) const {
    auto value = getStandardMetadata<StandardMetadataType::PLANE_LAYOUTS>(mMapper, bufferHandle);
    if (value.has_value()) {
        *outPlaneLayouts = *value;
        return OK;
    }
    return UNKNOWN_TRANSACTION;
}

status_t Gralloc5Mapper::getDataspace(buffer_handle_t bufferHandle,
                                      ui::Dataspace *outDataspace) const {
    auto value = getStandardMetadata<StandardMetadataType::DATASPACE>(mMapper, bufferHandle);
    if (value.has_value()) {
        *outDataspace = static_cast<ui::Dataspace>(*value);
        return OK;
    }
    return UNKNOWN_TRANSACTION;
}

status_t Gralloc5Mapper::setDataspace(buffer_handle_t bufferHandle, ui::Dataspace dataspace) const {
    return setStandardMetadata<StandardMetadataType::DATASPACE>(mMapper, bufferHandle,
                                                                static_cast<Dataspace>(dataspace));
}

status_t Gralloc5Mapper::getBlendMode(buffer_handle_t bufferHandle,
                                      ui::BlendMode *outBlendMode) const {
    auto value = getStandardMetadata<StandardMetadataType::BLEND_MODE>(mMapper, bufferHandle);
    if (value.has_value()) {
        *outBlendMode = static_cast<ui::BlendMode>(*value);
        return OK;
    }
    return UNKNOWN_TRANSACTION;
}

status_t Gralloc5Mapper::getSmpte2086(buffer_handle_t bufferHandle,
                                      std::optional<ui::Smpte2086> *outSmpte2086) const {
    auto value = getStandardMetadata<StandardMetadataType::SMPTE2086>(mMapper, bufferHandle);
    if (value.has_value()) {
        *outSmpte2086 = *value;
        return OK;
    }
    return UNKNOWN_TRANSACTION;
}

status_t Gralloc5Mapper::setSmpte2086(buffer_handle_t bufferHandle,
                                      std::optional<ui::Smpte2086> smpte2086) const {
    return setStandardMetadata<StandardMetadataType::SMPTE2086>(mMapper, bufferHandle, smpte2086);
}

status_t Gralloc5Mapper::getCta861_3(buffer_handle_t bufferHandle,
                                     std::optional<ui::Cta861_3> *outCta861_3) const {
    auto value = getStandardMetadata<StandardMetadataType::CTA861_3>(mMapper, bufferHandle);
    if (value.has_value()) {
        *outCta861_3 = *value;
        return OK;
    }
    return UNKNOWN_TRANSACTION;
}

status_t Gralloc5Mapper::setCta861_3(buffer_handle_t bufferHandle,
                                     std::optional<ui::Cta861_3> cta861_3) const {
    return setStandardMetadata<StandardMetadataType::CTA861_3>(mMapper, bufferHandle, cta861_3);
}

status_t Gralloc5Mapper::getSmpte2094_40(
        buffer_handle_t bufferHandle, std::optional<std::vector<uint8_t>> *outSmpte2094_40) const {
    auto value = getStandardMetadata<StandardMetadataType::SMPTE2094_40>(mMapper, bufferHandle);
    if (value.has_value()) {
        *outSmpte2094_40 = std::move(*value);
        return OK;
    }
    return UNKNOWN_TRANSACTION;
}

status_t Gralloc5Mapper::setSmpte2094_40(buffer_handle_t bufferHandle,
                                         std::optional<std::vector<uint8_t>> smpte2094_40) const {
    return setStandardMetadata<StandardMetadataType::SMPTE2094_40>(mMapper, bufferHandle,
                                                                   smpte2094_40);
}

status_t Gralloc5Mapper::getSmpte2094_10(
        buffer_handle_t bufferHandle, std::optional<std::vector<uint8_t>> *outSmpte2094_10) const {
    auto value = getStandardMetadata<StandardMetadataType::SMPTE2094_10>(mMapper, bufferHandle);
    if (value.has_value()) {
        *outSmpte2094_10 = std::move(*value);
        return OK;
    }
    return UNKNOWN_TRANSACTION;
}

status_t Gralloc5Mapper::setSmpte2094_10(buffer_handle_t bufferHandle,
                                         std::optional<std::vector<uint8_t>> smpte2094_10) const {
    return setStandardMetadata<StandardMetadataType::SMPTE2094_10>(mMapper, bufferHandle,
                                                                   smpte2094_10);
}

} // namespace android