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

#include "ComposerClient.h"

#include <aidlcommonsupport/NativeHandle.h>
#include <android/binder_ibinder_platform.h>

#include "Common.h"
#include "Device.h"
#include "GuestFrameComposer.h"
#include "HostFrameComposer.h"
#include "Time.h"

namespace aidl::android::hardware::graphics::composer3::impl {
namespace {

#define GET_DISPLAY_OR_RETURN_ERROR()                                        \
    std::shared_ptr<Display> display = getDisplay(displayId);                \
    if (display == nullptr) {                                                \
        ALOGE("%s failed to get display:%" PRIu64, __FUNCTION__, displayId); \
        return ToBinderStatus(HWC3::Error::BadDisplay);                      \
    }

}  // namespace

using ::aidl::android::hardware::graphics::common::PixelFormat;

class ComposerClient::CommandResultWriter {
   public:
    CommandResultWriter(std::vector<CommandResultPayload>* results)
        : mIndex(0), mResults(results) {}

    void nextCommand() { ++mIndex; }

    void addError(HWC3::Error error) {
        CommandError commandErrorResult;
        commandErrorResult.commandIndex = mIndex;
        commandErrorResult.errorCode = static_cast<int32_t>(error);
        mResults->emplace_back(std::move(commandErrorResult));
    }

    void addPresentFence(int64_t displayId, ::android::base::unique_fd fence) {
        if (fence >= 0) {
            PresentFence presentFenceResult;
            presentFenceResult.display = displayId;
            presentFenceResult.fence = ndk::ScopedFileDescriptor(fence.release());
            mResults->emplace_back(std::move(presentFenceResult));
        }
    }

    void addReleaseFences(int64_t displayId,
                          std::unordered_map<int64_t, ::android::base::unique_fd> layerFences) {
        ReleaseFences releaseFencesResult;
        releaseFencesResult.display = displayId;
        for (auto& [layer, layerFence] : layerFences) {
            if (layerFence >= 0) {
                ReleaseFences::Layer releaseFencesLayerResult;
                releaseFencesLayerResult.layer = layer;
                releaseFencesLayerResult.fence = ndk::ScopedFileDescriptor(layerFence.release());
                releaseFencesResult.layers.emplace_back(std::move(releaseFencesLayerResult));
            }
        }
        mResults->emplace_back(std::move(releaseFencesResult));
    }

    void addChanges(const DisplayChanges& changes) {
        if (changes.compositionChanges) {
            mResults->emplace_back(*changes.compositionChanges);
        }
        if (changes.displayRequestChanges) {
            mResults->emplace_back(*changes.displayRequestChanges);
        }
    }

    void addPresentOrValidateResult(int64_t displayId, PresentOrValidate::Result pov) {
        PresentOrValidate result;
        result.display = displayId;
        result.result = pov;
        mResults->emplace_back(std::move(result));
    }

