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

#include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
#include <android/native_window.h>
#include <android/surface_control.h>
#include <android/surface_control_jni.h>
#include <android_runtime/android_view_SurfaceControl.h>
#include <configstore/Utils.h>
#include <gui/HdrMetadata.h>
#include <gui/ISurfaceComposer.h>
#include <gui/Surface.h>
#include <gui/SurfaceComposerClient.h>
#include <gui/SurfaceControl.h>
#include <private/android/choreographer.h>
#include <surface_control_private.h>
#include <ui/DynamicDisplayInfo.h>
#include <utils/Timers.h>

#include <utility>

using namespace android::hardware::configstore;
using namespace android::hardware::configstore::V1_0;
using namespace android;

using Transaction = SurfaceComposerClient::Transaction;

#define CHECK_NOT_NULL(name) \
    LOG_ALWAYS_FATAL_IF(name == nullptr, "nullptr passed as " #name " argument");

#define CHECK_VALID_RECT(name)                                     \
    LOG_ALWAYS_FATAL_IF(!static_cast<const Rect&>(name).isValid(), \
                        "invalid arg passed as " #name " argument");

static_assert(static_cast<int>(ADATASPACE_UNKNOWN) == static_cast<int>(HAL_DATASPACE_UNKNOWN));
static_assert(static_cast<int>(ADATASPACE_SCRGB_LINEAR) ==
              static_cast<int>(HAL_DATASPACE_V0_SCRGB_LINEAR));
static_assert(static_cast<int>(ADATASPACE_SRGB) == static_cast<int>(HAL_DATASPACE_V0_SRGB));
static_assert(static_cast<int>(ADATASPACE_SCRGB) == static_cast<int>(HAL_DATASPACE_V0_SCRGB));
static_assert(static_cast<int>(ADATASPACE_DISPLAY_P3) ==
              static_cast<int>(HAL_DATASPACE_DISPLAY_P3));
static_assert(static_cast<int>(ADATASPACE_BT2020_PQ) == static_cast<int>(HAL_DATASPACE_BT2020_PQ));

Transaction* ASurfaceTransaction_to_Transaction(ASurfaceTransaction* aSurfaceTransaction) {
    return reinterpret_cast<Transaction*>(aSurfaceTransaction);
}

SurfaceControl* ASurfaceControl_to_SurfaceControl(ASurfaceControl* aSurfaceControl) {
    return reinterpret_cast<SurfaceControl*>(aSurfaceControl);
}

void SurfaceControl_acquire(SurfaceControl* surfaceControl) {
    // incStrong/decStrong token must be the same, doesn't matter what it is
    surfaceControl->incStrong((void*)SurfaceControl_acquire);
}

void SurfaceControl_release(SurfaceControl* surfaceControl) {
    // incStrong/decStrong token must be the same, doesn't matter what it is
    surfaceControl->decStrong((void*)SurfaceControl_acquire);
}

ASurfaceControl* ASurfaceControl_createFromWindow(ANativeWindow* window, const char* debug_name) {
    CHECK_NOT_NULL(window);
    CHECK_NOT_NULL(debug_name);

    sp<SurfaceComposerClient> client = new SurfaceComposerClient();
    if (client->initCheck() != NO_ERROR) {
        return nullptr;
    }

    Surface* surface = static_cast<Surface*>(window);
    sp<IBinder> parentHandle = surface->getSurfaceControlHandle();

    int32_t flags = ISurfaceComposerClient::eFXSurfaceBufferState;
    sp<SurfaceControl> surfaceControl;
    if (parentHandle) {
        surfaceControl =
                client->createSurface(String8(debug_name), 0 /* width */, 0 /* height */,
                                      // Format is only relevant for buffer queue layers.
                                      PIXEL_FORMAT_UNKNOWN /* format */, flags, parentHandle);
    } else {
        // deprecated, this should no longer be used
        surfaceControl = nullptr;
    }

    if (!surfaceControl) {
        return nullptr;
    }

    SurfaceControl_acquire(surfaceControl.get());
    return reinterpret_cast<ASurfaceControl*>(surfaceControl.get());
}

ASurfaceControl* ASurfaceControl_create(ASurfaceControl* parent, const char* debug_name) {
    CHECK_NOT_NULL(parent);
    CHECK_NOT_NULL(debug_name);

    SurfaceComposerClient* client = ASurfaceControl_to_SurfaceControl(parent)->getClient().get();

    SurfaceControl* surfaceControlParent = ASurfaceControl_to_SurfaceControl(parent);

    uint32_t flags = ISurfaceComposerClient::eFXSurfaceBufferState;
    sp<SurfaceControl> surfaceControl =
            client->createSurface(String8(debug_name), 0 /* width */, 0 /* height */,
                                  // Format is only relevant for buffer queue layers.
                                  PIXEL_FORMAT_UNKNOWN /* format */, flags,
                                  surfaceControlParent->getHandle());
    if (!surfaceControl) {
        return nullptr;
    }

    SurfaceControl_acquire(surfaceControl.get());
    return reinterpret_cast<ASurfaceControl*>(surfaceControl.get());
}

void ASurfaceControl_acquire(ASurfaceControl* aSurfaceControl) {
    SurfaceControl* surfaceControl = ASurfaceControl_to_SurfaceControl(aSurfaceControl);

    SurfaceControl_acquire(surfaceControl);
}

void ASurfaceControl_release(ASurfaceControl* aSurfaceControl) {
    SurfaceControl* surfaceControl = ASurfaceControl_to_SurfaceControl(aSurfaceControl);

    SurfaceControl_release(surfaceControl);
}

ASurfaceControl* ASurfaceControl_fromJava(JNIEnv* env, jobject surfaceControlObj) {
    LOG_ALWAYS_FATAL_IF(!env, "nullptr passed to ASurfaceControl_fromJava as env argument");
    LOG_ALWAYS_FATAL_IF(!surfaceControlObj,
                        "nullptr passed to ASurfaceControl_fromJava as surfaceControlObj argument");
    SurfaceControl* surfaceControl =
            android_view_SurfaceControl_getNativeSurfaceControl(env, surfaceControlObj);
    LOG_ALWAYS_FATAL_IF(!surfaceControl,
                        "surfaceControlObj passed to ASurfaceControl_fromJava is not valid");
    SurfaceControl_acquire(surfaceControl);
    return reinterpret_cast<ASurfaceControl*>(surfaceControl);
}

struct ASurfaceControlStats {
    std::variant<int64_t, sp<Fence>> acquireTimeOrFence;
    sp<Fence> previousReleaseFence;
    uint64_t frameNumber;
};

void ASurfaceControl_registerSurfaceStatsListener(ASurfaceControl* control, int32_t id,
                                                  void* context,
                                                  ASurfaceControl_SurfaceStatsListener func) {
    SurfaceStatsCallback callback = [func, id](void* callback_context, nsecs_t, const sp<Fence>&,
                                               const SurfaceStats& surfaceStats) {
        ASurfaceControlStats aSurfaceControlStats;

        aSurfaceControlStats.acquireTimeOrFence = surfaceStats.acquireTimeOrFence;
        aSurfaceControlStats.previousReleaseFence = surfaceStats.previousReleaseFence;
        aSurfaceControlStats.frameNumber = surfaceStats.eventStats.frameNumber;

        (*func)(callback_context, id, &aSurfaceControlStats);
    };

    TransactionCompletedListener::getInstance()->addSurfaceStatsListener(context,
            reinterpret_cast<void*>(func), ASurfaceControl_to_SurfaceControl(control), callback);
}

void ASurfaceControl_unregisterSurfaceStatsListener(void* context,
        ASurfaceControl_SurfaceStatsListener func) {
    TransactionCompletedListener::getInstance()->removeSurfaceStatsListener(context,
            reinterpret_cast<void*>(func));
}

AChoreographer* ASurfaceControl_getChoreographer(ASurfaceControl* aSurfaceControl) {
    LOG_ALWAYS_FATAL_IF(aSurfaceControl == nullptr, "aSurfaceControl should not be nullptr");
    SurfaceControl* surfaceControl =
            ASurfaceControl_to_SurfaceControl(reinterpret_cast<ASurfaceControl*>(aSurfaceControl));
    if (!surfaceControl->isValid()) {
        ALOGE("Attempted to get choreographer from invalid surface control");
        return nullptr;
    }
    SurfaceControl_acquire(surfaceControl);
    return reinterpret_cast<AChoreographer*>(surfaceControl->getChoreographer().get());
}

int64_t ASurfaceControlStats_getAcquireTime(ASurfaceControlStats* stats) {
    if (const auto* fence = std::get_if<sp<Fence>>(&stats->acquireTimeOrFence)) {
        // We got a fence instead of the acquire time due to latch unsignaled.
        // Ideally the client could just get the acquire time dericly from
        // the fence instead of calling this function which needs to block.
        (*fence)->waitForever("ASurfaceControlStats_getAcquireTime");
        return (*fence)->getSignalTime();
    }

    return std::get<int64_t>(stats->acquireTimeOrFence);
}

uint64_t ASurfaceControlStats_getFrameNumber(ASurfaceControlStats* stats) {
    return stats->frameNumber;
}

ASurfaceTransaction* ASurfaceTransaction_create() {
    Transaction* transaction = new Transaction;
    return reinterpret_cast<ASurfaceTransaction*>(transaction);
}

void ASurfaceTransaction_delete(ASurfaceTransaction* aSurfaceTransaction) {
    Transaction* transaction = ASurfaceTransaction_to_Transaction(aSurfaceTransaction);
    delete transaction;
}

ASurfaceTransaction* ASurfaceTransaction_fromJava(JNIEnv* env, jobject transactionObj) {
    LOG_ALWAYS_FATAL_IF(!env, "nullptr passed to ASurfaceTransaction_fromJava as env argument");
    LOG_ALWAYS_FATAL_IF(!transactionObj,
                        "nullptr passed to ASurfaceTransaction_fromJava as transactionObj "
                        "argument");
    Transaction* transaction =
            android_view_SurfaceTransaction_getNativeSurfaceTransaction(env, transactionObj);
    LOG_ALWAYS_FATAL_IF(!transaction,
                        "surfaceControlObj passed to ASurfaceTransaction_fromJava is not valid");
    return reinterpret_cast<ASurfaceTransaction*>(transaction);
}

void ASurfaceTransaction_apply(ASurfaceTransaction* aSurfaceTransaction) {
    CHECK_NOT_NULL(aSurfaceTransaction);

    Transaction* transaction = ASurfaceTransaction_to_Transaction(aSurfaceTransaction);

    transaction->apply();
}

struct ASurfaceTransactionStats {
    std::unordered_map<ASurfaceControl*, ASurfaceControlStats> aSurfaceControlStats;
    int64_t latchTime;
    sp<Fence> presentFence;
    bool transactionCompleted;
};

int64_t ASurfaceTransactionStats_getLatchTime(ASurfaceTransactionStats* aSurfaceTransactionStats) {
    CHECK_NOT_NULL(aSurfaceTransactionStats);
    return aSurfaceTransactionStats->latchTime;
}

int ASurfaceTransactionStats_getPresentFenceFd(ASurfaceTransactionStats* aSurfaceTransactionStats) {
    CHECK_NOT_NULL(aSurfaceTransactionStats);
    LOG_ALWAYS_FATAL_IF(!aSurfaceTransactionStats->transactionCompleted,
                        "ASurfaceTransactionStats queried from an incomplete transaction callback");

    auto& presentFence = aSurfaceTransactionStats->presentFence;
    return (presentFence) ? presentFence->dup() : -1;
}

void ASurfaceTransactionStats_getASurfaceControls(ASurfaceTransactionStats* aSurfaceTransactionStats,
                                                  ASurfaceControl*** outASurfaceControls,
                                                  size_t* outASurfaceControlsSize) {
    CHECK_NOT_NULL(aSurfaceTransactionStats);
    CHECK_NOT_NULL(outASurfaceControls);
    CHECK_NOT_NULL(outASurfaceControlsSize);

    size_t size = aSurfaceTransactionStats->aSurfaceControlStats.size();

    SurfaceControl** surfaceControls = new SurfaceControl*[size];
    ASurfaceControl** aSurfaceControls = reinterpret_cast<ASurfaceControl**>(surfaceControls);

    size_t i = 0;
    for (auto& [aSurfaceControl, aSurfaceControlStats] : aSurfaceTransactionStats->aSurfaceControlStats) {
        aSurfaceControls[i] = aSurfaceControl;
        i++;
    }

    *outASurfaceControls = aSurfaceControls;
    *outASurfaceControlsSize = size;
}

int64_t ASurfaceTransactionStats_getAcquireTime(ASurfaceTransactionStats* aSurfaceTransactionStats,
                                                ASurfaceControl* aSurfaceControl) {
    CHECK_NOT_NULL(aSurfaceTransactionStats);
    CHECK_NOT_NULL(aSurfaceControl);

    const auto& aSurfaceControlStats =
            aSurfaceTransactionStats->aSurfaceControlStats.find(aSurfaceControl);
    LOG_ALWAYS_FATAL_IF(
            aSurfaceControlStats == aSurfaceTransactionStats->aSurfaceControlStats.end(),
            "ASurfaceControl not found");

    return ASurfaceControlStats_getAcquireTime(&aSurfaceControlStats->second);
}

int ASurfaceTransactionStats_getPreviousReleaseFenceFd(
            ASurfaceTransactionStats* aSurfaceTransactionStats, ASurfaceControl* aSurfaceControl) {
    CHECK_NOT_NULL(aSurfaceTransactionStats);
    CHECK_NOT_NULL(aSurfaceControl);
    LOG_ALWAYS_FATAL_IF(!aSurfaceTransactionStats->transactionCompleted,
                        "ASurfaceTransactionStats queried from an incomplete transaction callback");

    const auto& aSurfaceControlStats =
            aSurfaceTransactionStats->aSurfaceControlStats.find(aSurfaceControl);
    LOG_ALWAYS_FATAL_IF(
            aSurfaceControlStats == aSurfaceTransactionStats->aSurfaceControlStats.end(),
            "ASurfaceControl not found");

    auto& previousReleaseFence = aSurfaceControlStats->second.previousReleaseFence;
    return (previousReleaseFence) ? previousReleaseFence->dup() : -1;
}

void ASurfaceTransactionStats_releaseASurfaceControls(ASurfaceControl** aSurfaceControls) {
    CHECK_NOT_NULL(aSurfaceControls);

    SurfaceControl** surfaceControls = reinterpret_cast<SurfaceControl**>(aSurfaceControls);
    delete[] surfaceControls;
}

void ASurfaceTransaction_setOnComplete(ASurfaceTransaction* aSurfaceTransaction, void* context,
                                       ASurfaceTransaction_OnComplete func) {
    CHECK_NOT_NULL(aSurfaceTransaction);
    CHECK_NOT_NULL(func);

    TransactionCompletedCallbackTakesContext callback = [func](void* callback_context,
                                                               nsecs_t latchTime,
                                                               const sp<Fence>& presentFence,
                                                               const std::vector<SurfaceControlStats>& surfaceControlStats) {
        ASurfaceTransactionStats aSurfaceTransactionStats;

        aSurfaceTransactionStats.latchTime = latchTime;
        aSurfaceTransactionStats.presentFence = presentFence;
        aSurfaceTransactionStats.transactionCompleted = true;

        auto& aSurfaceControlStats = aSurfaceTransactionStats.aSurfaceControlStats;

        for (const auto& [surfaceControl, latchTime, acquireTimeOrFence, presentFence,
                  previousReleaseFence, transformHint, frameEvents, ignore] : surfaceControlStats) {
            ASurfaceControl* aSurfaceControl = reinterpret_cast<ASurfaceControl*>(surfaceControl.get());
            aSurfaceControlStats[aSurfaceControl].acquireTimeOrFence = acquireTimeOrFence;
            aSurfaceControlStats[aSurfaceControl].previousReleaseFence = previousReleaseFence;
        }

        (*func)(callback_context, &aSurfaceTransactionStats);
    };

    Transaction* transaction = ASurfaceTransaction_to_Transaction(aSurfaceTransaction);

    transaction->addTransactionCompletedCallback(callback, context);
}

void ASurfaceTransaction_reparent(ASurfaceTransaction* aSurfaceTransaction,
                                  ASurfaceControl* aSurfaceControl,
                                  ASurfaceControl* newParentASurfaceControl) {
    CHECK_NOT_NULL(aSurfaceTransaction);
    CHECK_NOT_NULL(aSurfaceControl);

    sp<SurfaceControl> surfaceControl = ASurfaceControl_to_SurfaceControl(aSurfaceControl);
    sp<SurfaceControl> newParentSurfaceControl = ASurfaceControl_to_SurfaceControl(
            newParentASurfaceControl);
    Transaction* transaction = ASurfaceTransaction_to_Transaction(aSurfaceTransaction);

    transaction->reparent(surfaceControl, newParentSurfaceControl);
}

void ASurfaceTransaction_setVisibility(ASurfaceTransaction* aSurfaceTransaction,
                                       ASurfaceControl* aSurfaceControl,
                                       ASurfaceTransactionVisibility visibility) {
    CHECK_NOT_NULL(aSurfaceTransaction);
    CHECK_NOT_NULL(aSurfaceControl);

    sp<SurfaceControl> surfaceControl = ASurfaceControl_to_SurfaceControl(aSurfaceControl);
    Transaction* transaction = ASurfaceTransaction_to_Transaction(aSurfaceTransaction);

    switch (visibility) {
    case ASURFACE_TRANSACTION_VISIBILITY_SHOW:
        transaction->show(surfaceControl);
        break;
    case ASURFACE_TRANSACTION_VISIBILITY_HIDE:
        transaction->hide(surfaceControl);
        break;
    default:
        LOG_ALWAYS_FATAL("invalid visibility %d", visibility);
    }
}

void ASurfaceTransaction_setZOrder(ASurfaceTransaction* aSurfaceTransaction,
                                   ASurfaceControl* aSurfaceControl,
                                   int32_t z_order) {
    CHECK_NOT_NULL(aSurfaceTransaction);
    CHECK_NOT_NULL(aSurfaceControl);

    sp<SurfaceControl> surfaceControl = ASurfaceControl_to_SurfaceControl(aSurfaceControl);
    Transaction* transaction = ASurfaceTransaction_to_Transaction(aSurfaceTransaction);

    transaction->setLayer(surfaceControl, z_order);
}

void ASurfaceTransaction_setBuffer(ASurfaceTransaction* aSurfaceTransaction,
                                   ASurfaceControl* aSurfaceControl,
                                   AHardwareBuffer* buffer, int acquire_fence_fd) {
    CHECK_NOT_NULL(aSurfaceTransaction);
    CHECK_NOT_NULL(aSurfaceControl);

    sp<SurfaceControl> surfaceControl = ASurfaceControl_to_SurfaceControl(aSurfaceControl);
    Transaction* transaction = ASurfaceTransaction_to_Transaction(aSurfaceTransaction);

    sp<GraphicBuffer> graphic_buffer(GraphicBuffer::fromAHardwareBuffer(buffer));

    std::optional<sp<Fence>> fence = std::nullopt;
    if (acquire_fence_fd != -1) {
        fence = new Fence(acquire_fence_fd);
    }
    transaction->setBuffer(surfaceControl, graphic_buffer, fence);
}

void ASurfaceTransaction_setGeometry(ASurfaceTransaction* aSurfaceTransaction,
                                     ASurfaceControl* aSurfaceControl, const ARect& source,
                                     const ARect& destination, int32_t transform) {
    CHECK_NOT_NULL(aSurfaceTransaction);
    CHECK_NOT_NULL(aSurfaceControl);
    CHECK_VALID_RECT(source);
    CHECK_VALID_RECT(destination);

    sp<SurfaceControl> surfaceControl = ASurfaceControl_to_SurfaceControl(aSurfaceControl);
    Transaction* transaction = ASurfaceTransaction_to_Transaction(aSurfaceTransaction);

    Rect sourceRect = static_cast<const Rect&>(source);
    Rect destRect = static_cast<const Rect&>(destination);
    // Adjust the source so its top and left are not negative
    sourceRect.left = std::max(sourceRect.left, 0);
    sourceRect.top = std::max(sourceRect.top, 0);

    if (!sourceRect.isValid()) {
        sourceRect.makeInvalid();
    }
    transaction->setBufferCrop(surfaceControl, sourceRect);
    transaction->setDestinationFrame(surfaceControl, destRect);
    transaction->setTransform(surfaceControl, transform);
    bool transformToInverseDisplay = (NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY & transform) ==
            NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY;
    transaction->setTransformToDisplayInverse(surfaceControl, transformToInverseDisplay);
}

void ASurfaceTransaction_setCrop(ASurfaceTransaction* aSurfaceTransaction,
                                 ASurfaceControl* aSurfaceControl, const ARect& crop) {
    CHECK_NOT_NULL(aSurfaceTransaction);
    CHECK_NOT_NULL(aSurfaceControl);
    CHECK_VALID_RECT(crop);

    sp<SurfaceControl> surfaceControl = ASurfaceControl_to_SurfaceControl(aSurfaceControl);
    Transaction* transaction = ASurfaceTransaction_to_Transaction(aSurfaceTransaction);

    transaction->setCrop(surfaceControl, static_cast<const Rect&>(crop));
}

void ASurfaceTransaction_setPosition(ASurfaceTransaction* aSurfaceTransaction,
                                     ASurfaceControl* aSurfaceControl, int32_t x, int32_t y) {
    CHECK_NOT_NULL(aSurfaceTransaction);
    CHECK_NOT_NULL(aSurfaceControl);

    sp<SurfaceControl> surfaceControl = ASurfaceControl_to_SurfaceControl(aSurfaceControl);
    Transaction* transaction = ASurfaceTransaction_to_Transaction(aSurfaceTransaction);

    transaction->setPosition(surfaceControl, x, y);
}

void ASurfaceTransaction_setBufferTransform(ASurfaceTransaction* aSurfaceTransaction,
                                            ASurfaceControl* aSurfaceControl, int32_t transform) {
    CHECK_NOT_NULL(aSurfaceTransaction);
    CHECK_NOT_NULL(aSurfaceControl);

    sp<SurfaceControl> surfaceControl = ASurfaceControl_to_SurfaceControl(aSurfaceControl);
    Transaction* transaction = ASurfaceTransaction_to_Transaction(aSurfaceTransaction);

    transaction->setTransform(surfaceControl, transform);
    bool transformToInverseDisplay = (NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY & transform) ==
            NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY;
    transaction->setTransformToDisplayInverse(surfaceControl, transformToInverseDisplay);
}

void ASurfaceTransaction_setScale(ASurfaceTransaction* aSurfaceTransaction,
                                  ASurfaceControl* aSurfaceControl, float xScale, float yScale) {
    CHECK_NOT_NULL(aSurfaceTransaction);
    CHECK_NOT_NULL(aSurfaceControl);
    LOG_ALWAYS_FATAL_IF(xScale < 0, "negative value passed in for xScale");
    LOG_ALWAYS_FATAL_IF(yScale < 0, "negative value passed in for yScale");

    sp<SurfaceControl> surfaceControl = ASurfaceControl_to_SurfaceControl(aSurfaceControl);
    Transaction* transaction = ASurfaceTransaction_to_Transaction(aSurfaceTransaction);

    transaction->setMatrix(surfaceControl, xScale, 0, 0, yScale);
}

void ASurfaceTransaction_setBufferTransparency(ASurfaceTransaction* aSurfaceTransaction,
                                               ASurfaceControl* aSurfaceControl,
                                               ASurfaceTransactionTransparency transparency) {
    CHECK_NOT_NULL(aSurfaceTransaction);
    CHECK_NOT_NULL(aSurfaceControl);

    sp<SurfaceControl> surfaceControl = ASurfaceControl_to_SurfaceControl(aSurfaceControl);
    Transaction* transaction = ASurfaceTransaction_to_Transaction(aSurfaceTransaction);

    uint32_t flags = (transparency == ASURFACE_TRANSACTION_TRANSPARENCY_OPAQUE) ?
                      layer_state_t::eLayerOpaque : 0;
    transaction->setFlags(surfaceControl, flags, layer_state_t::eLayerOpaque);
}

void ASurfaceTransaction_setDamageRegion(ASurfaceTransaction* aSurfaceTransaction,
                                         ASurfaceControl* aSurfaceControl,
                                         const ARect rects[], uint32_t count) {
    CHECK_NOT_NULL(aSurfaceTransaction);
    CHECK_NOT_NULL(aSurfaceControl);

    sp<SurfaceControl> surfaceControl = ASurfaceControl_to_SurfaceControl(aSurfaceControl);
    Transaction* transaction = ASurfaceTransaction_to_Transaction(aSurfaceTransaction);

    Region region;
    for (uint32_t i = 0; i < count; ++i) {
        region.orSelf(static_cast<const Rect&>(rects[i]));
    }

    // Hardware composer interprets a DamageRegion with a single Rect of {0,0,0,0} to be an
    // undamaged region and {0,0,-1,-1} to be a fully damaged buffer. This is a confusing
    // distinction for a public api. Instead, default both cases to be a fully damaged buffer.
    if (count == 1 && region.getBounds().isEmpty()) {
        transaction->setSurfaceDamageRegion(surfaceControl, Region::INVALID_REGION);
        return;
    }

    transaction->setSurfaceDamageRegion(surfaceControl, region);
}

void ASurfaceTransaction_setDesiredPresentTime(ASurfaceTransaction* aSurfaceTransaction,
                                         int64_t desiredPresentTime) {
    CHECK_NOT_NULL(aSurfaceTransaction);

    Transaction* transaction = ASurfaceTransaction_to_Transaction(aSurfaceTransaction);

    transaction->setDesiredPresentTime(static_cast<nsecs_t>(desiredPresentTime));
}

void ASurfaceTransaction_setBufferAlpha(ASurfaceTransaction* aSurfaceTransaction,
                                         ASurfaceControl* aSurfaceControl,
                                         float alpha) {
    CHECK_NOT_NULL(aSurfaceTransaction);
    CHECK_NOT_NULL(aSurfaceControl);

    LOG_ALWAYS_FATAL_IF(alpha < 0.0 || alpha > 1.0, "invalid alpha");

    sp<SurfaceControl> surfaceControl = ASurfaceControl_to_SurfaceControl(aSurfaceControl);
    Transaction* transaction = ASurfaceTransaction_to_Transaction(aSurfaceTransaction);

    transaction->setAlpha(surfaceControl, alpha);
}

void ASurfaceTransaction_setBufferDataSpace(ASurfaceTransaction* aSurfaceTransaction,
                                         ASurfaceControl* aSurfaceControl,
                                         ADataSpace aDataSpace) {
    CHECK_NOT_NULL(aSurfaceTransaction);
    CHECK_NOT_NULL(aSurfaceControl);

    sp<SurfaceControl> surfaceControl = ASurfaceControl_to_SurfaceControl(aSurfaceControl);
    Transaction* transaction = ASurfaceTransaction_to_Transaction(aSurfaceTransaction);
    transaction->setDataspace(surfaceControl, static_cast<ui::Dataspace>(aDataSpace));
}

void ASurfaceTransaction_setHdrMetadata_smpte2086(ASurfaceTransaction* aSurfaceTransaction,
                                                  ASurfaceControl* aSurfaceControl,
                                                  struct AHdrMetadata_smpte2086* metadata) {
    CHECK_NOT_NULL(aSurfaceTransaction);
    CHECK_NOT_NULL(aSurfaceControl);

    sp<SurfaceControl> surfaceControl = ASurfaceControl_to_SurfaceControl(aSurfaceControl);
    Transaction* transaction = ASurfaceTransaction_to_Transaction(aSurfaceTransaction);

    HdrMetadata hdrMetadata;

    if (metadata) {
        hdrMetadata.smpte2086.displayPrimaryRed.x = metadata->displayPrimaryRed.x;
        hdrMetadata.smpte2086.displayPrimaryRed.y = metadata->displayPrimaryRed.y;
        hdrMetadata.smpte2086.displayPrimaryGreen.x = metadata->displayPrimaryGreen.x;
        hdrMetadata.smpte2086.displayPrimaryGreen.y = metadata->displayPrimaryGreen.y;
        hdrMetadata.smpte2086.displayPrimaryBlue.x = metadata->displayPrimaryBlue.x;
        hdrMetadata.smpte2086.displayPrimaryBlue.y = metadata->displayPrimaryBlue.y;
        hdrMetadata.smpte2086.whitePoint.x = metadata->whitePoint.x;
        hdrMetadata.smpte2086.whitePoint.y = metadata->whitePoint.y;
        hdrMetadata.smpte2086.minLuminance = metadata->minLuminance;
        hdrMetadata.smpte2086.maxLuminance = metadata->maxLuminance;

        hdrMetadata.validTypes |= HdrMetadata::SMPTE2086;
    } else {
        hdrMetadata.validTypes &= ~HdrMetadata::SMPTE2086;
    }

    transaction->setHdrMetadata(surfaceControl, hdrMetadata);
}

void ASurfaceTransaction_setHdrMetadata_cta861_3(ASurfaceTransaction* aSurfaceTransaction,
                                                 ASurfaceControl* aSurfaceControl,
                                                 struct AHdrMetadata_cta861_3* metadata) {
    CHECK_NOT_NULL(aSurfaceTransaction);
    CHECK_NOT_NULL(aSurfaceControl);

    sp<SurfaceControl> surfaceControl = ASurfaceControl_to_SurfaceControl(aSurfaceControl);
    Transaction* transaction = ASurfaceTransaction_to_Transaction(aSurfaceTransaction);

    HdrMetadata hdrMetadata;

    if (metadata) {
        hdrMetadata.cta8613.maxContentLightLevel = metadata->maxContentLightLevel;
        hdrMetadata.cta8613.maxFrameAverageLightLevel = metadata->maxFrameAverageLightLevel;

        hdrMetadata.validTypes |= HdrMetadata::CTA861_3;
    } else {
        hdrMetadata.validTypes &= ~HdrMetadata::CTA861_3;
    }

    transaction->setHdrMetadata(surfaceControl, hdrMetadata);
}

void ASurfaceTransaction_setExtendedRangeBrightness(ASurfaceTransaction* aSurfaceTransaction,
                                                    ASurfaceControl* aSurfaceControl,
                                                    float currentBufferRatio, float desiredRatio) {
    CHECK_NOT_NULL(aSurfaceTransaction);
    CHECK_NOT_NULL(aSurfaceControl);

    if (!isfinite(currentBufferRatio) || currentBufferRatio < 1.0f) {
        LOG_ALWAYS_FATAL("setExtendedRangeBrightness, currentBufferRatio %f isn't finite or >= "
                         "1.0f",
                         currentBufferRatio);
        return;
    }

    if (!isfinite(desiredRatio) || desiredRatio < 1.0f) {
        LOG_ALWAYS_FATAL("setExtendedRangeBrightness, desiredRatio %f isn't finite or >= 1.0f",
                         desiredRatio);
        return;
    }

    sp<SurfaceControl> surfaceControl = ASurfaceControl_to_SurfaceControl(aSurfaceControl);
    Transaction* transaction = ASurfaceTransaction_to_Transaction(aSurfaceTransaction);

    transaction->setExtendedRangeBrightness(surfaceControl, currentBufferRatio, desiredRatio);
}

void ASurfaceTransaction_setDesiredHdrHeadroom(ASurfaceTransaction* aSurfaceTransaction,
                                               ASurfaceControl* aSurfaceControl,
                                               float desiredRatio) {
    CHECK_NOT_NULL(aSurfaceTransaction);
    CHECK_NOT_NULL(aSurfaceControl);

    if (!isfinite(desiredRatio) || (desiredRatio < 1.0f && desiredRatio > 0.0f)) {
        LOG_ALWAYS_FATAL("setDesiredHdrHeadroom, desiredRatio isn't finite && >= 1.0f or 0, got %f",
                         desiredRatio);
        return;
    }

    sp<SurfaceControl> surfaceControl = ASurfaceControl_to_SurfaceControl(aSurfaceControl);
    Transaction* transaction = ASurfaceTransaction_to_Transaction(aSurfaceTransaction);

    transaction->setDesiredHdrHeadroom(surfaceControl, desiredRatio);
}

void ASurfaceTransaction_setColor(ASurfaceTransaction* aSurfaceTransaction,
                                  ASurfaceControl* aSurfaceControl,
                                  float r, float g, float b, float alpha,
                                  ADataSpace dataspace) {
    CHECK_NOT_NULL(aSurfaceTransaction);
    CHECK_NOT_NULL(aSurfaceControl);

    sp<SurfaceControl> surfaceControl = ASurfaceControl_to_SurfaceControl(aSurfaceControl);
    Transaction* transaction = ASurfaceTransaction_to_Transaction(aSurfaceTransaction);

    half3 color;
    color.r = r;
    color.g = g;
    color.b = b;

    transaction->setBackgroundColor(surfaceControl, color, alpha,
                                    static_cast<ui::Dataspace>(dataspace));
}

void ASurfaceTransaction_setFrameRate(ASurfaceTransaction* aSurfaceTransaction,
                                      ASurfaceControl* aSurfaceControl, float frameRate,
                                      int8_t compatibility) {
    ASurfaceTransaction_setFrameRateWithChangeStrategy(
            aSurfaceTransaction, aSurfaceControl, frameRate, compatibility,
            ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS);
}

void ASurfaceTransaction_setFrameRateWithChangeStrategy(ASurfaceTransaction* aSurfaceTransaction,
                                                        ASurfaceControl* aSurfaceControl,
                                                        float frameRate, int8_t compatibility,
                                                        int8_t changeFrameRateStrategy) {
    CHECK_NOT_NULL(aSurfaceTransaction);
    CHECK_NOT_NULL(aSurfaceControl);
    Transaction* transaction = ASurfaceTransaction_to_Transaction(aSurfaceTransaction);
    sp<SurfaceControl> surfaceControl = ASurfaceControl_to_SurfaceControl(aSurfaceControl);
    transaction->setFrameRate(surfaceControl, frameRate, compatibility, changeFrameRateStrategy);
}

void ASurfaceTransaction_clearFrameRate(ASurfaceTransaction* aSurfaceTransaction,
                                        ASurfaceControl* aSurfaceControl) {
    CHECK_NOT_NULL(aSurfaceTransaction);
    CHECK_NOT_NULL(aSurfaceControl);
    Transaction* transaction = ASurfaceTransaction_to_Transaction(aSurfaceTransaction);
    sp<SurfaceControl> surfaceControl = ASurfaceControl_to_SurfaceControl(aSurfaceControl);
    transaction->setFrameRate(surfaceControl, 0, ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT,
                              ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS);
}

void ASurfaceTransaction_setEnableBackPressure(ASurfaceTransaction* aSurfaceTransaction,
                                               ASurfaceControl* aSurfaceControl,
                                               bool enableBackpressure) {
    CHECK_NOT_NULL(aSurfaceControl);
    CHECK_NOT_NULL(aSurfaceTransaction);

    sp<SurfaceControl> surfaceControl = ASurfaceControl_to_SurfaceControl(aSurfaceControl);
    Transaction* transaction = ASurfaceTransaction_to_Transaction(aSurfaceTransaction);

    const uint32_t flags = enableBackpressure ?
                      layer_state_t::eEnableBackpressure : 0;
    transaction->setFlags(surfaceControl, flags, layer_state_t::eEnableBackpressure);
}

void ASurfaceTransaction_setOnCommit(ASurfaceTransaction* aSurfaceTransaction, void* context,
                                     ASurfaceTransaction_OnCommit func) {
    CHECK_NOT_NULL(aSurfaceTransaction);
    CHECK_NOT_NULL(func);

    TransactionCompletedCallbackTakesContext callback =
            [func](void* callback_context, nsecs_t latchTime, const sp<Fence>& /* presentFence */,
                   const std::vector<SurfaceControlStats>& surfaceControlStats) {
                ASurfaceTransactionStats aSurfaceTransactionStats;
                aSurfaceTransactionStats.latchTime = latchTime;
                aSurfaceTransactionStats.transactionCompleted = false;

                auto& aSurfaceControlStats = aSurfaceTransactionStats.aSurfaceControlStats;
                for (const auto& [surfaceControl, latchTime, acquireTimeOrFence, presentFence,
                              previousReleaseFence, transformHint, frameEvents, ignore] :
                     surfaceControlStats) {
                    ASurfaceControl* aSurfaceControl =
                            reinterpret_cast<ASurfaceControl*>(surfaceControl.get());
                    aSurfaceControlStats[aSurfaceControl].acquireTimeOrFence = acquireTimeOrFence;
                }

                (*func)(callback_context, &aSurfaceTransactionStats);
            };

    Transaction* transaction = ASurfaceTransaction_to_Transaction(aSurfaceTransaction);

    transaction->addTransactionCommittedCallback(callback, context);
}

void ASurfaceTransaction_setFrameTimeline(ASurfaceTransaction* aSurfaceTransaction,
                                          AVsyncId vsyncId) {
    CHECK_NOT_NULL(aSurfaceTransaction);
    const auto startTime = AChoreographer_getStartTimeNanosForVsyncId(vsyncId);
    FrameTimelineInfo ftInfo;
    ftInfo.vsyncId = vsyncId;
    ftInfo.startTimeNanos = startTime;
    ASurfaceTransaction_to_Transaction(aSurfaceTransaction)->setFrameTimelineInfo(ftInfo);
}
