/*
 * Copyright 2021 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.
 */

#undef LOG_TAG
#define LOG_TAG "HwcComposer"
#define ATRACE_TAG ATRACE_TAG_GRAPHICS

#include "AidlComposerHal.h"

#include <SurfaceFlingerProperties.h>
#include <android-base/file.h>
#include <android/binder_ibinder_platform.h>
#include <android/binder_manager.h>
#include <common/FlagManager.h>
#include <gui/TraceUtils.h>
#include <log/log.h>
#include <utils/Trace.h>

#include <aidl/android/hardware/graphics/composer3/BnComposerCallback.h>

#include <algorithm>
#include <cinttypes>

#include "HWC2.h"

namespace android {

using hardware::hidl_handle;
using hardware::hidl_vec;
using hardware::Return;

using aidl::android::hardware::graphics::composer3::BnComposerCallback;
using aidl::android::hardware::graphics::composer3::Capability;
using aidl::android::hardware::graphics::composer3::ClientTargetPropertyWithBrightness;
using aidl::android::hardware::graphics::composer3::PowerMode;
using aidl::android::hardware::graphics::composer3::VirtualDisplay;

using aidl::android::hardware::graphics::composer3::CommandResultPayload;

using AidlColorMode = aidl::android::hardware::graphics::composer3::ColorMode;
using AidlContentType = aidl::android::hardware::graphics::composer3::ContentType;
using AidlDisplayIdentification =
        aidl::android::hardware::graphics::composer3::DisplayIdentification;
using AidlDisplayContentSample = aidl::android::hardware::graphics::composer3::DisplayContentSample;
using AidlDisplayAttribute = aidl::android::hardware::graphics::composer3::DisplayAttribute;
using AidlDisplayCapability = aidl::android::hardware::graphics::composer3::DisplayCapability;
using AidlHdrCapabilities = aidl::android::hardware::graphics::composer3::HdrCapabilities;
using AidlHdrConversionCapability =
        aidl::android::hardware::graphics::common::HdrConversionCapability;
using AidlHdrConversionStrategy = aidl::android::hardware::graphics::common::HdrConversionStrategy;
using AidlOverlayProperties = aidl::android::hardware::graphics::composer3::OverlayProperties;
using AidlPerFrameMetadata = aidl::android::hardware::graphics::composer3::PerFrameMetadata;
using AidlPerFrameMetadataKey = aidl::android::hardware::graphics::composer3::PerFrameMetadataKey;
using AidlPerFrameMetadataBlob = aidl::android::hardware::graphics::composer3::PerFrameMetadataBlob;
using AidlRenderIntent = aidl::android::hardware::graphics::composer3::RenderIntent;
using AidlVsyncPeriodChangeConstraints =
        aidl::android::hardware::graphics::composer3::VsyncPeriodChangeConstraints;
using AidlVsyncPeriodChangeTimeline =
        aidl::android::hardware::graphics::composer3::VsyncPeriodChangeTimeline;
using AidlDisplayContentSamplingAttributes =
        aidl::android::hardware::graphics::composer3::DisplayContentSamplingAttributes;
using AidlFormatColorComponent = aidl::android::hardware::graphics::composer3::FormatColorComponent;
using AidlDisplayConnectionType =
        aidl::android::hardware::graphics::composer3::DisplayConnectionType;

using AidlColorTransform = aidl::android::hardware::graphics::common::ColorTransform;
using AidlDataspace = aidl::android::hardware::graphics::common::Dataspace;
using AidlDisplayHotplugEvent = aidl::android::hardware::graphics::common::DisplayHotplugEvent;
using AidlFRect = aidl::android::hardware::graphics::common::FRect;
using AidlRect = aidl::android::hardware::graphics::common::Rect;
using AidlTransform = aidl::android::hardware::graphics::common::Transform;

namespace Hwc2 {

namespace {

template <typename To, typename From>
To translate(From x) {
    return static_cast<To>(x);
}

template <typename To, typename From>
std::vector<To> translate(const std::vector<From>& in) {
    std::vector<To> out;
    out.reserve(in.size());
    std::transform(in.begin(), in.end(), std::back_inserter(out),
                   [](From x) { return translate<To>(x); });
    return out;
}

template <>
AidlRect translate(IComposerClient::Rect x) {
    return AidlRect{
            .left = x.left,
            .top = x.top,
            .right = x.right,
            .bottom = x.bottom,
    };
}

template <>
AidlFRect translate(IComposerClient::FRect x) {
    return AidlFRect{
            .left = x.left,
            .top = x.top,
            .right = x.right,
            .bottom = x.bottom,
    };
}

template <>
AidlPerFrameMetadataBlob translate(IComposerClient::PerFrameMetadataBlob x) {
    AidlPerFrameMetadataBlob blob;
    blob.key = translate<AidlPerFrameMetadataKey>(x.key),
    std::copy(x.blob.begin(), x.blob.end(), std::inserter(blob.blob, blob.blob.end()));
    return blob;
}

template <>
AidlPerFrameMetadata translate(IComposerClient::PerFrameMetadata x) {
    return AidlPerFrameMetadata{
            .key = translate<AidlPerFrameMetadataKey>(x.key),
            .value = x.value,
    };
}

template <>
DisplayedFrameStats translate(AidlDisplayContentSample x) {
    return DisplayedFrameStats{
            .numFrames = static_cast<uint64_t>(x.frameCount),
            .component_0_sample = translate<uint64_t>(x.sampleComponent0),
            .component_1_sample = translate<uint64_t>(x.sampleComponent1),
            .component_2_sample = translate<uint64_t>(x.sampleComponent2),
            .component_3_sample = translate<uint64_t>(x.sampleComponent3),
    };
}

template <>
AidlVsyncPeriodChangeConstraints translate(IComposerClient::VsyncPeriodChangeConstraints x) {
    return AidlVsyncPeriodChangeConstraints{
            .desiredTimeNanos = x.desiredTimeNanos,
            .seamlessRequired = x.seamlessRequired,
    };
}

template <>
VsyncPeriodChangeTimeline translate(AidlVsyncPeriodChangeTimeline x) {
    return VsyncPeriodChangeTimeline{
            .newVsyncAppliedTimeNanos = x.newVsyncAppliedTimeNanos,
            .refreshRequired = x.refreshRequired,
            .refreshTimeNanos = x.refreshTimeNanos,
    };
}
mat4 makeMat4(std::vector<float> in) {
    return mat4(static_cast<const float*>(in.data()));
}

} // namespace

class AidlIComposerCallbackWrapper : public BnComposerCallback {
public:
    AidlIComposerCallbackWrapper(HWC2::ComposerCallback& callback) : mCallback(callback) {}

    ::ndk::ScopedAStatus onHotplug(int64_t in_display, bool in_connected) override {
        const auto event = in_connected ? AidlDisplayHotplugEvent::CONNECTED
                                        : AidlDisplayHotplugEvent::DISCONNECTED;
        mCallback.onComposerHalHotplugEvent(translate<Display>(in_display), event);
        return ::ndk::ScopedAStatus::ok();
    }

    ::ndk::ScopedAStatus onRefresh(int64_t in_display) override {
        mCallback.onComposerHalRefresh(translate<Display>(in_display));
        return ::ndk::ScopedAStatus::ok();
    }

    ::ndk::ScopedAStatus onSeamlessPossible(int64_t in_display) override {
        mCallback.onComposerHalSeamlessPossible(translate<Display>(in_display));
        return ::ndk::ScopedAStatus::ok();
    }

    ::ndk::ScopedAStatus onVsync(int64_t in_display, int64_t in_timestamp,
                                 int32_t in_vsyncPeriodNanos) override {
        mCallback.onComposerHalVsync(translate<Display>(in_display), in_timestamp,
                                     static_cast<uint32_t>(in_vsyncPeriodNanos));
        return ::ndk::ScopedAStatus::ok();
    }

    ::ndk::ScopedAStatus onVsyncPeriodTimingChanged(
            int64_t in_display, const AidlVsyncPeriodChangeTimeline& in_updatedTimeline) override {
        mCallback.onComposerHalVsyncPeriodTimingChanged(translate<Display>(in_display),
                                                        translate<V2_4::VsyncPeriodChangeTimeline>(
                                                                in_updatedTimeline));
        return ::ndk::ScopedAStatus::ok();
    }