   private:
    int32_t mIndex = 0;
    std::vector<CommandResultPayload>* mResults = nullptr;
};

ComposerClient::ComposerClient() { DEBUG_LOG("%s", __FUNCTION__); }

ComposerClient::~ComposerClient() {
    DEBUG_LOG("%s", __FUNCTION__);

    std::lock_guard<std::mutex> lock(mDisplaysMutex);

    destroyDisplaysLocked();

    if (mOnClientDestroyed) {
        mOnClientDestroyed();
    }
}

HWC3::Error ComposerClient::init() {
    DEBUG_LOG("%s", __FUNCTION__);

    HWC3::Error error = HWC3::Error::None;

    std::lock_guard<std::mutex> lock(mDisplaysMutex);

    mResources = std::make_unique<ComposerResources>();
    if (!mResources) {
        ALOGE("%s failed to allocate ComposerResources", __FUNCTION__);
        return HWC3::Error::NoResources;
    }

    error = mResources->init();
    if (error != HWC3::Error::None) {
        ALOGE("%s failed to initialize ComposerResources", __FUNCTION__);
        return error;
    }

    error = Device::getInstance().getComposer(&mComposer);
    if (error != HWC3::Error::None) {
        ALOGE("%s failed to get FrameComposer", __FUNCTION__);
        return error;
    }

    const auto HotplugCallback = [this](bool connected,   //
                                        uint32_t id,      //
                                        uint32_t width,   //
                                        uint32_t height,  //
                                        uint32_t dpiX,    //
                                        uint32_t dpiY,    //
                                        uint32_t refreshRate) {
        handleHotplug(connected, id, width, height, dpiX, dpiY, refreshRate);
    };
    error = mComposer->registerOnHotplugCallback(HotplugCallback);
    if (error != HWC3::Error::None) {
        ALOGE("%s failed to register hotplug callback", __FUNCTION__);
        return error;
    }

    error = createDisplaysLocked();
    if (error != HWC3::Error::None) {
        ALOGE("%s failed to create displays.", __FUNCTION__);
        return error;
    }

    DEBUG_LOG("%s initialized!", __FUNCTION__);
    return HWC3::Error::None;
}

ndk::ScopedAStatus ComposerClient::createLayer(int64_t displayId, int32_t bufferSlotCount,
                                               int64_t* layerId) {
    DEBUG_LOG("%s display:%" PRIu64, __FUNCTION__, displayId);

    GET_DISPLAY_OR_RETURN_ERROR();

    HWC3::Error error = display->createLayer(layerId);
    if (error != HWC3::Error::None) {
        ALOGE("%s: display:%" PRIu64 " failed to create layer", __FUNCTION__, displayId);
        return ToBinderStatus(error);
    }

    error = mResources->addLayer(displayId, *layerId, static_cast<uint32_t>(bufferSlotCount));
    if (error != HWC3::Error::None) {
        ALOGE("%s: display:%" PRIu64 " resources failed to create layer", __FUNCTION__, displayId);
        return ToBinderStatus(error);
    }

    return ToBinderStatus(HWC3::Error::None);
}

ndk::ScopedAStatus ComposerClient::createVirtualDisplay(int32_t /*width*/, int32_t /*height*/,
                                                        PixelFormat /*formatHint*/,
                                                        int32_t /*outputBufferSlotCount*/,
                                                        VirtualDisplay* /*display*/) {
    DEBUG_LOG("%s", __FUNCTION__);

    return ToBinderStatus(HWC3::Error::Unsupported);
}

ndk::ScopedAStatus ComposerClient::destroyLayer(int64_t displayId, int64_t layerId) {
    DEBUG_LOG("%s display:%" PRIu64, __FUNCTION__, displayId);

    GET_DISPLAY_OR_RETURN_ERROR();

    HWC3::Error error = display->destroyLayer(layerId);
    if (error != HWC3::Error::None) {
        ALOGE("%s: display:%" PRIu64 " failed to destroy layer:%" PRIu64, __FUNCTION__, displayId,
              layerId);
        return ToBinderStatus(error);
    }

    error = mResources->removeLayer(displayId, layerId);
    if (error != HWC3::Error::None) {
        ALOGE("%s: display:%" PRIu64 " resources failed to destroy layer:%" PRIu64, __FUNCTION__,
              displayId, layerId);
        return ToBinderStatus(error);
    }

    return ToBinderStatus(HWC3::Error::None);
}

ndk::ScopedAStatus ComposerClient::destroyVirtualDisplay(int64_t /*displayId*/) {
    DEBUG_LOG("%s", __FUNCTION__);

    return ToBinderStatus(HWC3::Error::Unsupported);
}

ndk::ScopedAStatus ComposerClient::executeCommands(
    const std::vector<DisplayCommand>& commands,
    std::vector<CommandResultPayload>* commandResultPayloads) {
    DEBUG_LOG("%s", __FUNCTION__);

    CommandResultWriter commandResults(commandResultPayloads);
    for (const DisplayCommand& command : commands) {
        executeDisplayCommand(commandResults, command);
        commandResults.nextCommand();
    }

    return ToBinderStatus(HWC3::Error::None);
}

ndk::ScopedAStatus ComposerClient::getActiveConfig(int64_t displayId, int32_t* config) {
    DEBUG_LOG("%s", __FUNCTION__);

    GET_DISPLAY_OR_RETURN_ERROR();

    return ToBinderStatus(display->getActiveConfig(config));
}

ndk::ScopedAStatus ComposerClient::getColorModes(int64_t displayId,
                                                 std::vector<ColorMode>* colorModes) {
    DEBUG_LOG("%s", __FUNCTION__);

    GET_DISPLAY_OR_RETURN_ERROR();

    return ToBinderStatus(display->getColorModes(colorModes));
}

ndk::ScopedAStatus ComposerClient::getDataspaceSaturationMatrix(common::Dataspace dataspace,
                                                                std::vector<float>* matrix) {
    DEBUG_LOG("%s", __FUNCTION__);

    if (dataspace != common::Dataspace::SRGB_LINEAR) {
        return ToBinderStatus(HWC3::Error::BadParameter);
    }

    // clang-format off
  constexpr std::array<float, 16> kUnit {
    1.0f, 0.0f, 0.0f, 0.0f,
    0.0f, 1.0f, 0.0f, 0.0f,
    0.0f, 0.0f, 1.0f, 0.0f,
    0.0f, 0.0f, 0.0f, 1.0f,
  };
    // clang-format on
    matrix->clear();
    matrix->insert(matrix->begin(), kUnit.begin(), kUnit.end());

    return ToBinderStatus(HWC3::Error::None);
}

ndk::ScopedAStatus ComposerClient::getDisplayAttribute(int64_t displayId, int32_t config,
                                                       DisplayAttribute attribute, int32_t* value) {
    DEBUG_LOG("%s", __FUNCTION__);

    GET_DISPLAY_OR_RETURN_ERROR();

    return ToBinderStatus(display->getDisplayAttribute(config, attribute, value));
}

ndk::ScopedAStatus ComposerClient::getDisplayCapabilities(int64_t displayId,
                                                          std::vector<DisplayCapability>* outCaps) {
    DEBUG_LOG("%s", __FUNCTION__);

    GET_DISPLAY_OR_RETURN_ERROR();

    return ToBinderStatus(display->getDisplayCapabilities(outCaps));
}

ndk::ScopedAStatus ComposerClient::getDisplayConfigs(int64_t displayId,
                                                     std::vector<int32_t>* outConfigs) {
    DEBUG_LOG("%s", __FUNCTION__);

    GET_DISPLAY_OR_RETURN_ERROR();

    return ToBinderStatus(display->getDisplayConfigs(outConfigs));
}

ndk::ScopedAStatus ComposerClient::getDisplayConnectionType(int64_t displayId,
                                                            DisplayConnectionType* outType) {
    DEBUG_LOG("%s", __FUNCTION__);

    GET_DISPLAY_OR_RETURN_ERROR();

    return ToBinderStatus(display->getDisplayConnectionType(outType));
}

ndk::ScopedAStatus ComposerClient::getDisplayIdentificationData(
    int64_t displayId, DisplayIdentification* outIdentification) {
    DEBUG_LOG("%s", __FUNCTION__);

    GET_DISPLAY_OR_RETURN_ERROR();

    return ToBinderStatus(display->getDisplayIdentificationData(outIdentification));
}

ndk::ScopedAStatus ComposerClient::getDisplayName(int64_t displayId, std::string* outName) {
    DEBUG_LOG("%s", __FUNCTION__);

    GET_DISPLAY_OR_RETURN_ERROR();

    return ToBinderStatus(display->getDisplayName(outName));
}

ndk::ScopedAStatus ComposerClient::getDisplayVsyncPeriod(int64_t displayId,
                                                         int32_t* outVsyncPeriod) {
    DEBUG_LOG("%s", __FUNCTION__);

    GET_DISPLAY_OR_RETURN_ERROR();

    return ToBinderStatus(display->getDisplayVsyncPeriod(outVsyncPeriod));
}

ndk::ScopedAStatus ComposerClient::getDisplayedContentSample(int64_t displayId, int64_t maxFrames,
                                                             int64_t timestamp,
                                                             DisplayContentSample* outSamples) {
    DEBUG_LOG("%s", __FUNCTION__);

    GET_DISPLAY_OR_RETURN_ERROR();

    return ToBinderStatus(display->getDisplayedContentSample(maxFrames, timestamp, outSamples));
}

ndk::ScopedAStatus ComposerClient::getDisplayedContentSamplingAttributes(
    int64_t displayId, DisplayContentSamplingAttributes* outAttributes) {
    DEBUG_LOG("%s", __FUNCTION__);

    GET_DISPLAY_OR_RETURN_ERROR();

    return ToBinderStatus(display->getDisplayedContentSamplingAttributes(outAttributes));
}

ndk::ScopedAStatus ComposerClient::getDisplayPhysicalOrientation(
    int64_t displayId, common::Transform* outOrientation) {
    DEBUG_LOG("%s", __FUNCTION__);

    GET_DISPLAY_OR_RETURN_ERROR();

    return ToBinderStatus(display->getDisplayPhysicalOrientation(outOrientation));
}

ndk::ScopedAStatus ComposerClient::getHdrCapabilities(int64_t displayId,
                                                      HdrCapabilities* outCapabilities) {
    DEBUG_LOG("%s", __FUNCTION__);

    GET_DISPLAY_OR_RETURN_ERROR();

    return ToBinderStatus(display->getHdrCapabilities(outCapabilities));
}

ndk::ScopedAStatus ComposerClient::getOverlaySupport(OverlayProperties* /*properties*/) {
    DEBUG_LOG("%s", __FUNCTION__);

    return ToBinderStatus(HWC3::Error::Unsupported);
}

ndk::ScopedAStatus ComposerClient::getMaxVirtualDisplayCount(int32_t* outCount) {
    DEBUG_LOG("%s", __FUNCTION__);

    // Not supported.
    *outCount = 0;

    return ToBinderStatus(HWC3::Error::None);
}

ndk::ScopedAStatus ComposerClient::getPerFrameMetadataKeys(
    int64_t displayId, std::vector<PerFrameMetadataKey>* outKeys) {
    DEBUG_LOG("%s", __FUNCTION__);

    GET_DISPLAY_OR_RETURN_ERROR();

    return ToBinderStatus(display->getPerFrameMetadataKeys(outKeys));
}

ndk::ScopedAStatus ComposerClient::getReadbackBufferAttributes(
    int64_t displayId, ReadbackBufferAttributes* outAttributes) {
    DEBUG_LOG("%s", __FUNCTION__);

    GET_DISPLAY_OR_RETURN_ERROR();

    return ToBinderStatus(display->getReadbackBufferAttributes(outAttributes));
}

ndk::ScopedAStatus ComposerClient::getReadbackBufferFence(
    int64_t displayId, ndk::ScopedFileDescriptor* outAcquireFence) {
    DEBUG_LOG("%s", __FUNCTION__);

    GET_DISPLAY_OR_RETURN_ERROR();

    return ToBinderStatus(display->getReadbackBufferFence(outAcquireFence));
}

ndk::ScopedAStatus ComposerClient::getRenderIntents(int64_t displayId, ColorMode mode,
                                                    std::vector<RenderIntent>* outIntents) {
    DEBUG_LOG("%s", __FUNCTION__);

    GET_DISPLAY_OR_RETURN_ERROR();

    return ToBinderStatus(display->getRenderIntents(mode, outIntents));
}

ndk::ScopedAStatus ComposerClient::getSupportedContentTypes(int64_t displayId,
                                                            std::vector<ContentType>* outTypes) {
    DEBUG_LOG("%s", __FUNCTION__);

    GET_DISPLAY_OR_RETURN_ERROR();

    return ToBinderStatus(display->getSupportedContentTypes(outTypes));
}

ndk::ScopedAStatus ComposerClient::getDisplayDecorationSupport(
    int64_t displayId, std::optional<common::DisplayDecorationSupport>* outSupport) {
    DEBUG_LOG("%s", __FUNCTION__);

    GET_DISPLAY_OR_RETURN_ERROR();

    return ToBinderStatus(display->getDecorationSupport(outSupport));
}

ndk::ScopedAStatus ComposerClient::registerCallback(
    const std::shared_ptr<IComposerCallback>& callback) {
    DEBUG_LOG("%s", __FUNCTION__);

    const bool isFirstRegisterCallback = mCallbacks == nullptr;

    mCallbacks = callback;

    {
        std::lock_guard<std::mutex> lock(mDisplaysMutex);
        for (auto& [_, display] : mDisplays) {
            display->registerCallback(callback);
        }
    }

    if (isFirstRegisterCallback) {
        std::vector<int64_t> displayIds;
        {
            std::lock_guard<std::mutex> lock(mDisplaysMutex);
            for (auto& [displayId, _] : mDisplays) {
                displayIds.push_back(displayId);
            }
        }

        for (auto displayId : displayIds) {
            DEBUG_LOG("%s initial registration, hotplug connecting display:%" PRIu64, __FUNCTION__,
                      displayId);
            mCallbacks->onHotplug(displayId, /*connected=*/true);
        }
    }

    return ndk::ScopedAStatus::ok();
}

ndk::ScopedAStatus ComposerClient::setActiveConfig(int64_t displayId, int32_t configId) {
    DEBUG_LOG("%s display:%" PRIu64 " config:%" PRIu32, __FUNCTION__, displayId, configId);

    GET_DISPLAY_OR_RETURN_ERROR();

    return ToBinderStatus(display->setActiveConfig(configId));
}

ndk::ScopedAStatus ComposerClient::setActiveConfigWithConstraints(
    int64_t displayId, int32_t configId, const VsyncPeriodChangeConstraints& constraints,
    VsyncPeriodChangeTimeline* outTimeline) {
    DEBUG_LOG("%s display:%" PRIu64 " config:%" PRIu32, __FUNCTION__, displayId, configId);

    GET_DISPLAY_OR_RETURN_ERROR();

    return ToBinderStatus(
        display->setActiveConfigWithConstraints(configId, constraints, outTimeline));
}

ndk::ScopedAStatus ComposerClient::setBootDisplayConfig(int64_t displayId, int32_t configId) {
    DEBUG_LOG("%s display:%" PRIu64 " config:%" PRIu32, __FUNCTION__, displayId, configId);

    GET_DISPLAY_OR_RETURN_ERROR();

    return ToBinderStatus(display->setBootConfig(configId));
}

ndk::ScopedAStatus ComposerClient::clearBootDisplayConfig(int64_t displayId) {
    DEBUG_LOG("%s display:%" PRIu64, __FUNCTION__, displayId);

    GET_DISPLAY_OR_RETURN_ERROR();

    return ToBinderStatus(display->clearBootConfig());
}

ndk::ScopedAStatus ComposerClient::getPreferredBootDisplayConfig(int64_t displayId,
                                                                 int32_t* outConfigId) {
    DEBUG_LOG("%s display:%" PRIu64, __FUNCTION__, displayId);

    GET_DISPLAY_OR_RETURN_ERROR();

    return ToBinderStatus(display->getPreferredBootConfig(outConfigId));
}

ndk::ScopedAStatus ComposerClient::getHdrConversionCapabilities(
    std::vector<aidl::android::hardware::graphics::common::HdrConversionCapability>* capabilities) {
    DEBUG_LOG("%s", __FUNCTION__);
    capabilities->clear();
    return ToBinderStatus(HWC3::Error::None);
}

ndk::ScopedAStatus ComposerClient::setHdrConversionStrategy(
    const aidl::android::hardware::graphics::common::HdrConversionStrategy& conversionStrategy,
    aidl::android::hardware::graphics::common::Hdr* preferredHdrOutputType) {
    DEBUG_LOG("%s", __FUNCTION__);
    using HdrConversionStrategyTag =
        aidl::android::hardware::graphics::common::HdrConversionStrategy::Tag;
    switch (conversionStrategy.getTag()) {
        case HdrConversionStrategyTag::autoAllowedHdrTypes: {
            auto autoHdrTypes =
                conversionStrategy.get<HdrConversionStrategyTag::autoAllowedHdrTypes>();
            if (autoHdrTypes.size() != 0) {
                return ToBinderStatus(HWC3::Error::Unsupported);
            }
            break;
        }
        case HdrConversionStrategyTag::passthrough:
        case HdrConversionStrategyTag::forceHdrConversion: {
            break;
        }
    }
    *preferredHdrOutputType = aidl::android::hardware::graphics::common::Hdr::INVALID;
    return ToBinderStatus(HWC3::Error::None);
}

ndk::ScopedAStatus ComposerClient::setAutoLowLatencyMode(int64_t displayId, bool on) {
    DEBUG_LOG("%s", __FUNCTION__);

    GET_DISPLAY_OR_RETURN_ERROR();

    return ToBinderStatus(display->setAutoLowLatencyMode(on));
}

ndk::ScopedAStatus ComposerClient::setClientTargetSlotCount(int64_t displayId, int32_t count) {
    DEBUG_LOG("%s", __FUNCTION__);

    GET_DISPLAY_OR_RETURN_ERROR();

    return ToBinderStatus(
        mResources->setDisplayClientTargetCacheSize(displayId, static_cast<uint32_t>(count)));
}

ndk::ScopedAStatus ComposerClient::setColorMode(int64_t displayId, ColorMode mode,
                                                RenderIntent intent) {
    DEBUG_LOG("%s", __FUNCTION__);

    GET_DISPLAY_OR_RETURN_ERROR();

    return ToBinderStatus(display->setColorMode(mode, intent));
}

ndk::ScopedAStatus ComposerClient::setContentType(int64_t displayId, ContentType type) {
    DEBUG_LOG("%s", __FUNCTION__);

    GET_DISPLAY_OR_RETURN_ERROR();

    return ToBinderStatus(display->setContentType(type));
}

ndk::ScopedAStatus ComposerClient::setDisplayedContentSamplingEnabled(
    int64_t displayId, bool enable, FormatColorComponent componentMask, int64_t maxFrames) {
    DEBUG_LOG("%s", __FUNCTION__);

    GET_DISPLAY_OR_RETURN_ERROR();

    return ToBinderStatus(
        display->setDisplayedContentSamplingEnabled(enable, componentMask, maxFrames));
}

ndk::ScopedAStatus ComposerClient::setPowerMode(int64_t displayId, PowerMode mode) {
    DEBUG_LOG("%s", __FUNCTION__);

    GET_DISPLAY_OR_RETURN_ERROR();

    return ToBinderStatus(display->setPowerMode(mode));
}

ndk::ScopedAStatus ComposerClient::setReadbackBuffer(
    int64_t displayId, const aidl::android::hardware::common::NativeHandle& buffer,
    const ndk::ScopedFileDescriptor& releaseFence) {
    DEBUG_LOG("%s", __FUNCTION__);

    GET_DISPLAY_OR_RETURN_ERROR();

    // Owned by mResources.
    buffer_handle_t importedBuffer = nullptr;

    auto releaser = mResources->createReleaser(true /* isBuffer */);
    auto error =
        mResources->getDisplayReadbackBuffer(displayId, buffer, &importedBuffer, releaser.get());
    if (error != HWC3::Error::None) {
        ALOGE("%s: failed to get readback buffer from resources.", __FUNCTION__);
        return ToBinderStatus(error);
    }

    error = display->setReadbackBuffer(importedBuffer, releaseFence);
    if (error != HWC3::Error::None) {
        ALOGE("%s: failed to set readback buffer to display.", __FUNCTION__);
        return ToBinderStatus(error);
    }

    return ToBinderStatus(HWC3::Error::None);
}

ndk::ScopedAStatus ComposerClient::setVsyncEnabled(int64_t displayId, bool enabled) {
    DEBUG_LOG("%s", __FUNCTION__);

    GET_DISPLAY_OR_RETURN_ERROR();

    return ToBinderStatus(display->setVsyncEnabled(enabled));
}

ndk::ScopedAStatus ComposerClient::setIdleTimerEnabled(int64_t displayId, int32_t timeoutMs) {
    DEBUG_LOG("%s", __FUNCTION__);

    GET_DISPLAY_OR_RETURN_ERROR();

    return ToBinderStatus(display->setIdleTimerEnabled(timeoutMs));
}

ndk::ScopedAStatus ComposerClient::setRefreshRateChangedCallbackDebugEnabled(int64_t displayId,
                                                                             bool) {
    DEBUG_LOG("%s", __FUNCTION__);

    GET_DISPLAY_OR_RETURN_ERROR();

    return ToBinderStatus(HWC3::Error::Unsupported);
}

ndk::ScopedAStatus ComposerClient::getDisplayConfigurations(
    int64_t displayId, int32_t /*maxFrameIntervalNs*/,
    std::vector<DisplayConfiguration>* outDisplayConfig) {
    DEBUG_LOG("%s", __FUNCTION__);

    GET_DISPLAY_OR_RETURN_ERROR();

    return ToBinderStatus(display->getDisplayConfigurations(outDisplayConfig));
}

ndk::ScopedAStatus ComposerClient::notifyExpectedPresent(
    int64_t displayId, const ClockMonotonicTimestamp& /*expectedPresentTime*/,
    int32_t /*frameIntervalNs*/) {
    DEBUG_LOG("%s", __FUNCTION__);

    GET_DISPLAY_OR_RETURN_ERROR();

    return ToBinderStatus(HWC3::Error::Unsupported);
}

ndk::SpAIBinder ComposerClient::createBinder() {
    auto binder = BnComposerClient::createBinder();
    AIBinder_setInheritRt(binder.get(), true);
    return binder;
}

namespace {

#define DISPATCH_LAYER_COMMAND(layerCmd, commandResults, display, layer, field, funcName)         \
    do {                                                                                          \
        if (layerCmd.field) {                                                                     \
            ComposerClient::executeLayerCommandSetLayer##funcName(commandResults, display, layer, \
                                                                  *layerCmd.field);               \
        }                                                                                         \
    } while (0)

#define DISPATCH_DISPLAY_COMMAND(displayCmd, commandResults, display, field, funcName)   \
    do {                                                                                 \
        if (displayCmd.field) {                                                          \
            executeDisplayCommand##funcName(commandResults, display, *displayCmd.field); \
        }                                                                                \
    } while (0)

#define DISPATCH_DISPLAY_BOOL_COMMAND(displayCmd, commandResults, display, field, funcName) \
    do {                                                                                    \
        if (displayCmd.field) {                                                             \
            executeDisplayCommand##funcName(commandResults, display);                       \
        }                                                                                   \
    } while (0)

#define DISPATCH_DISPLAY_BOOL_COMMAND_AND_DATA(displayCmd, commandResults, display, field, data, \
                                               funcName)                                         \
    do {                                                                                         \
        if (displayCmd.field) {                                                                  \
            executeDisplayCommand##funcName(commandResults, display, displayCmd.data);           \
        }                                                                                        \
    } while (0)

