/*
 * Copyright 2019 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 "Gralloc4"

#include <aidl/android/hardware/graphics/allocator/AllocationError.h>
#include <aidl/android/hardware/graphics/allocator/AllocationResult.h>
#include <aidl/android/hardware/graphics/common/BufferUsage.h>
#include <aidlcommonsupport/NativeHandle.h>
#include <android/binder_enums.h>
#include <android/binder_manager.h>
#include <cutils/android_filesystem_config.h>
#include <cutils/multiuser.h>
#include <gralloctypes/Gralloc4.h>
#include <hidl/ServiceManagement.h>
#include <hwbinder/IPCThreadState.h>
#include <ui/Gralloc4.h>

#include <inttypes.h>
#include <log/log.h>
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wzero-length-array"
#include <sync/sync.h>
#pragma clang diagnostic pop

using aidl::android::hardware::graphics::allocator::AllocationError;
using aidl::android::hardware::graphics::allocator::AllocationResult;
using aidl::android::hardware::graphics::common::ExtendableType;
using aidl::android::hardware::graphics::common::PlaneLayoutComponentType;
using aidl::android::hardware::graphics::common::StandardMetadataType;
using android::hardware::hidl_vec;
using android::hardware::graphics::allocator::V4_0::IAllocator;
using android::hardware::graphics::common::V1_2::BufferUsage;
using android::hardware::graphics::common::V1_2::PixelFormat;
using android::hardware::graphics::mapper::V4_0::BufferDescriptor;
using android::hardware::graphics::mapper::V4_0::Error;
using android::hardware::graphics::mapper::V4_0::IMapper;
using AidlIAllocator = ::aidl::android::hardware::graphics::allocator::IAllocator;
using AidlBufferUsage = ::aidl::android::hardware::graphics::common::BufferUsage;
using AidlDataspace = ::aidl::android::hardware::graphics::common::Dataspace;
using AidlNativeHandle = ::aidl::android::hardware::common::NativeHandle;
using BufferDump = android::hardware::graphics::mapper::V4_0::IMapper::BufferDump;
using MetadataDump = android::hardware::graphics::mapper::V4_0::IMapper::MetadataDump;
using MetadataType = android::hardware::graphics::mapper::V4_0::IMapper::MetadataType;
using MetadataTypeDescription =
        android::hardware::graphics::mapper::V4_0::IMapper::MetadataTypeDescription;

namespace android {

namespace {

static constexpr Error kTransactionError = Error::NO_RESOURCES;
static const auto kAidlAllocatorServiceName = AidlIAllocator::descriptor + std::string("/default");

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

uint64_t getValidUsageBits() {
    static const uint64_t validUsageBits = []() -> uint64_t {
        uint64_t bits = 0;
        for (const auto bit :
             hardware::hidl_enum_range<hardware::graphics::common::V1_2::BufferUsage>()) {
            bits = bits | bit;
        }
        return bits;
    }();
    return validUsageBits | kRemovedUsageBits;
}

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

static inline IMapper::Rect sGralloc4Rect(const Rect& rect) {
    IMapper::Rect outRect{};
    outRect.left = rect.left;
    outRect.top = rect.top;
    outRect.width = rect.width();
    outRect.height = rect.height();
    return outRect;
}

// See if gralloc "4.1" is available.
static bool hasIAllocatorAidl() {
    // Avoid re-querying repeatedly for this information;
    static bool sHasIAllocatorAidl = []() -> bool {
        if (__builtin_available(android 31, *)) {
            return AServiceManager_isDeclared(kAidlAllocatorServiceName.c_str());
        }
        return false;
    }();
    return sHasIAllocatorAidl;
}

// Determines whether the passed info is compatible with the mapper.
static status_t validateBufferDescriptorInfo(IMapper::BufferDescriptorInfo* descriptorInfo) {
    uint64_t validUsageBits = getValidUsageBits();
    if (hasIAllocatorAidl()) {
        validUsageBits |= getValidUsageBits41();
    }

    if (descriptorInfo->usage & ~validUsageBits) {
        ALOGE("buffer descriptor contains invalid usage bits 0x%" PRIx64,
              descriptorInfo->usage & ~validUsageBits);
        return BAD_VALUE;
    }

    // Combinations that are only allowed with gralloc 4.1.
    // Previous grallocs must be protected from this.
    if (!hasIAllocatorAidl() &&
            descriptorInfo->format != hardware::graphics::common::V1_2::PixelFormat::BLOB &&
            descriptorInfo->usage & BufferUsage::GPU_DATA_BUFFER) {
        ALOGE("non-BLOB pixel format with GPU_DATA_BUFFER usage is not supported prior to gralloc 4.1");
        return BAD_VALUE;
    }

    return NO_ERROR;
}

static inline status_t sBufferDescriptorInfo(std::string name, uint32_t width, uint32_t height,
                                             PixelFormat format, uint32_t layerCount,
                                             uint64_t usage,
                                             IMapper::BufferDescriptorInfo* outDescriptorInfo) {
    outDescriptorInfo->name = name;
    outDescriptorInfo->width = width;
    outDescriptorInfo->height = height;
    outDescriptorInfo->layerCount = layerCount;
    outDescriptorInfo->format = static_cast<hardware::graphics::common::V1_2::PixelFormat>(format);
    outDescriptorInfo->usage = usage;
    outDescriptorInfo->reservedSize = 0;

    return validateBufferDescriptorInfo(outDescriptorInfo);
}

} // anonymous namespace

void Gralloc4Mapper::preload() {
    android::hardware::preloadPassthroughService<IMapper>();
}

Gralloc4Mapper::Gralloc4Mapper() {
    mMapper = IMapper::getService();
    if (mMapper == nullptr) {
        ALOGI("mapper 4.x is not supported");
        return;
    }
    if (mMapper->isRemote()) {
        LOG_ALWAYS_FATAL("gralloc-mapper must be in passthrough mode");
    }
}

bool Gralloc4Mapper::isLoaded() const {
    return mMapper != nullptr;
}

status_t Gralloc4Mapper::createDescriptor(void* bufferDescriptorInfo,
                                          void* outBufferDescriptor) const {
    IMapper::BufferDescriptorInfo* descriptorInfo =
            static_cast<IMapper::BufferDescriptorInfo*>(bufferDescriptorInfo);
    BufferDescriptor* outDescriptor = static_cast<BufferDescriptor*>(outBufferDescriptor);

    status_t status = validateBufferDescriptorInfo(descriptorInfo);
    if (status != NO_ERROR) {
        return status;
    }

    Error error;
    auto hidl_cb = [&](const auto& tmpError, const auto& tmpDescriptor) {
        error = tmpError;
        if (error != Error::NONE) {
            return;
        }
        *outDescriptor = tmpDescriptor;
    };

    hardware::Return<void> ret = mMapper->createDescriptor(*descriptorInfo, hidl_cb);

    return static_cast<status_t>((ret.isOk()) ? error : kTransactionError);
}

status_t Gralloc4Mapper::importBuffer(const native_handle_t* rawHandle,
                                      buffer_handle_t* outBufferHandle) const {
    Error error;
    auto ret = mMapper->importBuffer(rawHandle, [&](const auto& tmpError, const auto& tmpBuffer) {
        error = tmpError;
        if (error != Error::NONE) {
            return;
        }
        *outBufferHandle = static_cast<buffer_handle_t>(tmpBuffer);
    });

    return static_cast<status_t>((ret.isOk()) ? error : kTransactionError);
}

void Gralloc4Mapper::freeBuffer(buffer_handle_t bufferHandle) const {
    auto buffer = const_cast<native_handle_t*>(bufferHandle);
    auto ret = mMapper->freeBuffer(buffer);

    auto error = (ret.isOk()) ? static_cast<Error>(ret) : kTransactionError;
    ALOGE_IF(error != Error::NONE, "freeBuffer(%p) failed with %d", buffer, error);
}

status_t Gralloc4Mapper::validateBufferSize(buffer_handle_t bufferHandle, uint32_t width,
                                            uint32_t height, PixelFormat format,
                                            uint32_t layerCount, uint64_t usage,
                                            uint32_t stride) const {
    IMapper::BufferDescriptorInfo descriptorInfo;
    if (auto error = sBufferDescriptorInfo("validateBufferSize", width, height, format, layerCount,
                                           usage, &descriptorInfo) != OK) {
        return error;
    }

    auto buffer = const_cast<native_handle_t*>(bufferHandle);
    auto ret = mMapper->validateBufferSize(buffer, descriptorInfo, stride);

    return static_cast<status_t>((ret.isOk()) ? static_cast<Error>(ret) : kTransactionError);
}

void Gralloc4Mapper::getTransportSize(buffer_handle_t bufferHandle, uint32_t* outNumFds,
                                      uint32_t* outNumInts) const {
    *outNumFds = uint32_t(bufferHandle->numFds);
    *outNumInts = uint32_t(bufferHandle->numInts);

    Error error;
    auto buffer = const_cast<native_handle_t*>(bufferHandle);
    auto ret = mMapper->getTransportSize(buffer,
                                         [&](const auto& tmpError, const auto& tmpNumFds,
                                             const auto& tmpNumInts) {
                                             error = tmpError;
                                             if (error != Error::NONE) {
                                                 return;
                                             }
                                             *outNumFds = tmpNumFds;
                                             *outNumInts = tmpNumInts;
                                         });

    error = (ret.isOk()) ? error : kTransactionError;

    ALOGE_IF(error != Error::NONE, "getTransportSize(%p) failed with %d", buffer, error);
}

status_t Gralloc4Mapper::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 buffer = const_cast<native_handle_t*>(bufferHandle);

    IMapper::Rect accessRegion = sGralloc4Rect(bounds);

    // put acquireFence in a hidl_handle
    hardware::hidl_handle acquireFenceHandle;
    NATIVE_HANDLE_DECLARE_STORAGE(acquireFenceStorage, 1, 0);
    if (acquireFence >= 0) {
        auto h = native_handle_init(acquireFenceStorage, 1, 0);
        h->data[0] = acquireFence;
        acquireFenceHandle = h;
    }

    Error error;
    auto ret = mMapper->lock(buffer, usage, accessRegion, acquireFenceHandle,
                             [&](const auto& tmpError, const auto& tmpData) {
                                 error = tmpError;
                                 if (error != Error::NONE) {
                                     return;
                                 }
                                 *outData = tmpData;
                             });

    // we own acquireFence even on errors
    if (acquireFence >= 0) {
        close(acquireFence);
    }

    error = (ret.isOk()) ? error : kTransactionError;

    ALOGW_IF(error != Error::NONE, "lock(%p, ...) failed: %d", bufferHandle, error);

    return static_cast<status_t>(error);
}

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

    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 static_cast<status_t>(Error::NONE);
}

int Gralloc4Mapper::unlock(buffer_handle_t bufferHandle) const {
    auto buffer = const_cast<native_handle_t*>(bufferHandle);

    int releaseFence = -1;
    Error error;
    auto ret = mMapper->unlock(buffer, [&](const auto& tmpError, const auto& tmpReleaseFence) {
        error = tmpError;
        if (error != Error::NONE) {
            return;
        }

        auto fenceHandle = tmpReleaseFence.getNativeHandle();
        if (fenceHandle && fenceHandle->numFds == 1) {
            int fd = dup(fenceHandle->data[0]);
            if (fd >= 0) {
                releaseFence = fd;
            } else {
                ALOGW("failed to dup unlock release fence");
                sync_wait(fenceHandle->data[0], -1);
            }
        }
    });

    if (!ret.isOk()) {
        error = kTransactionError;
    }

    if (error != Error::NONE) {
        ALOGE("unlock(%p) failed with %d", buffer, error);
    }

    return releaseFence;
}

status_t Gralloc4Mapper::isSupported(uint32_t width, uint32_t height, PixelFormat format,
                                     uint32_t layerCount, uint64_t usage,
                                     bool* outSupported) const {
    IMapper::BufferDescriptorInfo descriptorInfo;
    if (sBufferDescriptorInfo("isSupported", width, height, format, layerCount, usage,
                              &descriptorInfo) != OK) {
        // Usage isn't known to the HAL or otherwise failed validation.
        *outSupported = false;
        return OK;
    }

    Error error;
    auto ret = mMapper->isSupported(descriptorInfo,
                                    [&](const auto& tmpError, const auto& tmpSupported) {
                                        error = tmpError;
                                        if (error != Error::NONE) {
                                            return;
                                        }
                                        if (outSupported) {
                                            *outSupported = tmpSupported;
                                        }
                                    });

    if (!ret.isOk()) {
        error = kTransactionError;
    }

    if (error != Error::NONE) {
        ALOGE("isSupported(%u, %u, %d, %u, ...) failed with %d", width, height, format, layerCount,
              error);
    }

    return static_cast<status_t>(error);
}

template <class T>
status_t Gralloc4Mapper::get(buffer_handle_t bufferHandle, const MetadataType& metadataType,
                             DecodeFunction<T> decodeFunction, T* outMetadata) const {
    if (!outMetadata) {
        return BAD_VALUE;
    }

    hidl_vec<uint8_t> vec;
    Error error;
    auto ret = mMapper->get(const_cast<native_handle_t*>(bufferHandle), metadataType,
                            [&](const auto& tmpError, const hidl_vec<uint8_t>& tmpVec) {
                                error = tmpError;
                                vec = tmpVec;
                            });

    if (!ret.isOk()) {
        error = kTransactionError;
    }

    if (error != Error::NONE) {
        ALOGE("get(%s, %" PRIu64 ", ...) failed with %d", metadataType.name.c_str(),
              metadataType.value, error);
        return static_cast<status_t>(error);
    }

    return decodeFunction(vec, outMetadata);
}

template <class T>
status_t Gralloc4Mapper::set(buffer_handle_t bufferHandle, const MetadataType& metadataType,
                             const T& metadata, EncodeFunction<T> encodeFunction) const {
    hidl_vec<uint8_t> encodedMetadata;
    if (const status_t status = encodeFunction(metadata, &encodedMetadata); status != OK) {
        ALOGE("Encoding metadata(%s) failed with %d", metadataType.name.c_str(), status);
        return status;
    }
    hidl_vec<uint8_t> vec;
    auto ret =
            mMapper->set(const_cast<native_handle_t*>(bufferHandle), metadataType, encodedMetadata);

    const Error error = ret.withDefault(kTransactionError);
    switch (error) {
        case Error::BAD_DESCRIPTOR:
        case Error::BAD_BUFFER:
        case Error::BAD_VALUE:
        case Error::NO_RESOURCES:
            ALOGE("set(%s, %" PRIu64 ", ...) failed with %d", metadataType.name.c_str(),
                  metadataType.value, error);
            break;
        // It is not an error to attempt to set metadata that a particular gralloc implementation
        // happens to not support.
        case Error::UNSUPPORTED:
        case Error::NONE:
            break;
    }

    return static_cast<status_t>(error);
}

status_t Gralloc4Mapper::getBufferId(buffer_handle_t bufferHandle, uint64_t* outBufferId) const {
    return get(bufferHandle, gralloc4::MetadataType_BufferId, gralloc4::decodeBufferId,
               outBufferId);
}

status_t Gralloc4Mapper::getName(buffer_handle_t bufferHandle, std::string* outName) const {
    return get(bufferHandle, gralloc4::MetadataType_Name, gralloc4::decodeName, outName);
}

status_t Gralloc4Mapper::getWidth(buffer_handle_t bufferHandle, uint64_t* outWidth) const {
    return get(bufferHandle, gralloc4::MetadataType_Width, gralloc4::decodeWidth, outWidth);
}

status_t Gralloc4Mapper::getHeight(buffer_handle_t bufferHandle, uint64_t* outHeight) const {
    return get(bufferHandle, gralloc4::MetadataType_Height, gralloc4::decodeHeight, outHeight);
}

status_t Gralloc4Mapper::getLayerCount(buffer_handle_t bufferHandle,
                                       uint64_t* outLayerCount) const {
    return get(bufferHandle, gralloc4::MetadataType_LayerCount, gralloc4::decodeLayerCount,
               outLayerCount);
}

status_t Gralloc4Mapper::getPixelFormatRequested(buffer_handle_t bufferHandle,
                                                 ui::PixelFormat* outPixelFormatRequested) const {
    return get(bufferHandle, gralloc4::MetadataType_PixelFormatRequested,
               gralloc4::decodePixelFormatRequested, outPixelFormatRequested);
}

status_t Gralloc4Mapper::getPixelFormatFourCC(buffer_handle_t bufferHandle,
                                              uint32_t* outPixelFormatFourCC) const {
    return get(bufferHandle, gralloc4::MetadataType_PixelFormatFourCC,
               gralloc4::decodePixelFormatFourCC, outPixelFormatFourCC);
}

status_t Gralloc4Mapper::getPixelFormatModifier(buffer_handle_t bufferHandle,
                                                uint64_t* outPixelFormatModifier) const {
    return get(bufferHandle, gralloc4::MetadataType_PixelFormatModifier,
               gralloc4::decodePixelFormatModifier, outPixelFormatModifier);
}

status_t Gralloc4Mapper::getUsage(buffer_handle_t bufferHandle, uint64_t* outUsage) const {
    return get(bufferHandle, gralloc4::MetadataType_Usage, gralloc4::decodeUsage, outUsage);
}

status_t Gralloc4Mapper::getAllocationSize(buffer_handle_t bufferHandle,
                                           uint64_t* outAllocationSize) const {
    return get(bufferHandle, gralloc4::MetadataType_AllocationSize, gralloc4::decodeAllocationSize,
               outAllocationSize);
}

status_t Gralloc4Mapper::getProtectedContent(buffer_handle_t bufferHandle,
                                             uint64_t* outProtectedContent) const {
    return get(bufferHandle, gralloc4::MetadataType_ProtectedContent,
               gralloc4::decodeProtectedContent, outProtectedContent);
}

status_t Gralloc4Mapper::getCompression(buffer_handle_t bufferHandle,
                                        ExtendableType* outCompression) const {
    return get(bufferHandle, gralloc4::MetadataType_Compression, gralloc4::decodeCompression,
               outCompression);
}

status_t Gralloc4Mapper::getCompression(buffer_handle_t bufferHandle,
                                        ui::Compression* outCompression) const {
    if (!outCompression) {
        return BAD_VALUE;
    }
    ExtendableType compression;
    status_t error = getCompression(bufferHandle, &compression);
    if (error) {
        return error;
    }
    if (!gralloc4::isStandardCompression(compression)) {
        return BAD_TYPE;
    }
    *outCompression = gralloc4::getStandardCompressionValue(compression);
    return NO_ERROR;
}

status_t Gralloc4Mapper::getInterlaced(buffer_handle_t bufferHandle,
                                       ExtendableType* outInterlaced) const {
    return get(bufferHandle, gralloc4::MetadataType_Interlaced, gralloc4::decodeInterlaced,
               outInterlaced);
}

status_t Gralloc4Mapper::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 Gralloc4Mapper::getChromaSiting(buffer_handle_t bufferHandle,
                                         ExtendableType* outChromaSiting) const {
    return get(bufferHandle, gralloc4::MetadataType_ChromaSiting, gralloc4::decodeChromaSiting,
               outChromaSiting);
}

status_t Gralloc4Mapper::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 Gralloc4Mapper::getPlaneLayouts(buffer_handle_t bufferHandle,
                                         std::vector<ui::PlaneLayout>* outPlaneLayouts) const {
    return get(bufferHandle, gralloc4::MetadataType_PlaneLayouts, gralloc4::decodePlaneLayouts,
               outPlaneLayouts);
}

status_t Gralloc4Mapper::getDataspace(buffer_handle_t bufferHandle,
                                      ui::Dataspace* outDataspace) const {
    if (!outDataspace) {
        return BAD_VALUE;
    }
    AidlDataspace dataspace;
    status_t error = get(bufferHandle, gralloc4::MetadataType_Dataspace, gralloc4::decodeDataspace,
                         &dataspace);
    if (error) {
        return error;
    }

    // Gralloc4 uses stable AIDL dataspace but the rest of the system still uses HIDL dataspace
    *outDataspace = static_cast<ui::Dataspace>(dataspace);
    return NO_ERROR;
}

status_t Gralloc4Mapper::setDataspace(buffer_handle_t bufferHandle, ui::Dataspace dataspace) const {
    return set(bufferHandle, gralloc4::MetadataType_Dataspace,
               static_cast<aidl::android::hardware::graphics::common::Dataspace>(dataspace),
               gralloc4::encodeDataspace);
}

status_t Gralloc4Mapper::getBlendMode(buffer_handle_t bufferHandle,
                                      ui::BlendMode* outBlendMode) const {
    return get(bufferHandle, gralloc4::MetadataType_BlendMode, gralloc4::decodeBlendMode,
               outBlendMode);
}

status_t Gralloc4Mapper::getSmpte2086(buffer_handle_t bufferHandle,
                                      std::optional<ui::Smpte2086>* outSmpte2086) const {
    return get(bufferHandle, gralloc4::MetadataType_Smpte2086, gralloc4::decodeSmpte2086,
               outSmpte2086);
}

status_t Gralloc4Mapper::setSmpte2086(buffer_handle_t bufferHandle,
                                      std::optional<ui::Smpte2086> smpte2086) const {
    return set(bufferHandle, gralloc4::MetadataType_Smpte2086, smpte2086,
               gralloc4::encodeSmpte2086);
}

status_t Gralloc4Mapper::getCta861_3(buffer_handle_t bufferHandle,
                                     std::optional<ui::Cta861_3>* outCta861_3) const {
    return get(bufferHandle, gralloc4::MetadataType_Cta861_3, gralloc4::decodeCta861_3,
               outCta861_3);
}

status_t Gralloc4Mapper::setCta861_3(buffer_handle_t bufferHandle,
                                     std::optional<ui::Cta861_3> cta861_3) const {
    return set(bufferHandle, gralloc4::MetadataType_Cta861_3, cta861_3, gralloc4::encodeCta861_3);
}

status_t Gralloc4Mapper::getSmpte2094_40(
        buffer_handle_t bufferHandle, std::optional<std::vector<uint8_t>>* outSmpte2094_40) const {
    return get(bufferHandle, gralloc4::MetadataType_Smpte2094_40, gralloc4::decodeSmpte2094_40,
               outSmpte2094_40);
}

status_t Gralloc4Mapper::setSmpte2094_40(buffer_handle_t bufferHandle,
                                         std::optional<std::vector<uint8_t>> smpte2094_40) const {
    return set(bufferHandle, gralloc4::MetadataType_Smpte2094_40, smpte2094_40,
               gralloc4::encodeSmpte2094_40);
}

status_t Gralloc4Mapper::getSmpte2094_10(
        buffer_handle_t bufferHandle, std::optional<std::vector<uint8_t>>* outSmpte2094_10) const {
    return get(bufferHandle, gralloc4::MetadataType_Smpte2094_10, gralloc4::decodeSmpte2094_10,
               outSmpte2094_10);
}

status_t Gralloc4Mapper::setSmpte2094_10(buffer_handle_t bufferHandle,
                                         std::optional<std::vector<uint8_t>> smpte2094_10) const {
    return set(bufferHandle, gralloc4::MetadataType_Smpte2094_10, smpte2094_10,
               gralloc4::encodeSmpte2094_10);
}

std::vector<MetadataTypeDescription> Gralloc4Mapper::listSupportedMetadataTypes() const {
    hidl_vec<MetadataTypeDescription> descriptions;
    Error error;
    auto ret = mMapper->listSupportedMetadataTypes(
            [&](const auto& tmpError, const auto& tmpDescriptions) {
                error = tmpError;
                descriptions = tmpDescriptions;
            });

    if (!ret.isOk()) {
        error = kTransactionError;
    }

    if (error != Error::NONE) {
        ALOGE("listSupportedMetadataType() failed with %d", error);
        return {};
    }

    return static_cast<std::vector<MetadataTypeDescription>>(descriptions);
}

template <class T>
status_t Gralloc4Mapper::metadataDumpHelper(const BufferDump& bufferDump,
                                            StandardMetadataType metadataType,
                                            DecodeFunction<T> decodeFunction, T* outT) const {
    const auto& metadataDump = bufferDump.metadataDump;

    auto itr =
            std::find_if(metadataDump.begin(), metadataDump.end(),
                         [&](const MetadataDump& tmpMetadataDump) {
                             if (!gralloc4::isStandardMetadataType(tmpMetadataDump.metadataType)) {
                                 return false;
                             }
                             return metadataType ==
                                     gralloc4::getStandardMetadataTypeValue(
                                             tmpMetadataDump.metadataType);
                         });
    if (itr == metadataDump.end()) {
        return BAD_VALUE;
    }

    return decodeFunction(itr->metadata, outT);
}

status_t Gralloc4Mapper::bufferDumpHelper(const BufferDump& bufferDump, std::ostringstream* outDump,
                                          uint64_t* outAllocationSize, bool less) const {
    uint64_t bufferId;
    std::string name;
    uint64_t width;
    uint64_t height;
    uint64_t layerCount;
    ui::PixelFormat pixelFormatRequested;
    uint32_t pixelFormatFourCC;
    uint64_t pixelFormatModifier;
    uint64_t usage;
    AidlDataspace dataspace;
    uint64_t allocationSize;
    uint64_t protectedContent;
    ExtendableType compression;
    ExtendableType interlaced;
    ExtendableType chromaSiting;
    std::vector<ui::PlaneLayout> planeLayouts;

    status_t error = metadataDumpHelper(bufferDump, StandardMetadataType::BUFFER_ID,
                                        gralloc4::decodeBufferId, &bufferId);
    if (error != NO_ERROR) {
        return error;
    }
    error = metadataDumpHelper(bufferDump, StandardMetadataType::NAME, gralloc4::decodeName, &name);
    if (error != NO_ERROR) {
        return error;
    }
    error = metadataDumpHelper(bufferDump, StandardMetadataType::WIDTH, gralloc4::decodeWidth,
                               &width);
    if (error != NO_ERROR) {
        return error;
    }
    error = metadataDumpHelper(bufferDump, StandardMetadataType::HEIGHT, gralloc4::decodeHeight,
                               &height);
    if (error != NO_ERROR) {
        return error;
    }
    error = metadataDumpHelper(bufferDump, StandardMetadataType::LAYER_COUNT,
                               gralloc4::decodeLayerCount, &layerCount);
    if (error != NO_ERROR) {
        return error;
    }
    error = metadataDumpHelper(bufferDump, StandardMetadataType::PIXEL_FORMAT_REQUESTED,
                               gralloc4::decodePixelFormatRequested, &pixelFormatRequested);
    if (error != NO_ERROR) {
        return error;
    }
    error = metadataDumpHelper(bufferDump, StandardMetadataType::PIXEL_FORMAT_FOURCC,
                               gralloc4::decodePixelFormatFourCC, &pixelFormatFourCC);
    if (error != NO_ERROR) {
        return error;
    }
    error = metadataDumpHelper(bufferDump, StandardMetadataType::PIXEL_FORMAT_MODIFIER,
                               gralloc4::decodePixelFormatModifier, &pixelFormatModifier);
    if (error != NO_ERROR) {
        return error;
    }
    error = metadataDumpHelper(bufferDump, StandardMetadataType::USAGE, gralloc4::decodeUsage,
                               &usage);
    if (error != NO_ERROR) {
        return error;
    }
    error = metadataDumpHelper(bufferDump, StandardMetadataType::DATASPACE,
                               gralloc4::decodeDataspace, &dataspace);
    if (error != NO_ERROR) {
        return error;
    }
    error = metadataDumpHelper(bufferDump, StandardMetadataType::ALLOCATION_SIZE,
                               gralloc4::decodeAllocationSize, &allocationSize);
    if (error != NO_ERROR) {
        return error;
    }
    error = metadataDumpHelper(bufferDump, StandardMetadataType::PROTECTED_CONTENT,
                               gralloc4::decodeProtectedContent, &protectedContent);
    if (error != NO_ERROR) {
        return error;
    }
    error = metadataDumpHelper(bufferDump, StandardMetadataType::COMPRESSION,
                               gralloc4::decodeCompression, &compression);
    if (error != NO_ERROR) {
        return error;
    }
    error = metadataDumpHelper(bufferDump, StandardMetadataType::INTERLACED,
                               gralloc4::decodeInterlaced, &interlaced);
    if (error != NO_ERROR) {
        return error;
    }
    error = metadataDumpHelper(bufferDump, StandardMetadataType::CHROMA_SITING,
                               gralloc4::decodeChromaSiting, &chromaSiting);
    if (error != NO_ERROR) {
        return error;
    }
    error = metadataDumpHelper(bufferDump, StandardMetadataType::PLANE_LAYOUTS,
                               gralloc4::decodePlaneLayouts, &planeLayouts);
    if (error != NO_ERROR) {
        return error;
    }

    if (outAllocationSize) {
        *outAllocationSize = allocationSize;
    }
    double allocationSizeKiB = static_cast<double>(allocationSize) / 1024;

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

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

    bool firstPlane = true;
    for (const auto& planeLayout : 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;
        if (!less) {
            *outDump << ", inc:" << planeLayout.sampleIncrementInBits
                     << " bits, subsampling w/h:" << planeLayout.horizontalSubsampling << "x"
                     << planeLayout.verticalSubsampling;
        }
        *outDump << "\n";
    }

    if (!less) {
        *outDump << "\tlayer cnt: " << layerCount << ", protected content: " << protectedContent
                 << ", interlaced: " << gralloc4::getInterlacedName(interlaced)
                 << ", chroma siting:" << gralloc4::getChromaSitingName(chromaSiting) << "\n";
    }

    return NO_ERROR;
}

std::string Gralloc4Mapper::dumpBuffer(buffer_handle_t bufferHandle, bool less) const {
    auto buffer = const_cast<native_handle_t*>(bufferHandle);

    BufferDump bufferDump;
    Error error;
    auto ret = mMapper->dumpBuffer(buffer, [&](const auto& tmpError, const auto& tmpBufferDump) {
        error = tmpError;
        bufferDump = tmpBufferDump;
    });

    if (!ret.isOk()) {
        error = kTransactionError;
    }

    if (error != Error::NONE) {
        ALOGE("dumpBuffer() failed with %d", error);
        return "";
    }

    std::ostringstream stream;
    stream.precision(2);

    status_t err = bufferDumpHelper(bufferDump, &stream, nullptr, less);
    if (err != NO_ERROR) {
        ALOGE("bufferDumpHelper() failed with %d", err);
        return "";
    }

    return stream.str();
}

std::string Gralloc4Mapper::dumpBuffers(bool less) const {
    hidl_vec<BufferDump> bufferDumps;
    Error error;
    auto ret = mMapper->dumpBuffers([&](const auto& tmpError, const auto& tmpBufferDump) {
        error = tmpError;
        bufferDumps = tmpBufferDump;
    });

    if (!ret.isOk()) {
        error = kTransactionError;
    }

    if (error != Error::NONE) {
        ALOGE("dumpBuffer() failed with %d", error);
        return "";
    }

    uint64_t totalAllocationSize = 0;
    std::ostringstream stream;
    stream.precision(2);

    stream << "Imported gralloc buffers:\n";

    for (const auto& bufferDump : bufferDumps) {
        uint64_t allocationSize = 0;
        status_t err = bufferDumpHelper(bufferDump, &stream, &allocationSize, less);
        if (err != NO_ERROR) {
            ALOGE("bufferDumpHelper() failed with %d", err);
            return "";
        }
        totalAllocationSize += allocationSize;
    }

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

Gralloc4Allocator::Gralloc4Allocator(const Gralloc4Mapper& mapper) : mMapper(mapper) {
    mAllocator = IAllocator::getService();
    if (__builtin_available(android 31, *)) {
        if (hasIAllocatorAidl()) {
            // TODO(b/269517338): Perform the isolated checking for this in service manager instead.
            uid_t aid = multiuser_get_app_id(getuid());
            if (aid >= AID_ISOLATED_START && aid <= AID_ISOLATED_END) {
                mAidlAllocator = AidlIAllocator::fromBinder(ndk::SpAIBinder(
                        AServiceManager_getService(kAidlAllocatorServiceName.c_str())));
            } else {
                mAidlAllocator = AidlIAllocator::fromBinder(ndk::SpAIBinder(
                        AServiceManager_waitForService(kAidlAllocatorServiceName.c_str())));
            }
            ALOGE_IF(!mAidlAllocator, "AIDL IAllocator declared but failed to get service");
        }
    }
    if (mAllocator == nullptr && mAidlAllocator == nullptr) {
        ALOGW("allocator 4.x is not supported");
        return;
    }
}

bool Gralloc4Allocator::isLoaded() const {
    return mAllocator != nullptr || mAidlAllocator != nullptr;
}

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

status_t Gralloc4Allocator::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 {
    IMapper::BufferDescriptorInfo descriptorInfo;
    if (auto error = sBufferDescriptorInfo(requestorName, width, height, format, layerCount, usage,
                                           &descriptorInfo) != OK) {
        return error;
    }

    BufferDescriptor descriptor;
    status_t error = mMapper.createDescriptor(static_cast<void*>(&descriptorInfo),
                                              static_cast<void*>(&descriptor));
    if (error != NO_ERROR) {
        return error;
    }

    constexpr auto bufferCount = 1;

    if (mAidlAllocator) {
        AllocationResult result;
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
        auto status = mAidlAllocator->allocate(descriptor, bufferCount, &result);
#pragma clang diagnostic pop // deprecation
        if (!status.isOk()) {
            error = status.getExceptionCode();
            if (error == EX_SERVICE_SPECIFIC) {
                error = status.getServiceSpecificError();
            }
            if (error == OK) {
                error = UNKNOWN_ERROR;
            }
        } else {
            if (importBuffers) {
                for (uint32_t i = 0; i < bufferCount; i++) {
                    auto handle = makeFromAidl(result.buffers[i]);
                    error = mMapper.importBuffer(handle, &outBufferHandles[i]);
                    native_handle_delete(handle);
                    if (error != NO_ERROR) {
                        for (uint32_t j = 0; j < i; j++) {
                            mMapper.freeBuffer(outBufferHandles[j]);
                            outBufferHandles[j] = nullptr;
                        }
                        break;
                    }
                }
            } else {
                for (uint32_t i = 0; i < bufferCount; i++) {
                    outBufferHandles[i] = dupFromAidl(result.buffers[i]);
                    if (!outBufferHandles[i]) {
                        for (uint32_t j = 0; j < i; j++) {
                            auto buffer = const_cast<native_handle_t*>(outBufferHandles[j]);
                            native_handle_close(buffer);
                            native_handle_delete(buffer);
                            outBufferHandles[j] = nullptr;
                        }
                    }
                }
            }
        }
        *outStride = 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
        hardware::IPCThreadState::self()->flushCommands();
        return error;
    }

    auto ret = mAllocator->allocate(descriptor, bufferCount,
                                    [&](const auto& tmpError, const auto& tmpStride,
                                        const auto& tmpBuffers) {
                                        error = static_cast<status_t>(tmpError);
                                        if (tmpError != Error::NONE) {
                                            return;
                                        }

                                        if (importBuffers) {
                                            for (uint32_t i = 0; i < bufferCount; i++) {
                                                error = mMapper.importBuffer(tmpBuffers[i],
                                                                             &outBufferHandles[i]);
                                                if (error != NO_ERROR) {
                                                    for (uint32_t j = 0; j < i; j++) {
                                                        mMapper.freeBuffer(outBufferHandles[j]);
                                                        outBufferHandles[j] = nullptr;
                                                    }
                                                    return;
                                                }
                                            }
                                        } else {
                                            for (uint32_t i = 0; i < bufferCount; i++) {
                                                outBufferHandles[i] = native_handle_clone(
                                                        tmpBuffers[i].getNativeHandle());
                                                if (!outBufferHandles[i]) {
                                                    for (uint32_t j = 0; j < i; j++) {
                                                        auto buffer = const_cast<native_handle_t*>(
                                                                outBufferHandles[j]);
                                                        native_handle_close(buffer);
                                                        native_handle_delete(buffer);
                                                        outBufferHandles[j] = nullptr;
                                                    }
                                                }
                                            }
                                        }
                                        *outStride = tmpStride;
                                    });

    // make sure the kernel driver sees BC_FREE_BUFFER and closes the fds now
    hardware::IPCThreadState::self()->flushCommands();

    return (ret.isOk()) ? error : static_cast<status_t>(kTransactionError);
}

} // namespace android