    ::ndk::ScopedAStatus onVsyncIdle(int64_t in_display) override {
        mCallback.onComposerHalVsyncIdle(translate<Display>(in_display));
        return ::ndk::ScopedAStatus::ok();
    }

    ::ndk::ScopedAStatus onRefreshRateChangedDebug(
            const RefreshRateChangedDebugData& refreshRateChangedDebugData) override {
        mCallback.onRefreshRateChangedDebug(refreshRateChangedDebugData);
        return ::ndk::ScopedAStatus::ok();
    }

    ::ndk::ScopedAStatus onHotplugEvent(int64_t in_display,
                                        AidlDisplayHotplugEvent event) override {
        mCallback.onComposerHalHotplugEvent(translate<Display>(in_display), event);
        return ::ndk::ScopedAStatus::ok();
    }

private:
    HWC2::ComposerCallback& mCallback;
};

std::string AidlComposer::instance(const std::string& serviceName) {
    return std::string(AidlIComposer::descriptor) + "/" + serviceName;
}

bool AidlComposer::isDeclared(const std::string& serviceName) {
    return AServiceManager_isDeclared(instance(serviceName).c_str());
}

AidlComposer::AidlComposer(const std::string& serviceName) {
    // This only waits if the service is actually declared
    mAidlComposer = AidlIComposer::fromBinder(
            ndk::SpAIBinder(AServiceManager_waitForService(instance(serviceName).c_str())));
    if (!mAidlComposer) {
        LOG_ALWAYS_FATAL("Failed to get AIDL composer service");
        return;
    }

    if (!mAidlComposer->createClient(&mAidlComposerClient).isOk()) {
        LOG_ALWAYS_FATAL("Can't create AidlComposerClient, fallback to HIDL");
        return;
    }

    addReader(translate<Display>(kSingleReaderKey));

    // If unable to read interface version, then become backwards compatible.
    const auto status = mAidlComposerClient->getInterfaceVersion(&mComposerInterfaceVersion);
    if (!status.isOk()) {
        ALOGE("getInterfaceVersion for AidlComposer constructor failed %s",
              status.getDescription().c_str());
    }

    if (mComposerInterfaceVersion <= 1) {
        if (sysprop::clear_slots_with_set_layer_buffer(false)) {
            mClearSlotBuffer = sp<GraphicBuffer>::make(1, 1, PIXEL_FORMAT_RGBX_8888,
                                                       GraphicBuffer::USAGE_HW_COMPOSER |
                                                               GraphicBuffer::USAGE_SW_READ_OFTEN |
                                                               GraphicBuffer::USAGE_SW_WRITE_OFTEN,
                                                       "AidlComposer");
            if (!mClearSlotBuffer || mClearSlotBuffer->initCheck() != ::android::OK) {
                LOG_ALWAYS_FATAL("Failed to allocate a buffer for clearing layer buffer slots");
                return;
            }
        }
    }
    if (getLayerLifecycleBatchCommand()) {
        mEnableLayerCommandBatchingFlag =
                FlagManager::getInstance().enable_layer_command_batching();
    }
    ALOGI("Loaded AIDL composer3 HAL service");
}

AidlComposer::~AidlComposer() = default;

bool AidlComposer::isSupported(OptionalFeature feature) const {
    switch (feature) {
        case OptionalFeature::RefreshRateSwitching:
        case OptionalFeature::ExpectedPresentTime:
        case OptionalFeature::DisplayBrightnessCommand:
        case OptionalFeature::KernelIdleTimer:
        case OptionalFeature::PhysicalDisplayOrientation:
            return true;
    }
}

bool AidlComposer::isVrrSupported() const {
    return mComposerInterfaceVersion >= 3 && FlagManager::getInstance().vrr_config();
}

std::vector<Capability> AidlComposer::getCapabilities() {
    std::vector<Capability> capabilities;
    const auto status = mAidlComposer->getCapabilities(&capabilities);
    if (!status.isOk()) {
        ALOGE("getCapabilities failed %s", status.getDescription().c_str());
        return {};
    }
    return capabilities;
}

std::string AidlComposer::dumpDebugInfo() {
    int pipefds[2];
    int result = pipe(pipefds);
    if (result < 0) {
        ALOGE("dumpDebugInfo: pipe failed: %s", strerror(errno));
        return {};
    }

    std::string str;
    // Use other thread to read pipe to prevent
    // pipe is full, making HWC be blocked in writing.
    std::thread t([&]() {
        base::ReadFdToString(pipefds[0], &str);
    });
    const auto status = mAidlComposer->dump(pipefds[1], /*args*/ nullptr, /*numArgs*/ 0);
    // Close the write-end of the pipe to make sure that when reading from the
    // read-end we will get eof instead of blocking forever
    close(pipefds[1]);

    if (status != STATUS_OK) {
        ALOGE("dumpDebugInfo: dump failed: %d", status);
    }

    t.join();
    close(pipefds[0]);

    std::string hash;
    mAidlComposer->getInterfaceHash(&hash);
    return std::string(mAidlComposer->descriptor) +
            " version:" + std::to_string(mComposerInterfaceVersion) + " hash:" + hash + str;
}

void AidlComposer::registerCallback(HWC2::ComposerCallback& callback) {
    if (mAidlComposerCallback) {
        ALOGE("Callback already registered");
    }

    mAidlComposerCallback = ndk::SharedRefBase::make<AidlIComposerCallbackWrapper>(callback);

    ndk::SpAIBinder binder = mAidlComposerCallback->asBinder();
    AIBinder_setMinSchedulerPolicy(binder.get(), SCHED_FIFO, 2);

    const auto status = mAidlComposerClient->registerCallback(mAidlComposerCallback);
    if (!status.isOk()) {
        ALOGE("registerCallback failed %s", status.getDescription().c_str());
    }
}

Error AidlComposer::executeCommands(Display display) {
    mMutex.lock_shared();
    auto error = execute(display);
    mMutex.unlock_shared();
    return error;
}

uint32_t AidlComposer::getMaxVirtualDisplayCount() {
    int32_t count = 0;
    const auto status = mAidlComposerClient->getMaxVirtualDisplayCount(&count);
    if (!status.isOk()) {
        ALOGE("getMaxVirtualDisplayCount failed %s", status.getDescription().c_str());
        return 0;
    }
    return static_cast<uint32_t>(count);
}

Error AidlComposer::createVirtualDisplay(uint32_t width, uint32_t height, PixelFormat* format,
                                         Display* outDisplay) {
    using AidlPixelFormat = aidl::android::hardware::graphics::common::PixelFormat;
    const int32_t bufferSlotCount = 1;
    VirtualDisplay virtualDisplay;
    const auto status =
            mAidlComposerClient->createVirtualDisplay(static_cast<int32_t>(width),
                                                      static_cast<int32_t>(height),
                                                      static_cast<AidlPixelFormat>(*format),
                                                      bufferSlotCount, &virtualDisplay);

    if (!status.isOk()) {
        ALOGE("createVirtualDisplay failed %s", status.getDescription().c_str());
        return static_cast<Error>(status.getServiceSpecificError());
    }

    *outDisplay = translate<Display>(virtualDisplay.display);
    *format = static_cast<PixelFormat>(virtualDisplay.format);
    addDisplay(translate<Display>(virtualDisplay.display));
    return Error::NONE;
}

Error AidlComposer::destroyVirtualDisplay(Display display) {
    const auto status = mAidlComposerClient->destroyVirtualDisplay(translate<int64_t>(display));
    if (!status.isOk()) {
        ALOGE("destroyVirtualDisplay failed %s", status.getDescription().c_str());
        return static_cast<Error>(status.getServiceSpecificError());
    }
    removeDisplay(display);
    return Error::NONE;
}

Error AidlComposer::acceptDisplayChanges(Display display) {
    Error error = Error::NONE;
    mMutex.lock_shared();
    if (auto writer = getWriter(display)) {
        writer->get().acceptDisplayChanges(translate<int64_t>(display));
    } else {
        error = Error::BAD_DISPLAY;
    }
    mMutex.unlock_shared();
    return error;
}

Error AidlComposer::createLayer(Display display, Layer* outLayer) {
    int64_t layer;
    Error error = Error::NONE;
    if (!mEnableLayerCommandBatchingFlag) {
        const auto status = mAidlComposerClient->createLayer(translate<int64_t>(display),
                                                             kMaxLayerBufferCount, &layer);
        if (!status.isOk()) {
            ALOGE("createLayer failed %s", status.getDescription().c_str());
            return static_cast<Error>(status.getServiceSpecificError());
        }
    } else {
        // generate a unique layerID. map in AidlComposer with <SF_layerID, HWC_layerID>
        // Add this as a new displayCommand in execute command.
        // return the SF generated layerID instead of calling HWC
        layer = mLayerID++;
        mMutex.lock_shared();
        if (auto writer = getWriter(display)) {
            writer->get().setLayerLifecycleBatchCommandType(translate<int64_t>(display),
                                                            translate<int64_t>(layer),
                                                            LayerLifecycleBatchCommandType::CREATE);
            writer->get().setNewBufferSlotCount(translate<int64_t>(display),
                                                translate<int64_t>(layer), kMaxLayerBufferCount);
        } else {
            error = Error::BAD_DISPLAY;
        }
        mMutex.unlock_shared();
    }
    *outLayer = translate<Layer>(layer);
    return error;
}

Error AidlComposer::destroyLayer(Display display, Layer layer) {
    Error error = Error::NONE;
    if (!mEnableLayerCommandBatchingFlag) {
        const auto status = mAidlComposerClient->destroyLayer(translate<int64_t>(display),
                                                              translate<int64_t>(layer));
        if (!status.isOk()) {
            ALOGE("destroyLayer failed %s", status.getDescription().c_str());
            return static_cast<Error>(status.getServiceSpecificError());
        }
    } else {
        mMutex.lock_shared();
        if (auto writer = getWriter(display)) {
            writer->get()
                    .setLayerLifecycleBatchCommandType(translate<int64_t>(display),
                                                       translate<int64_t>(layer),
                                                       LayerLifecycleBatchCommandType::DESTROY);
        } else {
            error = Error::BAD_DISPLAY;
        }
        mMutex.unlock_shared();
    }

    return error;
}

Error AidlComposer::getActiveConfig(Display display, Config* outConfig) {
    int32_t config;
    const auto status = mAidlComposerClient->getActiveConfig(translate<int64_t>(display), &config);
    if (!status.isOk()) {
        ALOGE("getActiveConfig failed %s", status.getDescription().c_str());
        return static_cast<Error>(status.getServiceSpecificError());
    }
    *outConfig = translate<Config>(config);
    return Error::NONE;
}

Error AidlComposer::getChangedCompositionTypes(
        Display display, std::vector<Layer>* outLayers,
        std::vector<aidl::android::hardware::graphics::composer3::Composition>* outTypes) {
    std::vector<ChangedCompositionLayer> changedLayers;
    Error error = Error::NONE;
    {
        mMutex.lock_shared();
        if (auto reader = getReader(display)) {
            changedLayers = reader->get().takeChangedCompositionTypes(translate<int64_t>(display));
        } else {
            error = Error::BAD_DISPLAY;
        }
        mMutex.unlock_shared();
    }
    outLayers->reserve(changedLayers.size());
    outTypes->reserve(changedLayers.size());

    for (const auto& layer : changedLayers) {
        outLayers->emplace_back(translate<Layer>(layer.layer));
        outTypes->emplace_back(layer.composition);
    }
    return error;
}

Error AidlComposer::getColorModes(Display display, std::vector<ColorMode>* outModes) {
    std::vector<AidlColorMode> modes;
    const auto status = mAidlComposerClient->getColorModes(translate<int64_t>(display), &modes);
    if (!status.isOk()) {
        ALOGE("getColorModes failed %s", status.getDescription().c_str());
        return static_cast<Error>(status.getServiceSpecificError());
    }
    *outModes = translate<ColorMode>(modes);
    return Error::NONE;
}

Error AidlComposer::getDisplayAttribute(Display display, Config config,
                                        IComposerClient::Attribute attribute, int32_t* outValue) {
    const auto status =
            mAidlComposerClient->getDisplayAttribute(translate<int64_t>(display),
                                                     translate<int32_t>(config),
                                                     static_cast<AidlDisplayAttribute>(attribute),
                                                     outValue);
    if (!status.isOk()) {
        ALOGE("getDisplayAttribute failed %s", status.getDescription().c_str());
        return static_cast<Error>(status.getServiceSpecificError());
    }
    return Error::NONE;
}

Error AidlComposer::getDisplayConfigs(Display display, std::vector<Config>* outConfigs) {
    std::vector<int32_t> configs;
    const auto status =
            mAidlComposerClient->getDisplayConfigs(translate<int64_t>(display), &configs);
    if (!status.isOk()) {
        ALOGE("getDisplayConfigs failed %s", status.getDescription().c_str());
        return static_cast<Error>(status.getServiceSpecificError());
    }
    *outConfigs = translate<Config>(configs);
    return Error::NONE;
}

Error AidlComposer::getDisplayConfigurations(Display display, int32_t maxFrameIntervalNs,
                                             std::vector<DisplayConfiguration>* outConfigs) {
    const auto status =
            mAidlComposerClient->getDisplayConfigurations(translate<int64_t>(display),
                                                          maxFrameIntervalNs, outConfigs);
    if (!status.isOk()) {
        ALOGE("getDisplayConfigurations failed %s", status.getDescription().c_str());
        return static_cast<Error>(status.getServiceSpecificError());
    }

    return Error::NONE;
}

Error AidlComposer::getDisplayName(Display display, std::string* outName) {
    const auto status = mAidlComposerClient->getDisplayName(translate<int64_t>(display), outName);
    if (!status.isOk()) {
        ALOGE("getDisplayName failed %s", status.getDescription().c_str());
        return static_cast<Error>(status.getServiceSpecificError());
    }
    return Error::NONE;
}

Error AidlComposer::getDisplayRequests(Display display, uint32_t* outDisplayRequestMask,
                                       std::vector<Layer>* outLayers,
                                       std::vector<uint32_t>* outLayerRequestMasks) {
    Error error = Error::NONE;
    DisplayRequest displayRequests;
    {
        mMutex.lock_shared();
        if (auto reader = getReader(display)) {
            displayRequests = reader->get().takeDisplayRequests(translate<int64_t>(display));
        } else {
            error = Error::BAD_DISPLAY;
        }
        mMutex.unlock_shared();
    }
    *outDisplayRequestMask = translate<uint32_t>(displayRequests.mask);
    outLayers->reserve(displayRequests.layerRequests.size());
    outLayerRequestMasks->reserve(displayRequests.layerRequests.size());

    for (const auto& layer : displayRequests.layerRequests) {
        outLayers->emplace_back(translate<Layer>(layer.layer));
        outLayerRequestMasks->emplace_back(translate<uint32_t>(layer.mask));
    }
    return error;
}

Error AidlComposer::getDozeSupport(Display display, bool* outSupport) {
    std::vector<AidlDisplayCapability> capabilities;
    const auto status =
            mAidlComposerClient->getDisplayCapabilities(translate<int64_t>(display), &capabilities);
    if (!status.isOk()) {
        ALOGE("getDisplayCapabilities failed %s", status.getDescription().c_str());
        return static_cast<Error>(status.getServiceSpecificError());
    }
    *outSupport = std::find(capabilities.begin(), capabilities.end(),
                            AidlDisplayCapability::DOZE) != capabilities.end();
    return Error::NONE;
}

Error AidlComposer::hasDisplayIdleTimerCapability(Display display, bool* outSupport) {
    std::vector<AidlDisplayCapability> capabilities;
    const auto status =
            mAidlComposerClient->getDisplayCapabilities(translate<int64_t>(display), &capabilities);
    if (!status.isOk()) {
        ALOGE("getDisplayCapabilities failed %s", status.getDescription().c_str());
        return static_cast<Error>(status.getServiceSpecificError());
    }
    *outSupport = std::find(capabilities.begin(), capabilities.end(),
                            AidlDisplayCapability::DISPLAY_IDLE_TIMER) != capabilities.end();
    return Error::NONE;
}

Error AidlComposer::getHdrCapabilities(Display display, std::vector<Hdr>* outTypes,
                                       float* outMaxLuminance, float* outMaxAverageLuminance,
                                       float* outMinLuminance) {
    AidlHdrCapabilities capabilities;
    const auto status =
            mAidlComposerClient->getHdrCapabilities(translate<int64_t>(display), &capabilities);
    if (!status.isOk()) {
        ALOGE("getHdrCapabilities failed %s", status.getDescription().c_str());
        return static_cast<Error>(status.getServiceSpecificError());
    }

    *outTypes = capabilities.types;
    *outMaxLuminance = capabilities.maxLuminance;
    *outMaxAverageLuminance = capabilities.maxAverageLuminance;
    *outMinLuminance = capabilities.minLuminance;
    return Error::NONE;
}

bool AidlComposer::getLayerLifecycleBatchCommand() {
    std::vector<Capability> capabilities = getCapabilities();
    bool hasCapability = std::find(capabilities.begin(), capabilities.end(),
                                   Capability::LAYER_LIFECYCLE_BATCH_COMMAND) != capabilities.end();
    return hasCapability;
}

Error AidlComposer::getOverlaySupport(AidlOverlayProperties* outProperties) {
    const auto status = mAidlComposerClient->getOverlaySupport(outProperties);
    if (!status.isOk()) {
        ALOGE("getOverlaySupport failed %s", status.getDescription().c_str());
        return static_cast<Error>(status.getServiceSpecificError());
    }
    return Error::NONE;
}

Error AidlComposer::getReleaseFences(Display display, std::vector<Layer>* outLayers,
                                     std::vector<int>* outReleaseFences) {
    Error error = Error::NONE;
    std::vector<ReleaseFences::Layer> fences;
    {
        mMutex.lock_shared();
        if (auto reader = getReader(display)) {
            fences = reader->get().takeReleaseFences(translate<int64_t>(display));
        } else {
            error = Error::BAD_DISPLAY;
        }
        mMutex.unlock_shared();
    }
    outLayers->reserve(fences.size());
    outReleaseFences->reserve(fences.size());

    for (auto& fence : fences) {
        outLayers->emplace_back(translate<Layer>(fence.layer));
        // take ownership
        const int fenceOwner = fence.fence.get();
        *fence.fence.getR() = -1;
        outReleaseFences->emplace_back(fenceOwner);
    }
    return error;
}

Error AidlComposer::presentDisplay(Display display, int* outPresentFence) {
    const auto displayId = translate<int64_t>(display);
    ATRACE_FORMAT("HwcPresentDisplay %" PRId64, displayId);

    Error error = Error::NONE;
    mMutex.lock_shared();
    auto writer = getWriter(display);
    auto reader = getReader(display);
    if (writer && reader) {
        writer->get().presentDisplay(displayId);
        error = execute(display);
    } else {
        error = Error::BAD_DISPLAY;
    }

    if (error != Error::NONE) {
        mMutex.unlock_shared();
        return error;
    }

    auto fence = reader->get().takePresentFence(displayId);
    mMutex.unlock_shared();
    // take ownership
    *outPresentFence = fence.get();
    *fence.getR() = -1;
    return Error::NONE;
}

Error AidlComposer::setActiveConfig(Display display, Config config) {
    const auto status = mAidlComposerClient->setActiveConfig(translate<int64_t>(display),
                                                             translate<int32_t>(config));
    if (!status.isOk()) {
        ALOGE("setActiveConfig failed %s", status.getDescription().c_str());
        return static_cast<Error>(status.getServiceSpecificError());
    }
    return Error::NONE;
}

Error AidlComposer::setClientTarget(Display display, uint32_t slot, const sp<GraphicBuffer>& target,
                                    int acquireFence, Dataspace dataspace,
                                    const std::vector<IComposerClient::Rect>& damage,
                                    float hdrSdrRatio) {
    const native_handle_t* handle = nullptr;
    if (target.get()) {
        handle = target->getNativeBuffer()->handle;
    }

    Error error = Error::NONE;
    mMutex.lock_shared();
    if (auto writer = getWriter(display)) {
        writer->get()
                .setClientTarget(translate<int64_t>(display), slot, handle, acquireFence,
                                 translate<aidl::android::hardware::graphics::common::Dataspace>(
                                         dataspace),
                                 translate<AidlRect>(damage), hdrSdrRatio);
    } else {
        error = Error::BAD_DISPLAY;
    }
    mMutex.unlock_shared();
    return error;
}

Error AidlComposer::setColorMode(Display display, ColorMode mode, RenderIntent renderIntent) {
    const auto status =
            mAidlComposerClient->setColorMode(translate<int64_t>(display),
                                              translate<AidlColorMode>(mode),
                                              translate<AidlRenderIntent>(renderIntent));
    if (!status.isOk()) {
        ALOGE("setColorMode failed %s", status.getDescription().c_str());
        return static_cast<Error>(status.getServiceSpecificError());
    }
    return Error::NONE;
}

Error AidlComposer::setColorTransform(Display display, const float* matrix) {
    auto error = Error::NONE;
    mMutex.lock_shared();
    if (auto writer = getWriter(display)) {
        writer->get().setColorTransform(translate<int64_t>(display), matrix);
    } else {
        error = Error::BAD_DISPLAY;
    }
    mMutex.unlock_shared();
    return error;
}

Error AidlComposer::setOutputBuffer(Display display, const native_handle_t* buffer,
                                    int releaseFence) {
    auto error = Error::NONE;
    mMutex.lock_shared();
    if (auto writer = getWriter(display)) {
        writer->get().setOutputBuffer(translate<int64_t>(display), 0, buffer, dup(releaseFence));
    } else {
        error = Error::BAD_DISPLAY;
    }
    mMutex.unlock_shared();
    return error;
}

Error AidlComposer::setPowerMode(Display display, IComposerClient::PowerMode mode) {
    const auto status = mAidlComposerClient->setPowerMode(translate<int64_t>(display),
                                                          translate<PowerMode>(mode));
    if (!status.isOk()) {
        ALOGE("setPowerMode failed %s", status.getDescription().c_str());
        return static_cast<Error>(status.getServiceSpecificError());
    }
    return Error::NONE;
}

Error AidlComposer::setVsyncEnabled(Display display, IComposerClient::Vsync enabled) {
    const bool enableVsync = enabled == IComposerClient::Vsync::ENABLE;
    const auto status =
            mAidlComposerClient->setVsyncEnabled(translate<int64_t>(display), enableVsync);
    if (!status.isOk()) {
        ALOGE("setVsyncEnabled failed %s", status.getDescription().c_str());
        return static_cast<Error>(status.getServiceSpecificError());
    }
    return Error::NONE;
}

Error AidlComposer::setClientTargetSlotCount(Display display) {
    const int32_t bufferSlotCount = BufferQueue::NUM_BUFFER_SLOTS;
    const auto status = mAidlComposerClient->setClientTargetSlotCount(translate<int64_t>(display),
                                                                      bufferSlotCount);
    if (!status.isOk()) {
        ALOGE("setClientTargetSlotCount failed %s", status.getDescription().c_str());
        return static_cast<Error>(status.getServiceSpecificError());
    }
    return Error::NONE;
}

Error AidlComposer::validateDisplay(Display display, nsecs_t expectedPresentTime,
                                    int32_t frameIntervalNs, uint32_t* outNumTypes,
                                    uint32_t* outNumRequests) {
    const auto displayId = translate<int64_t>(display);
    ATRACE_FORMAT("HwcValidateDisplay %" PRId64, displayId);

    Error error = Error::NONE;
    mMutex.lock_shared();
    auto writer = getWriter(display);
    auto reader = getReader(display);
    if (writer && reader) {
        writer->get().validateDisplay(displayId, ClockMonotonicTimestamp{expectedPresentTime},
                                      frameIntervalNs);
        error = execute(display);
    } else {
        error = Error::BAD_DISPLAY;
    }

    if (error != Error::NONE) {
        mMutex.unlock_shared();
        return error;
    }

    reader->get().hasChanges(displayId, outNumTypes, outNumRequests);

    mMutex.unlock_shared();
    return Error::NONE;
}

Error AidlComposer::presentOrValidateDisplay(Display display, nsecs_t expectedPresentTime,
                                             int32_t frameIntervalNs, uint32_t* outNumTypes,
                                             uint32_t* outNumRequests, int* outPresentFence,
                                             uint32_t* state) {
    const auto displayId = translate<int64_t>(display);
    ATRACE_FORMAT("HwcPresentOrValidateDisplay %" PRId64, displayId);

    Error error = Error::NONE;
    mMutex.lock_shared();
    auto writer = getWriter(display);
    auto reader = getReader(display);
    if (writer && reader) {
        writer->get().presentOrvalidateDisplay(displayId,
                                               ClockMonotonicTimestamp{expectedPresentTime},
                                               frameIntervalNs);
        error = execute(display);
    } else {
        error = Error::BAD_DISPLAY;
    }

    if (error != Error::NONE) {
        mMutex.unlock_shared();
        return error;
    }

    const auto result = reader->get().takePresentOrValidateStage(displayId);
    if (!result.has_value()) {
        *state = translate<uint32_t>(-1);
        mMutex.unlock_shared();
        return Error::NO_RESOURCES;
    }

    *state = translate<uint32_t>(*result);

    if (*result == PresentOrValidate::Result::Presented) {
        auto fence = reader->get().takePresentFence(displayId);
        // take ownership
        *outPresentFence = fence.get();
        *fence.getR() = -1;
    }

    if (*result == PresentOrValidate::Result::Validated) {
        reader->get().hasChanges(displayId, outNumTypes, outNumRequests);
    }

    mMutex.unlock_shared();
    return Error::NONE;
}

Error AidlComposer::setCursorPosition(Display display, Layer layer, int32_t x, int32_t y) {
    Error error = Error::NONE;
    mMutex.lock_shared();
    if (auto writer = getWriter(display)) {
        writer->get().setLayerCursorPosition(translate<int64_t>(display), translate<int64_t>(layer),
                                             x, y);
    } else {
        error = Error::BAD_DISPLAY;
    }
    mMutex.unlock_shared();
    return error;
}

Error AidlComposer::setLayerBuffer(Display display, Layer layer, uint32_t slot,
                                   const sp<GraphicBuffer>& buffer, int acquireFence) {
    const native_handle_t* handle = nullptr;
    if (buffer.get()) {
        handle = buffer->getNativeBuffer()->handle;
    }

    Error error = Error::NONE;
    mMutex.lock_shared();
    if (auto writer = getWriter(display)) {
        writer->get().setLayerBuffer(translate<int64_t>(display), translate<int64_t>(layer), slot,
                                     handle, acquireFence);
    } else {
        error = Error::BAD_DISPLAY;
    }
    mMutex.unlock_shared();
    return error;
}

Error AidlComposer::setLayerBufferSlotsToClear(Display display, Layer layer,
                                               const std::vector<uint32_t>& slotsToClear,
                                               uint32_t activeBufferSlot) {
    if (slotsToClear.empty()) {
        return Error::NONE;
    }

    Error error = Error::NONE;
    mMutex.lock_shared();
    if (auto writer = getWriter(display)) {
        if (mComposerInterfaceVersion > 1) {
            writer->get().setLayerBufferSlotsToClear(translate<int64_t>(display),
                                                     translate<int64_t>(layer), slotsToClear);
            // Backwards compatible way of clearing buffer slots is to set the layer buffer with a
            // placeholder buffer, using the slot that needs to cleared... tricky.
        } else if (mClearSlotBuffer != nullptr) {
            for (uint32_t slot : slotsToClear) {
                // Don't clear the active buffer slot because we need to restore the active buffer
                // after clearing the requested buffer slots with a placeholder buffer.
                if (slot != activeBufferSlot) {
                    writer->get().setLayerBufferWithNewCommand(translate<int64_t>(display),
                                                               translate<int64_t>(layer), slot,
                                                               mClearSlotBuffer->handle,
                                                               /*fence*/ -1);
                }
            }
            // Since we clear buffers by setting them to a placeholder buffer, we want to make
            // sure that the last setLayerBuffer command is sent with the currently active
            // buffer, not the placeholder buffer, so that there is no perceptual change when
            // buffers are discarded.
            writer->get().setLayerBufferWithNewCommand(translate<int64_t>(display),
                                                       translate<int64_t>(layer), activeBufferSlot,
                                                       // The active buffer is still cached in
                                                       // its slot and doesn't need a fence.
                                                       /*buffer*/ nullptr, /*fence*/ -1);
        }
    } else {
        error = Error::BAD_DISPLAY;
    }
    mMutex.unlock_shared();
    return error;
}

Error AidlComposer::setLayerSurfaceDamage(Display display, Layer layer,
                                          const std::vector<IComposerClient::Rect>& damage) {
    Error error = Error::NONE;
    mMutex.lock_shared();
    if (auto writer = getWriter(display)) {
        writer->get().setLayerSurfaceDamage(translate<int64_t>(display), translate<int64_t>(layer),
                                            translate<AidlRect>(damage));
    } else {
        error = Error::BAD_DISPLAY;
    }
    mMutex.unlock_shared();
    return error;
}

Error AidlComposer::setLayerBlendMode(Display display, Layer layer,
                                      IComposerClient::BlendMode mode) {
    Error error = Error::NONE;
    mMutex.lock_shared();
    if (auto writer = getWriter(display)) {
        writer->get().setLayerBlendMode(translate<int64_t>(display), translate<int64_t>(layer),
                                        translate<BlendMode>(mode));
    } else {
        error = Error::BAD_DISPLAY;
    }
    mMutex.unlock_shared();
    return error;
}

Error AidlComposer::setLayerColor(Display display, Layer layer, const Color& color) {
    Error error = Error::NONE;
    mMutex.lock_shared();
    if (auto writer = getWriter(display)) {
        writer->get().setLayerColor(translate<int64_t>(display), translate<int64_t>(layer), color);
    } else {
        error = Error::BAD_DISPLAY;
    }
    mMutex.unlock_shared();
    return error;
}

Error AidlComposer::setLayerCompositionType(
        Display display, Layer layer,
        aidl::android::hardware::graphics::composer3::Composition type) {
    Error error = Error::NONE;
    mMutex.lock_shared();
    if (auto writer = getWriter(display)) {
        writer->get().setLayerCompositionType(translate<int64_t>(display),
                                              translate<int64_t>(layer), type);
    } else {
        error = Error::BAD_DISPLAY;
    }
    mMutex.unlock_shared();
    return error;
}

Error AidlComposer::setLayerDataspace(Display display, Layer layer, Dataspace dataspace) {
    Error error = Error::NONE;
    mMutex.lock_shared();
    if (auto writer = getWriter(display)) {
        writer->get().setLayerDataspace(translate<int64_t>(display), translate<int64_t>(layer),
                                        translate<AidlDataspace>(dataspace));
    } else {
        error = Error::BAD_DISPLAY;
    }
    mMutex.unlock_shared();
    return error;
}

Error AidlComposer::setLayerDisplayFrame(Display display, Layer layer,
                                         const IComposerClient::Rect& frame) {
    Error error = Error::NONE;
    mMutex.lock_shared();
    if (auto writer = getWriter(display)) {
        writer->get().setLayerDisplayFrame(translate<int64_t>(display), translate<int64_t>(layer),
                                           translate<AidlRect>(frame));
    } else {
        error = Error::BAD_DISPLAY;
    }
    mMutex.unlock_shared();
    return error;
}

Error AidlComposer::setLayerPlaneAlpha(Display display, Layer layer, float alpha) {
    Error error = Error::NONE;
    mMutex.lock_shared();
    if (auto writer = getWriter(display)) {
        writer->get().setLayerPlaneAlpha(translate<int64_t>(display), translate<int64_t>(layer),
                                         alpha);
    } else {
        error = Error::BAD_DISPLAY;
    }
    mMutex.unlock_shared();
    return error;
}

Error AidlComposer::setLayerSidebandStream(Display display, Layer layer,
                                           const native_handle_t* stream) {
    Error error = Error::NONE;
    mMutex.lock_shared();
    if (auto writer = getWriter(display)) {
        writer->get().setLayerSidebandStream(translate<int64_t>(display), translate<int64_t>(layer),
                                             stream);
    } else {
        error = Error::BAD_DISPLAY;
    }
    mMutex.unlock_shared();
    return error;
}

Error AidlComposer::setLayerSourceCrop(Display display, Layer layer,
                                       const IComposerClient::FRect& crop) {
    Error error = Error::NONE;
    mMutex.lock_shared();
    if (auto writer = getWriter(display)) {
        writer->get().setLayerSourceCrop(translate<int64_t>(display), translate<int64_t>(layer),
                                         translate<AidlFRect>(crop));
    } else {
        error = Error::BAD_DISPLAY;
    }
    mMutex.unlock_shared();
    return error;
}

Error AidlComposer::setLayerTransform(Display display, Layer layer, Transform transform) {
    Error error = Error::NONE;
    mMutex.lock_shared();
    if (auto writer = getWriter(display)) {
        writer->get().setLayerTransform(translate<int64_t>(display), translate<int64_t>(layer),
                                        translate<AidlTransform>(transform));
    } else {
        error = Error::BAD_DISPLAY;
    }
    mMutex.unlock_shared();
    return error;
}

Error AidlComposer::setLayerVisibleRegion(Display display, Layer layer,
                                          const std::vector<IComposerClient::Rect>& visible) {
    Error error = Error::NONE;
    mMutex.lock_shared();
    if (auto writer = getWriter(display)) {
        writer->get().setLayerVisibleRegion(translate<int64_t>(display), translate<int64_t>(layer),
                                            translate<AidlRect>(visible));
    } else {
        error = Error::BAD_DISPLAY;
    }
    mMutex.unlock_shared();
    return error;
}

Error AidlComposer::setLayerZOrder(Display display, Layer layer, uint32_t z) {
    Error error = Error::NONE;
    mMutex.lock_shared();
    if (auto writer = getWriter(display)) {
        writer->get().setLayerZOrder(translate<int64_t>(display), translate<int64_t>(layer), z);
    } else {
        error = Error::BAD_DISPLAY;
    }
    mMutex.unlock_shared();
    return error;
}

Error AidlComposer::execute(Display display) {
    auto writer = getWriter(display);
    auto reader = getReader(display);
    if (!writer || !reader) {
        return Error::BAD_DISPLAY;
    }

    auto commands = writer->get().takePendingCommands();
    if (commands.empty()) {
        return Error::NONE;
    }

    { // scope for results
        std::vector<CommandResultPayload> results;
        auto status = mAidlComposerClient->executeCommands(commands, &results);
        if (!status.isOk()) {
            ALOGE("executeCommands failed %s", status.getDescription().c_str());
            return static_cast<Error>(status.getServiceSpecificError());
        }

        reader->get().parse(std::move(results));
    }
    const auto commandErrors = reader->get().takeErrors();
    Error error = Error::NONE;
    for (const auto& cmdErr : commandErrors) {
        const auto index = static_cast<size_t>(cmdErr.commandIndex);
        if (index < 0 || index >= commands.size()) {
            ALOGE("invalid command index %zu", index);
            return Error::BAD_PARAMETER;
        }

        const auto& command = commands[index];
        if (command.validateDisplay || command.presentDisplay || command.presentOrValidateDisplay) {
            error = translate<Error>(cmdErr.errorCode);
        } else {
            ALOGW("command '%s' generated error %" PRId32, command.toString().c_str(),
                  cmdErr.errorCode);
        }
    }

    return error;
}

Error AidlComposer::setLayerPerFrameMetadata(
        Display display, Layer layer,
        const std::vector<IComposerClient::PerFrameMetadata>& perFrameMetadatas) {
    Error error = Error::NONE;
    mMutex.lock_shared();
    if (auto writer = getWriter(display)) {
        writer->get().setLayerPerFrameMetadata(translate<int64_t>(display),
                                               translate<int64_t>(layer),
                                               translate<AidlPerFrameMetadata>(perFrameMetadatas));
    } else {
        error = Error::BAD_DISPLAY;
    }
    mMutex.unlock_shared();
    return error;
}

std::vector<IComposerClient::PerFrameMetadataKey> AidlComposer::getPerFrameMetadataKeys(
        Display display) {
    std::vector<AidlPerFrameMetadataKey> keys;
    const auto status =
            mAidlComposerClient->getPerFrameMetadataKeys(translate<int64_t>(display), &keys);
    if (!status.isOk()) {
        ALOGE("getPerFrameMetadataKeys failed %s", status.getDescription().c_str());
        return {};
    }
    return translate<IComposerClient::PerFrameMetadataKey>(keys);
}

Error AidlComposer::getRenderIntents(Display display, ColorMode colorMode,
                                     std::vector<RenderIntent>* outRenderIntents) {
    std::vector<AidlRenderIntent> renderIntents;
    const auto status = mAidlComposerClient->getRenderIntents(translate<int64_t>(display),
                                                              translate<AidlColorMode>(colorMode),
                                                              &renderIntents);
    if (!status.isOk()) {
        ALOGE("getRenderIntents failed %s", status.getDescription().c_str());
        return static_cast<Error>(status.getServiceSpecificError());
    }
    *outRenderIntents = translate<RenderIntent>(renderIntents);
    return Error::NONE;
}

Error AidlComposer::getDataspaceSaturationMatrix(Dataspace dataspace, mat4* outMatrix) {
    std::vector<float> matrix;
    const auto status =
            mAidlComposerClient->getDataspaceSaturationMatrix(translate<AidlDataspace>(dataspace),
                                                              &matrix);
    if (!status.isOk()) {
        ALOGE("getDataspaceSaturationMatrix failed %s", status.getDescription().c_str());
        return static_cast<Error>(status.getServiceSpecificError());
    }
    *outMatrix = makeMat4(matrix);
    return Error::NONE;
}

Error AidlComposer::getDisplayIdentificationData(Display display, uint8_t* outPort,
                                                 std::vector<uint8_t>* outData) {
    AidlDisplayIdentification displayIdentification;
    const auto status =
            mAidlComposerClient->getDisplayIdentificationData(translate<int64_t>(display),
                                                              &displayIdentification);
    if (!status.isOk()) {
        ALOGE("getDisplayIdentificationData failed %s", status.getDescription().c_str());
        return static_cast<Error>(status.getServiceSpecificError());
    }

    *outPort = static_cast<uint8_t>(displayIdentification.port);
    *outData = displayIdentification.data;

    return Error::NONE;
}

Error AidlComposer::setLayerColorTransform(Display display, Layer layer, const float* matrix) {
    Error error = Error::NONE;
    mMutex.lock_shared();
    if (auto writer = getWriter(display)) {
        writer->get().setLayerColorTransform(translate<int64_t>(display), translate<int64_t>(layer),
                                             matrix);
    } else {
        error = Error::BAD_DISPLAY;
    }
    mMutex.unlock_shared();
    return error;
}

Error AidlComposer::getDisplayedContentSamplingAttributes(Display display, PixelFormat* outFormat,
                                                          Dataspace* outDataspace,
                                                          uint8_t* outComponentMask) {
    if (!outFormat || !outDataspace || !outComponentMask) {
        return Error::BAD_PARAMETER;
    }

    AidlDisplayContentSamplingAttributes attributes;
    const auto status =
            mAidlComposerClient->getDisplayedContentSamplingAttributes(translate<int64_t>(display),
                                                                       &attributes);
    if (!status.isOk()) {
        ALOGE("getDisplayedContentSamplingAttributes failed %s", status.getDescription().c_str());
        return static_cast<Error>(status.getServiceSpecificError());
    }

    *outFormat = translate<PixelFormat>(attributes.format);
    *outDataspace = translate<Dataspace>(attributes.dataspace);
    *outComponentMask = static_cast<uint8_t>(attributes.componentMask);
    return Error::NONE;
}

Error AidlComposer::setDisplayContentSamplingEnabled(Display display, bool enabled,
                                                     uint8_t componentMask, uint64_t maxFrames) {
    const auto status =
            mAidlComposerClient
                    ->setDisplayedContentSamplingEnabled(translate<int64_t>(display), enabled,
                                                         static_cast<AidlFormatColorComponent>(
                                                                 componentMask),
                                                         static_cast<int64_t>(maxFrames));
    if (!status.isOk()) {
        ALOGE("setDisplayedContentSamplingEnabled failed %s", status.getDescription().c_str());
        return static_cast<Error>(status.getServiceSpecificError());
    }
    return Error::NONE;
}

Error AidlComposer::getDisplayedContentSample(Display display, uint64_t maxFrames,
                                              uint64_t timestamp, DisplayedFrameStats* outStats) {
    if (!outStats) {
        return Error::BAD_PARAMETER;
    }

    AidlDisplayContentSample sample;
    const auto status =
            mAidlComposerClient->getDisplayedContentSample(translate<int64_t>(display),
                                                           static_cast<int64_t>(maxFrames),
                                                           static_cast<int64_t>(timestamp),
                                                           &sample);
    if (!status.isOk()) {
        ALOGE("getDisplayedContentSample failed %s", status.getDescription().c_str());
        return static_cast<Error>(status.getServiceSpecificError());
    }
    *outStats = translate<DisplayedFrameStats>(sample);
    return Error::NONE;
}

Error AidlComposer::setLayerPerFrameMetadataBlobs(
        Display display, Layer layer,
        const std::vector<IComposerClient::PerFrameMetadataBlob>& metadata) {
    Error error = Error::NONE;
    mMutex.lock_shared();
    if (auto writer = getWriter(display)) {
        writer->get().setLayerPerFrameMetadataBlobs(translate<int64_t>(display),
                                                    translate<int64_t>(layer),
                                                    translate<AidlPerFrameMetadataBlob>(metadata));
    } else {
        error = Error::BAD_DISPLAY;
    }
    mMutex.unlock_shared();
    return error;
}

Error AidlComposer::setDisplayBrightness(Display display, float brightness, float brightnessNits,
                                         const DisplayBrightnessOptions& options) {
    Error error = Error::NONE;
    mMutex.lock_shared();
    if (auto writer = getWriter(display)) {
        writer->get().setDisplayBrightness(translate<int64_t>(display), brightness, brightnessNits);

        if (options.applyImmediately) {
            error = execute(display);
            mMutex.unlock_shared();
            return error;
        }
    } else {
        error = Error::BAD_DISPLAY;
    }
    mMutex.unlock_shared();
    return error;
}

Error AidlComposer::getDisplayCapabilities(Display display,
                                           std::vector<AidlDisplayCapability>* outCapabilities) {
    const auto status = mAidlComposerClient->getDisplayCapabilities(translate<int64_t>(display),
                                                                    outCapabilities);
    if (!status.isOk()) {
        ALOGE("getDisplayCapabilities failed %s", status.getDescription().c_str());
        outCapabilities->clear();
        return static_cast<Error>(status.getServiceSpecificError());
    }
    return Error::NONE;
}

V2_4::Error AidlComposer::getDisplayConnectionType(
        Display display, IComposerClient::DisplayConnectionType* outType) {
    AidlDisplayConnectionType type;
    const auto status =
            mAidlComposerClient->getDisplayConnectionType(translate<int64_t>(display), &type);
    if (!status.isOk()) {
        ALOGE("getDisplayConnectionType failed %s", status.getDescription().c_str());
        return static_cast<V2_4::Error>(status.getServiceSpecificError());
    }
    *outType = translate<IComposerClient::DisplayConnectionType>(type);
    return V2_4::Error::NONE;
}

V2_4::Error AidlComposer::getDisplayVsyncPeriod(Display display, VsyncPeriodNanos* outVsyncPeriod) {
    int32_t vsyncPeriod;
    const auto status =
            mAidlComposerClient->getDisplayVsyncPeriod(translate<int64_t>(display), &vsyncPeriod);
    if (!status.isOk()) {
        ALOGE("getDisplayVsyncPeriod failed %s", status.getDescription().c_str());
        return static_cast<V2_4::Error>(status.getServiceSpecificError());
    }
    *outVsyncPeriod = translate<VsyncPeriodNanos>(vsyncPeriod);
    return V2_4::Error::NONE;
}

V2_4::Error AidlComposer::setActiveConfigWithConstraints(
        Display display, Config config,
        const IComposerClient::VsyncPeriodChangeConstraints& vsyncPeriodChangeConstraints,
        VsyncPeriodChangeTimeline* outTimeline) {
    AidlVsyncPeriodChangeTimeline timeline;
    const auto status =
            mAidlComposerClient
                    ->setActiveConfigWithConstraints(translate<int64_t>(display),
                                                     translate<int32_t>(config),
                                                     translate<AidlVsyncPeriodChangeConstraints>(
                                                             vsyncPeriodChangeConstraints),
                                                     &timeline);
    if (!status.isOk()) {
        ALOGE("setActiveConfigWithConstraints failed %s", status.getDescription().c_str());
        return static_cast<V2_4::Error>(status.getServiceSpecificError());
    }
    *outTimeline = translate<VsyncPeriodChangeTimeline>(timeline);
    return V2_4::Error::NONE;
}

V2_4::Error AidlComposer::setAutoLowLatencyMode(Display display, bool on) {
    const auto status = mAidlComposerClient->setAutoLowLatencyMode(translate<int64_t>(display), on);
    if (!status.isOk()) {
        ALOGE("setAutoLowLatencyMode failed %s", status.getDescription().c_str());
        return static_cast<V2_4::Error>(status.getServiceSpecificError());
    }
    return V2_4::Error::NONE;
}

V2_4::Error AidlComposer::getSupportedContentTypes(
        Display displayId, std::vector<IComposerClient::ContentType>* outSupportedContentTypes) {
    std::vector<AidlContentType> types;
    const auto status =
            mAidlComposerClient->getSupportedContentTypes(translate<int64_t>(displayId), &types);
    if (!status.isOk()) {
        ALOGE("getSupportedContentTypes failed %s", status.getDescription().c_str());
        return static_cast<V2_4::Error>(status.getServiceSpecificError());
    }
    *outSupportedContentTypes = translate<IComposerClient::ContentType>(types);
    return V2_4::Error::NONE;
}

V2_4::Error AidlComposer::setContentType(Display display,
                                         IComposerClient::ContentType contentType) {
    const auto status =
            mAidlComposerClient->setContentType(translate<int64_t>(display),
                                                translate<AidlContentType>(contentType));
    if (!status.isOk()) {
        ALOGE("setContentType failed %s", status.getDescription().c_str());
        return static_cast<V2_4::Error>(status.getServiceSpecificError());
    }
    return V2_4::Error::NONE;
}

V2_4::Error AidlComposer::setLayerGenericMetadata(Display, Layer, const std::string&, bool,
                                                  const std::vector<uint8_t>&) {
    // There are no users for this API. See b/209691612.
    return V2_4::Error::UNSUPPORTED;
}

V2_4::Error AidlComposer::getLayerGenericMetadataKeys(
        std::vector<IComposerClient::LayerGenericMetadataKey>*) {
    // There are no users for this API. See b/209691612.
    return V2_4::Error::UNSUPPORTED;
}

Error AidlComposer::setBootDisplayConfig(Display display, Config config) {
    const auto status = mAidlComposerClient->setBootDisplayConfig(translate<int64_t>(display),
                                                                  translate<int32_t>(config));
    if (!status.isOk()) {
        ALOGE("setBootDisplayConfig failed %s", status.getDescription().c_str());
        return static_cast<Error>(status.getServiceSpecificError());
    }
    return Error::NONE;
}

Error AidlComposer::clearBootDisplayConfig(Display display) {
    const auto status = mAidlComposerClient->clearBootDisplayConfig(translate<int64_t>(display));
    if (!status.isOk()) {
        ALOGE("clearBootDisplayConfig failed %s", status.getDescription().c_str());
        return static_cast<Error>(status.getServiceSpecificError());
    }
    return Error::NONE;
}

Error AidlComposer::getPreferredBootDisplayConfig(Display display, Config* config) {
    int32_t displayConfig;
    const auto status =
            mAidlComposerClient->getPreferredBootDisplayConfig(translate<int64_t>(display),
                                                               &displayConfig);
    if (!status.isOk()) {
        ALOGE("getPreferredBootDisplayConfig failed %s", status.getDescription().c_str());
        return static_cast<Error>(status.getServiceSpecificError());
    }
    *config = translate<uint32_t>(displayConfig);
    return Error::NONE;
}

Error AidlComposer::getHdrConversionCapabilities(
        std::vector<AidlHdrConversionCapability>* hdrConversionCapabilities) {
    const auto status =
            mAidlComposerClient->getHdrConversionCapabilities(hdrConversionCapabilities);
    if (!status.isOk()) {
        hdrConversionCapabilities = {};
        ALOGE("getHdrConversionCapabilities failed %s", status.getDescription().c_str());
        return static_cast<Error>(status.getServiceSpecificError());
    }
    return Error::NONE;
}

Error AidlComposer::setHdrConversionStrategy(AidlHdrConversionStrategy hdrConversionStrategy,
                                             Hdr* outPreferredHdrOutputType) {
    const auto status = mAidlComposerClient->setHdrConversionStrategy(hdrConversionStrategy,
                                                                      outPreferredHdrOutputType);
    if (!status.isOk()) {
        ALOGE("setHdrConversionStrategy failed %s", status.getDescription().c_str());
        return static_cast<Error>(status.getServiceSpecificError());
    }
    return Error::NONE;
}

Error AidlComposer::setRefreshRateChangedCallbackDebugEnabled(Display displayId, bool enabled) {
    const auto status =
            mAidlComposerClient->setRefreshRateChangedCallbackDebugEnabled(translate<int64_t>(
                                                                                   displayId),
                                                                           enabled);
    if (!status.isOk()) {
        ALOGE("setRefreshRateChangedCallbackDebugEnabled failed %s",
              status.getDescription().c_str());
        return static_cast<Error>(status.getServiceSpecificError());
    }
    return Error::NONE;
}

Error AidlComposer::notifyExpectedPresent(Display displayId, nsecs_t expectedPresentTime,
                                          int32_t frameIntervalNs) {
    const auto status =
            mAidlComposerClient->notifyExpectedPresent(translate<int64_t>(displayId),
                                                       ClockMonotonicTimestamp{expectedPresentTime},
                                                       frameIntervalNs);

    if (!status.isOk()) {
        ALOGE("notifyExpectedPresent failed %s", status.getDescription().c_str());
        return static_cast<Error>(status.getServiceSpecificError());
    }
    return Error::NONE;
}

Error AidlComposer::getClientTargetProperty(
        Display display, ClientTargetPropertyWithBrightness* outClientTargetProperty) {
    Error error = Error::NONE;
    mMutex.lock_shared();
    if (auto reader = getReader(display)) {
        *outClientTargetProperty =
                reader->get().takeClientTargetProperty(translate<int64_t>(display));
    } else {
        error = Error::BAD_DISPLAY;
    }
    mMutex.unlock_shared();
    return error;
}

Error AidlComposer::setLayerBrightness(Display display, Layer layer, float brightness) {
    Error error = Error::NONE;
    mMutex.lock_shared();
    if (auto writer = getWriter(display)) {
        writer->get().setLayerBrightness(translate<int64_t>(display), translate<int64_t>(layer),
                                         brightness);
    } else {
        error = Error::BAD_DISPLAY;
    }
    mMutex.unlock_shared();
    return error;
}

Error AidlComposer::setLayerBlockingRegion(Display display, Layer layer,
                                           const std::vector<IComposerClient::Rect>& blocking) {
    Error error = Error::NONE;
    mMutex.lock_shared();
    if (auto writer = getWriter(display)) {
        writer->get().setLayerBlockingRegion(translate<int64_t>(display), translate<int64_t>(layer),
                                             translate<AidlRect>(blocking));
    } else {
        error = Error::BAD_DISPLAY;
    }
    mMutex.unlock_shared();
    return error;
}

Error AidlComposer::getDisplayDecorationSupport(Display display,
                                                std::optional<DisplayDecorationSupport>* support) {
    const auto status =
            mAidlComposerClient->getDisplayDecorationSupport(translate<int64_t>(display), support);
    if (!status.isOk()) {
        ALOGE("getDisplayDecorationSupport failed %s", status.getDescription().c_str());
        support->reset();
        return static_cast<Error>(status.getServiceSpecificError());
    }
    return Error::NONE;
}

Error AidlComposer::setIdleTimerEnabled(Display displayId, std::chrono::milliseconds timeout) {
    const auto status =
            mAidlComposerClient->setIdleTimerEnabled(translate<int64_t>(displayId),
                                                     translate<int32_t>(timeout.count()));
    if (!status.isOk()) {
        ALOGE("setIdleTimerEnabled failed %s", status.getDescription().c_str());
        return static_cast<Error>(status.getServiceSpecificError());
    }
    return Error::NONE;
}

Error AidlComposer::getPhysicalDisplayOrientation(Display displayId,
                                                  AidlTransform* outDisplayOrientation) {
    const auto status =
            mAidlComposerClient->getDisplayPhysicalOrientation(translate<int64_t>(displayId),
                                                               outDisplayOrientation);
    if (!status.isOk()) {
        ALOGE("getPhysicalDisplayOrientation failed %s", status.getDescription().c_str());
        return static_cast<Error>(status.getServiceSpecificError());
    }
    return Error::NONE;
}

ftl::Optional<std::reference_wrapper<ComposerClientWriter>> AidlComposer::getWriter(Display display)
        REQUIRES_SHARED(mMutex) {
    return mWriters.get(display);
}

ftl::Optional<std::reference_wrapper<ComposerClientReader>> AidlComposer::getReader(Display display)
        REQUIRES_SHARED(mMutex) {
    if (mSingleReader) {
        display = translate<Display>(kSingleReaderKey);
    }
    return mReaders.get(display);
}

void AidlComposer::removeDisplay(Display display) {
    mMutex.lock();
    bool wasErased = mWriters.erase(display);
    ALOGW_IF(!wasErased,
             "Attempting to remove writer for display %" PRId64 " which is not connected",
             translate<int64_t>(display));
    if (!mSingleReader) {
        removeReader(display);
    }
    mMutex.unlock();
}

void AidlComposer::onHotplugDisconnect(Display display) {
    removeDisplay(display);
}

bool AidlComposer::hasMultiThreadedPresentSupport(Display display) {
    if (!FlagManager::getInstance().multithreaded_present()) return false;
    const auto displayId = translate<int64_t>(display);
    std::vector<AidlDisplayCapability> capabilities;
    const auto status = mAidlComposerClient->getDisplayCapabilities(displayId, &capabilities);
    if (!status.isOk()) {
        ALOGE("getDisplayCapabilities failed %s", status.getDescription().c_str());
        return false;
    }
    return std::find(capabilities.begin(), capabilities.end(),
                     AidlDisplayCapability::MULTI_THREADED_PRESENT) != capabilities.end();
}

void AidlComposer::addReader(Display display) {
    const auto displayId = translate<int64_t>(display);
    std::optional<int64_t> displayOpt;
    if (displayId != kSingleReaderKey) {
        displayOpt.emplace(displayId);
    }
    auto [it, added] = mReaders.try_emplace(display, std::move(displayOpt));
    ALOGW_IF(!added, "Attempting to add writer for display %" PRId64 " which is already connected",
             displayId);
}

void AidlComposer::removeReader(Display display) {
    bool wasErased = mReaders.erase(display);
    ALOGW_IF(!wasErased,
             "Attempting to remove reader for display %" PRId64 " which is not connected",
             translate<int64_t>(display));
}

void AidlComposer::addDisplay(Display display) {
    const auto displayId = translate<int64_t>(display);
    mMutex.lock();
    auto [it, added] = mWriters.try_emplace(display, displayId);
    ALOGW_IF(!added, "Attempting to add writer for display %" PRId64 " which is already connected",
             displayId);
    if (mSingleReader) {
        if (hasMultiThreadedPresentSupport(display)) {
            mSingleReader = false;
            removeReader(translate<Display>(kSingleReaderKey));
            // Note that this includes the new display.
            for (const auto& [existingDisplay, _] : mWriters) {
                addReader(existingDisplay);
            }
        }
    } else {
        addReader(display);
    }
    mMutex.unlock();
}

void AidlComposer::onHotplugConnect(Display display) {
    addDisplay(display);
}
} // namespace Hwc2
} // namespace android