#define LOG_DISPLAY_COMMAND_ERROR(display, error)                                      \
    do {                                                                               \
        const std::string errorString = toString(error);                               \
        ALOGE("%s: display:%" PRId64 " failed with:%s", __FUNCTION__, display.getId(), \
              errorString.c_str());                                                    \
    } while (0)

#define LOG_LAYER_COMMAND_ERROR(display, layer, error)                                  \
    do {                                                                                \
        const std::string errorString = toString(error);                                \
        ALOGE("%s: display:%" PRId64 " layer:%" PRId64 " failed with:%s", __FUNCTION__, \
              display.getId(), layer->getId(), errorString.c_str());                    \
    } while (0)

}  // namespace

void ComposerClient::executeDisplayCommand(CommandResultWriter& commandResults,
                                           const DisplayCommand& displayCommand) {
    std::shared_ptr<Display> display = getDisplay(displayCommand.display);
    if (display == nullptr) {
        commandResults.addError(HWC3::Error::BadDisplay);
        return;
    }

    for (const LayerCommand& layerCmd : displayCommand.layers) {
        executeLayerCommand(commandResults, *display, layerCmd);
    }

    DISPATCH_DISPLAY_COMMAND(displayCommand, commandResults, *display, colorTransformMatrix,
                             SetColorTransform);
    DISPATCH_DISPLAY_COMMAND(displayCommand, commandResults, *display, brightness, SetBrightness);
    DISPATCH_DISPLAY_COMMAND(displayCommand, commandResults, *display, clientTarget,
                             SetClientTarget);
    DISPATCH_DISPLAY_COMMAND(displayCommand, commandResults, *display, virtualDisplayOutputBuffer,
                             SetOutputBuffer);
    DISPATCH_DISPLAY_BOOL_COMMAND_AND_DATA(displayCommand, commandResults, *display,
                                           validateDisplay, expectedPresentTime, ValidateDisplay);
    DISPATCH_DISPLAY_BOOL_COMMAND(displayCommand, commandResults, *display, acceptDisplayChanges,
                                  AcceptDisplayChanges);
    DISPATCH_DISPLAY_BOOL_COMMAND(displayCommand, commandResults, *display, presentDisplay,
                                  PresentDisplay);
    DISPATCH_DISPLAY_BOOL_COMMAND_AND_DATA(displayCommand, commandResults, *display,
                                           presentOrValidateDisplay, expectedPresentTime,
                                           PresentOrValidateDisplay);
}

void ComposerClient::executeLayerCommand(CommandResultWriter& commandResults, Display& display,
                                         const LayerCommand& layerCommand) {
    Layer* layer = display.getLayer(layerCommand.layer);
    if (layer == nullptr) {
        commandResults.addError(HWC3::Error::BadLayer);
        return;
    }

    DISPATCH_LAYER_COMMAND(layerCommand, commandResults, display, layer, cursorPosition,
                           CursorPosition);
    DISPATCH_LAYER_COMMAND(layerCommand, commandResults, display, layer, buffer, Buffer);
    DISPATCH_LAYER_COMMAND(layerCommand, commandResults, display, layer, damage, SurfaceDamage);
    DISPATCH_LAYER_COMMAND(layerCommand, commandResults, display, layer, blendMode, BlendMode);
    DISPATCH_LAYER_COMMAND(layerCommand, commandResults, display, layer, color, Color);
    DISPATCH_LAYER_COMMAND(layerCommand, commandResults, display, layer, composition, Composition);
    DISPATCH_LAYER_COMMAND(layerCommand, commandResults, display, layer, dataspace, Dataspace);
    DISPATCH_LAYER_COMMAND(layerCommand, commandResults, display, layer, displayFrame,
                           DisplayFrame);
    DISPATCH_LAYER_COMMAND(layerCommand, commandResults, display, layer, planeAlpha, PlaneAlpha);
    DISPATCH_LAYER_COMMAND(layerCommand, commandResults, display, layer, sidebandStream,
                           SidebandStream);
    DISPATCH_LAYER_COMMAND(layerCommand, commandResults, display, layer, sourceCrop, SourceCrop);
    DISPATCH_LAYER_COMMAND(layerCommand, commandResults, display, layer, transform, Transform);
    DISPATCH_LAYER_COMMAND(layerCommand, commandResults, display, layer, visibleRegion,
                           VisibleRegion);
    DISPATCH_LAYER_COMMAND(layerCommand, commandResults, display, layer, z, ZOrder);
    DISPATCH_LAYER_COMMAND(layerCommand, commandResults, display, layer, colorTransform,
                           ColorTransform);
    DISPATCH_LAYER_COMMAND(layerCommand, commandResults, display, layer, brightness, Brightness);
    DISPATCH_LAYER_COMMAND(layerCommand, commandResults, display, layer, perFrameMetadata,
                           PerFrameMetadata);
    DISPATCH_LAYER_COMMAND(layerCommand, commandResults, display, layer, perFrameMetadataBlob,
                           PerFrameMetadataBlobs);
}

void ComposerClient::executeDisplayCommandSetColorTransform(CommandResultWriter& commandResults,
                                                            Display& display,
                                                            const std::vector<float>& matrix) {
    DEBUG_LOG("%s", __FUNCTION__);

    auto error = display.setColorTransform(matrix);
    if (error != HWC3::Error::None) {
        LOG_DISPLAY_COMMAND_ERROR(display, error);
        commandResults.addError(error);
    }
}

void ComposerClient::executeDisplayCommandSetBrightness(CommandResultWriter& commandResults,
                                                        Display& display,
                                                        const DisplayBrightness& brightness) {
    DEBUG_LOG("%s", __FUNCTION__);

    auto error = display.setBrightness(brightness.brightness);
    if (error != HWC3::Error::None) {
        LOG_DISPLAY_COMMAND_ERROR(display, error);
        commandResults.addError(error);
    }
}

void ComposerClient::executeDisplayCommandSetClientTarget(CommandResultWriter& commandResults,
                                                          Display& display,
                                                          const ClientTarget& clientTarget) {
    DEBUG_LOG("%s", __FUNCTION__);

    // Owned by mResources.
    buffer_handle_t importedBuffer = nullptr;

    auto releaser = mResources->createReleaser(/*isBuffer=*/true);
    auto error = mResources->getDisplayClientTarget(display.getId(), clientTarget.buffer,
                                                    &importedBuffer, releaser.get());
    if (error != HWC3::Error::None) {
        LOG_DISPLAY_COMMAND_ERROR(display, error);
        commandResults.addError(error);
        return;
    }

    error = display.setClientTarget(importedBuffer, clientTarget.buffer.fence,
                                    clientTarget.dataspace, clientTarget.damage);
    if (error != HWC3::Error::None) {
        LOG_DISPLAY_COMMAND_ERROR(display, error);
        commandResults.addError(error);
        return;
    }
}

void ComposerClient::executeDisplayCommandSetOutputBuffer(CommandResultWriter& commandResults,
                                                          Display& display, const Buffer& buffer) {
    DEBUG_LOG("%s", __FUNCTION__);

    // Owned by mResources.
    buffer_handle_t importedBuffer = nullptr;

    auto releaser = mResources->createReleaser(/*isBuffer=*/true);
    auto error = mResources->getDisplayOutputBuffer(display.getId(), buffer, &importedBuffer,
                                                    releaser.get());
    if (error != HWC3::Error::None) {
        LOG_DISPLAY_COMMAND_ERROR(display, error);
        commandResults.addError(error);
        return;
    }

    error = display.setOutputBuffer(importedBuffer, buffer.fence);
    if (error != HWC3::Error::None) {
        LOG_DISPLAY_COMMAND_ERROR(display, error);
        commandResults.addError(error);
        return;
    }
}

void ComposerClient::executeDisplayCommandValidateDisplay(
    CommandResultWriter& commandResults, Display& display,
    const std::optional<ClockMonotonicTimestamp> expectedPresentTime) {
    DEBUG_LOG("%s", __FUNCTION__);

    auto error = display.setExpectedPresentTime(expectedPresentTime);
    if (error != HWC3::Error::None) {
        LOG_DISPLAY_COMMAND_ERROR(display, error);
        commandResults.addError(error);
    }

    DisplayChanges changes;

    error = display.validate(&changes);
    if (error != HWC3::Error::None) {
        LOG_DISPLAY_COMMAND_ERROR(display, error);
        commandResults.addError(error);
    } else {
        commandResults.addChanges(changes);
    }

    mResources->setDisplayMustValidateState(display.getId(), false);
}

void ComposerClient::executeDisplayCommandAcceptDisplayChanges(CommandResultWriter& commandResults,
                                                               Display& display) {
    DEBUG_LOG("%s", __FUNCTION__);

    auto error = display.acceptChanges();
    if (error != HWC3::Error::None) {
        LOG_DISPLAY_COMMAND_ERROR(display, error);
        commandResults.addError(error);
    }
}

void ComposerClient::executeDisplayCommandPresentOrValidateDisplay(
    CommandResultWriter& commandResults, Display& display,
    const std::optional<ClockMonotonicTimestamp> expectedPresentTime) {
    DEBUG_LOG("%s", __FUNCTION__);

    // TODO: Support SKIP_VALIDATE.

    auto error = display.setExpectedPresentTime(expectedPresentTime);
    if (error != HWC3::Error::None) {
        LOG_DISPLAY_COMMAND_ERROR(display, error);
        commandResults.addError(error);
    }

    DisplayChanges changes;

    error = display.validate(&changes);
    if (error != HWC3::Error::None) {
        LOG_DISPLAY_COMMAND_ERROR(display, error);
        commandResults.addError(error);
    } else {
        const int64_t displayId = display.getId();
        commandResults.addChanges(changes);
        commandResults.addPresentOrValidateResult(displayId, PresentOrValidate::Result::Validated);
    }

    mResources->setDisplayMustValidateState(display.getId(), false);
}

void ComposerClient::executeDisplayCommandPresentDisplay(CommandResultWriter& commandResults,
                                                         Display& display) {
    DEBUG_LOG("%s", __FUNCTION__);

    if (mResources->mustValidateDisplay(display.getId())) {
        ALOGE("%s: display:%" PRIu64 " not validated", __FUNCTION__, display.getId());
        commandResults.addError(HWC3::Error::NotValidated);
        return;
    }

    ::android::base::unique_fd displayFence;
    std::unordered_map<int64_t, ::android::base::unique_fd> layerFences;

    auto error = display.present(&displayFence, &layerFences);
    if (error != HWC3::Error::None) {
        LOG_DISPLAY_COMMAND_ERROR(display, error);
        commandResults.addError(error);
    } else {
        const int64_t displayId = display.getId();
        commandResults.addPresentFence(displayId, std::move(displayFence));
        commandResults.addReleaseFences(displayId, std::move(layerFences));
    }
}

void ComposerClient::executeLayerCommandSetLayerCursorPosition(
    CommandResultWriter& commandResults, Display& display, Layer* layer,
    const common::Point& cursorPosition) {
    DEBUG_LOG("%s", __FUNCTION__);

    auto error = layer->setCursorPosition(cursorPosition);
    if (error != HWC3::Error::None) {
        LOG_LAYER_COMMAND_ERROR(display, layer, error);
        commandResults.addError(error);
    }
}

void ComposerClient::executeLayerCommandSetLayerBuffer(CommandResultWriter& commandResults,
                                                       Display& display, Layer* layer,
                                                       const Buffer& buffer) {
    DEBUG_LOG("%s", __FUNCTION__);

    // Owned by mResources.
    buffer_handle_t importedBuffer = nullptr;

    auto releaser = mResources->createReleaser(/*isBuffer=*/true);
    auto error = mResources->getLayerBuffer(display.getId(), layer->getId(), buffer,
                                            &importedBuffer, releaser.get());
    if (error != HWC3::Error::None) {
        LOG_LAYER_COMMAND_ERROR(display, layer, error);
        commandResults.addError(error);
        return;
    }

    error = layer->setBuffer(importedBuffer, buffer.fence);
    if (error != HWC3::Error::None) {
        LOG_LAYER_COMMAND_ERROR(display, layer, error);
        commandResults.addError(error);
    }
}

void ComposerClient::executeLayerCommandSetLayerSurfaceDamage(
    CommandResultWriter& commandResults, Display& display, Layer* layer,
    const std::vector<std::optional<common::Rect>>& damage) {
    DEBUG_LOG("%s", __FUNCTION__);

    auto error = layer->setSurfaceDamage(damage);
    if (error != HWC3::Error::None) {
        LOG_LAYER_COMMAND_ERROR(display, layer, error);
        commandResults.addError(error);
    }
}

void ComposerClient::executeLayerCommandSetLayerBlendMode(CommandResultWriter& commandResults,
                                                          Display& display, Layer* layer,
                                                          const ParcelableBlendMode& blendMode) {
    DEBUG_LOG("%s", __FUNCTION__);

    auto error = layer->setBlendMode(blendMode.blendMode);
    if (error != HWC3::Error::None) {
        LOG_LAYER_COMMAND_ERROR(display, layer, error);
        commandResults.addError(error);
    }
}

void ComposerClient::executeLayerCommandSetLayerColor(CommandResultWriter& commandResults,
                                                      Display& display, Layer* layer,
                                                      const Color& color) {
    DEBUG_LOG("%s", __FUNCTION__);

    auto error = layer->setColor(color);
    if (error != HWC3::Error::None) {
        LOG_LAYER_COMMAND_ERROR(display, layer, error);
        commandResults.addError(error);
    }
}

void ComposerClient::executeLayerCommandSetLayerComposition(
    CommandResultWriter& commandResults, Display& display, Layer* layer,
    const ParcelableComposition& composition) {
    DEBUG_LOG("%s", __FUNCTION__);

    auto error = layer->setCompositionType(composition.composition);
    if (error != HWC3::Error::None) {
        LOG_LAYER_COMMAND_ERROR(display, layer, error);
        commandResults.addError(error);
    }
}

void ComposerClient::executeLayerCommandSetLayerDataspace(CommandResultWriter& commandResults,
                                                          Display& display, Layer* layer,
                                                          const ParcelableDataspace& dataspace) {
    DEBUG_LOG("%s", __FUNCTION__);

    auto error = layer->setDataspace(dataspace.dataspace);
    if (error != HWC3::Error::None) {
        LOG_LAYER_COMMAND_ERROR(display, layer, error);
        commandResults.addError(error);
    }
}

void ComposerClient::executeLayerCommandSetLayerDisplayFrame(CommandResultWriter& commandResults,
                                                             Display& display, Layer* layer,
                                                             const common::Rect& rect) {
    DEBUG_LOG("%s", __FUNCTION__);

    auto error = layer->setDisplayFrame(rect);
    if (error != HWC3::Error::None) {
        LOG_LAYER_COMMAND_ERROR(display, layer, error);
        commandResults.addError(error);
    }
}

void ComposerClient::executeLayerCommandSetLayerPlaneAlpha(CommandResultWriter& commandResults,
                                                           Display& display, Layer* layer,
                                                           const PlaneAlpha& planeAlpha) {
    DEBUG_LOG("%s", __FUNCTION__);

    auto error = layer->setPlaneAlpha(planeAlpha.alpha);
    if (error != HWC3::Error::None) {
        LOG_LAYER_COMMAND_ERROR(display, layer, error);
        commandResults.addError(error);
    }
}

void ComposerClient::executeLayerCommandSetLayerSidebandStream(
    CommandResultWriter& commandResults, Display& display, Layer* layer,
    const aidl::android::hardware::common::NativeHandle& handle) {
    DEBUG_LOG("%s", __FUNCTION__);

    // Owned by mResources.
    buffer_handle_t importedStream = nullptr;

    auto releaser = mResources->createReleaser(/*isBuffer=*/false);
    auto error = mResources->getLayerSidebandStream(display.getId(), layer->getId(), handle,
                                                    &importedStream, releaser.get());
    if (error != HWC3::Error::None) {
        LOG_LAYER_COMMAND_ERROR(display, layer, error);
        commandResults.addError(error);
        return;
    }

    error = layer->setSidebandStream(importedStream);
    if (error != HWC3::Error::None) {
        LOG_LAYER_COMMAND_ERROR(display, layer, error);
        commandResults.addError(error);
    }
}

void ComposerClient::executeLayerCommandSetLayerSourceCrop(CommandResultWriter& commandResults,
                                                           Display& display, Layer* layer,
                                                           const common::FRect& sourceCrop) {
    DEBUG_LOG("%s", __FUNCTION__);

    auto error = layer->setSourceCrop(sourceCrop);
    if (error != HWC3::Error::None) {
        LOG_LAYER_COMMAND_ERROR(display, layer, error);
        commandResults.addError(error);
    }
}

void ComposerClient::executeLayerCommandSetLayerTransform(CommandResultWriter& commandResults,
                                                          Display& display, Layer* layer,
                                                          const ParcelableTransform& transform) {
    DEBUG_LOG("%s", __FUNCTION__);

    auto error = layer->setTransform(transform.transform);
    if (error != HWC3::Error::None) {
        LOG_LAYER_COMMAND_ERROR(display, layer, error);
        commandResults.addError(error);
    }
}

void ComposerClient::executeLayerCommandSetLayerVisibleRegion(
    CommandResultWriter& commandResults, Display& display, Layer* layer,
    const std::vector<std::optional<common::Rect>>& visibleRegion) {
    DEBUG_LOG("%s", __FUNCTION__);

    auto error = layer->setVisibleRegion(visibleRegion);
    if (error != HWC3::Error::None) {
        LOG_LAYER_COMMAND_ERROR(display, layer, error);
        commandResults.addError(error);
    }
}

void ComposerClient::executeLayerCommandSetLayerZOrder(CommandResultWriter& commandResults,
                                                       Display& display, Layer* layer,
                                                       const ZOrder& zOrder) {
    DEBUG_LOG("%s", __FUNCTION__);

    auto error = layer->setZOrder(zOrder.z);
    if (error != HWC3::Error::None) {
        LOG_LAYER_COMMAND_ERROR(display, layer, error);
        commandResults.addError(error);
    }
}

void ComposerClient::executeLayerCommandSetLayerPerFrameMetadata(
    CommandResultWriter& commandResults, Display& display, Layer* layer,
    const std::vector<std::optional<PerFrameMetadata>>& perFrameMetadata) {
    DEBUG_LOG("%s", __FUNCTION__);

    auto error = layer->setPerFrameMetadata(perFrameMetadata);
    if (error != HWC3::Error::None) {
        LOG_LAYER_COMMAND_ERROR(display, layer, error);
        commandResults.addError(error);
    }
}

void ComposerClient::executeLayerCommandSetLayerColorTransform(
    CommandResultWriter& commandResults, Display& display, Layer* layer,
    const std::vector<float>& colorTransform) {
    DEBUG_LOG("%s", __FUNCTION__);

    auto error = layer->setColorTransform(colorTransform);
    if (error != HWC3::Error::None) {
        LOG_LAYER_COMMAND_ERROR(display, layer, error);
        commandResults.addError(error);
    }
}

void ComposerClient::executeLayerCommandSetLayerBrightness(CommandResultWriter& commandResults,
                                                           Display& display, Layer* layer,
                                                           const LayerBrightness& brightness) {
    DEBUG_LOG("%s", __FUNCTION__);

    auto error = layer->setBrightness(brightness.brightness);
    if (error != HWC3::Error::None) {
        LOG_LAYER_COMMAND_ERROR(display, layer, error);
        commandResults.addError(error);
    }
}

void ComposerClient::executeLayerCommandSetLayerPerFrameMetadataBlobs(
    CommandResultWriter& commandResults, Display& display, Layer* layer,
    const std::vector<std::optional<PerFrameMetadataBlob>>& perFrameMetadataBlob) {
    DEBUG_LOG("%s", __FUNCTION__);

    auto error = layer->setPerFrameMetadataBlobs(perFrameMetadataBlob);
    if (error != HWC3::Error::None) {
        LOG_LAYER_COMMAND_ERROR(display, layer, error);
        commandResults.addError(error);
    }
}

std::shared_ptr<Display> ComposerClient::getDisplay(int64_t displayId) {
    std::lock_guard<std::mutex> lock(mDisplaysMutex);

    auto it = mDisplays.find(displayId);
    if (it == mDisplays.end()) {
        ALOGE("%s: no display:%" PRIu64, __FUNCTION__, displayId);
        return nullptr;
    }
    return it->second;
}

HWC3::Error ComposerClient::createDisplaysLocked() {
    DEBUG_LOG("%s", __FUNCTION__);

    if (!mComposer) {
        ALOGE("%s composer not initialized!", __FUNCTION__);
        return HWC3::Error::NoResources;
    }

    std::vector<DisplayMultiConfigs> displays;

    HWC3::Error error = findDisplays(mComposer->getDrmPresenter(), &displays);
    if (error != HWC3::Error::None) {
        ALOGE("%s failed to find display configs", __FUNCTION__);
        return error;
    }

    for (const auto& iter : displays) {
        error = createDisplayLocked(iter.displayId, iter.activeConfigId, iter.configs);
        if (error != HWC3::Error::None) {
            ALOGE("%s failed to create display from config", __FUNCTION__);
            return error;
        }
    }

    return HWC3::Error::None;
}

HWC3::Error ComposerClient::createDisplayLocked(int64_t displayId, int32_t activeConfigId,
                                                const std::vector<DisplayConfig>& configs) {
    DEBUG_LOG("%s", __FUNCTION__);

    if (!mComposer) {
        ALOGE("%s composer not initialized!", __FUNCTION__);
        return HWC3::Error::NoResources;
    }

    auto display = std::make_shared<Display>(mComposer, displayId);
    if (display == nullptr) {
        ALOGE("%s failed to allocate display", __FUNCTION__);
        return HWC3::Error::NoResources;
    }

    HWC3::Error error = display->init(configs, activeConfigId);
    if (error != HWC3::Error::None) {
        ALOGE("%s failed to initialize display:%" PRIu64, __FUNCTION__, displayId);
        return error;
    }

    error = mComposer->onDisplayCreate(display.get());
    if (error != HWC3::Error::None) {
        ALOGE("%s failed to register display:%" PRIu64 " with composer", __FUNCTION__, displayId);
        return error;
    }

    display->setPowerMode(PowerMode::ON);

    DEBUG_LOG("%s: adding display:%" PRIu64, __FUNCTION__, displayId);
    mDisplays.emplace(displayId, std::move(display));

    error = mResources->addPhysicalDisplay(displayId);
    if (error != HWC3::Error::None) {
        ALOGE("%s failed to initialize display:%" PRIu64 " resources", __FUNCTION__, displayId);
        return error;
    }

    return HWC3::Error::None;
}

HWC3::Error ComposerClient::destroyDisplaysLocked() {
    DEBUG_LOG("%s", __FUNCTION__);

    std::vector<int64_t> displayIds;
    for (const auto& [displayId, _] : mDisplays) {
        displayIds.push_back(displayId);
    }
    for (const int64_t displayId : displayIds) {
        destroyDisplayLocked(displayId);
    }

    return HWC3::Error::None;
}

HWC3::Error ComposerClient::destroyDisplayLocked(int64_t displayId) {
    DEBUG_LOG("%s display:%" PRId64, __FUNCTION__, displayId);

    auto it = mDisplays.find(displayId);
    if (it == mDisplays.end()) {
        ALOGE("%s: display:%" PRId64 " no such display?", __FUNCTION__, displayId);
        return HWC3::Error::BadDisplay;
    }

    Display* display = it->second.get();

    display->setPowerMode(PowerMode::OFF);

    HWC3::Error error = mComposer->onDisplayDestroy(it->second.get());
    if (error != HWC3::Error::None) {
        ALOGE("%s: display:%" PRId64 " failed to destroy with frame composer", __FUNCTION__,
              displayId);
    }

    error = mResources->removeDisplay(displayId);
    if (error != HWC3::Error::None) {
        ALOGE("%s: display:%" PRId64 " failed to destroy with resources", __FUNCTION__, displayId);
    }

    mDisplays.erase(it);

    return HWC3::Error::None;
}

HWC3::Error ComposerClient::handleHotplug(bool connected, uint32_t id, uint32_t width,
                                          uint32_t height, uint32_t dpiX, uint32_t dpiY,
                                          uint32_t refreshRateHz) {
    if (!mCallbacks) {
        return HWC3::Error::None;
    }

    const int64_t displayId = static_cast<int64_t>(id);

    if (connected) {
        const int32_t configId = static_cast<int32_t>(id);
        int32_t vsyncPeriodNanos = HertzToPeriodNanos(refreshRateHz);
        const std::vector<DisplayConfig> configs = {
            DisplayConfig(configId, static_cast<int>(width), static_cast<int>(height),
                          static_cast<int>(dpiX), static_cast<int>(dpiY), vsyncPeriodNanos)};
        {
            std::lock_guard<std::mutex> lock(mDisplaysMutex);
            createDisplayLocked(displayId, configId, configs);
        }

        ALOGI("Hotplug connecting display:%" PRIu32 " w:%" PRIu32 " h:%" PRIu32 " dpiX:%" PRIu32
              " dpiY %" PRIu32 "fps %" PRIu32,
              id, width, height, dpiX, dpiY, refreshRateHz);
        mCallbacks->onHotplug(displayId, /*connected=*/true);
    } else {
        ALOGI("Hotplug disconnecting display:%" PRIu64, displayId);
        mCallbacks->onHotplug(displayId, /*connected=*/false);

        {
            std::lock_guard<std::mutex> lock(mDisplaysMutex);
            destroyDisplayLocked(displayId);
        }
    }

    return HWC3::Error::None;
}

}  // namespace aidl::android::hardware::graphics::composer3::impl
