/*
 * Copyright (C) 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.
 */

#include <gui/test/CallbackUtils.h>
#include "LayerTransactionTest.h"

using namespace std::chrono_literals;

namespace android {

using android::hardware::graphics::common::V1_1::BufferUsage;

::testing::Environment* const binderEnv =
        ::testing::AddGlobalTestEnvironment(new BinderEnvironment());

// b/181132765 - disabled until cuttlefish failures are investigated
class ReleaseBufferCallbackHelper {
public:
    static void function(void* callbackContext, ReleaseCallbackId callbackId,
                         const sp<Fence>& releaseFence,
                         std::optional<uint32_t> /*currentMaxAcquiredBufferCount*/) {
        if (!callbackContext) {
            FAIL() << "failed to get callback context";
        }
        ReleaseBufferCallbackHelper* helper =
                static_cast<ReleaseBufferCallbackHelper*>(callbackContext);
        std::lock_guard lock(helper->mMutex);
        helper->mCallbackDataQueue.emplace(callbackId, releaseFence);
        helper->mConditionVariable.notify_all();
    }

    void getCallbackData(ReleaseCallbackId* callbackId) {
        std::unique_lock lock(mMutex);
        if (mCallbackDataQueue.empty()) {
            if (!mConditionVariable.wait_for(lock, std::chrono::seconds(3),
                                             [&] { return !mCallbackDataQueue.empty(); })) {
                FAIL() << "failed to get releaseBuffer callback";
            }
        }

        auto callbackData = mCallbackDataQueue.front();
        mCallbackDataQueue.pop();
        *callbackId = callbackData.first;
    }

    void verifyNoCallbacks() {
        // Wait to see if there are extra callbacks
        std::this_thread::sleep_for(300ms);

        std::lock_guard lock(mMutex);
        EXPECT_EQ(mCallbackDataQueue.size(), 0U) << "extra callbacks received";
        mCallbackDataQueue = {};
    }

    android::ReleaseBufferCallback getCallback() {
        return std::bind(function, static_cast<void*>(this) /* callbackContext */,
                         std::placeholders::_1, std::placeholders::_2, std::placeholders::_3);
    }

    std::mutex mMutex;
    std::condition_variable mConditionVariable;
    std::queue<std::pair<ReleaseCallbackId, sp<Fence>>> mCallbackDataQueue;
};

class ReleaseBufferCallbackTest : public LayerTransactionTest {
public:
    virtual sp<SurfaceControl> createBufferStateLayer() {
        return createLayer(mClient, "test", 0, 0, ISurfaceComposerClient::eFXSurfaceBufferState);
    }

    static void submitBuffer(const sp<SurfaceControl>& layer, sp<GraphicBuffer> buffer,
                             sp<Fence> fence, CallbackHelper& callback, const ReleaseCallbackId& id,
                             ReleaseBufferCallbackHelper& releaseCallback) {
        Transaction t;
        t.setBuffer(layer, buffer, fence, id.framenumber, releaseCallback.getCallback());
        t.addTransactionCompletedCallback(callback.function, callback.getContext());
        t.apply();
    }

    static void waitForCallback(CallbackHelper& helper, const ExpectedResult& expectedResult) {
        CallbackData callbackData;
        helper.getCallbackData(&callbackData);
        expectedResult.verifyCallbackData(callbackData);
    }

    static void waitForReleaseBufferCallback(ReleaseBufferCallbackHelper& releaseCallback,
                                             const ReleaseCallbackId& expectedReleaseBufferId) {
        ReleaseCallbackId actualReleaseBufferId;
        releaseCallback.getCallbackData(&actualReleaseBufferId);
        EXPECT_EQ(expectedReleaseBufferId, actualReleaseBufferId);
        releaseCallback.verifyNoCallbacks();
    }
    static ReleaseBufferCallbackHelper* getReleaseBufferCallbackHelper() {
        static std::vector<ReleaseBufferCallbackHelper*> sCallbacks;
        sCallbacks.emplace_back(new ReleaseBufferCallbackHelper());
        return sCallbacks.back();
    }

    static sp<GraphicBuffer> getBuffer() {
        return new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1,
                                 BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
                                         BufferUsage::COMPOSER_OVERLAY,
                                 "test");
    }
    static uint64_t generateFrameNumber() {
        static uint64_t sFrameNumber = 0;
        return ++sFrameNumber;
    }
};

TEST_F(ReleaseBufferCallbackTest, DISABLED_PresentBuffer) {
    sp<SurfaceControl> layer = createBufferStateLayer();
    CallbackHelper transactionCallback;
    ReleaseBufferCallbackHelper* releaseCallback = getReleaseBufferCallbackHelper();

    // If a buffer is being presented, we should not emit a release callback.
    sp<GraphicBuffer> firstBuffer = getBuffer();
    ReleaseCallbackId firstBufferCallbackId(firstBuffer->getId(), generateFrameNumber());
    submitBuffer(layer, firstBuffer, Fence::NO_FENCE, transactionCallback, firstBufferCallbackId,
                 *releaseCallback);
    ExpectedResult expected;
    expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer,
                        ExpectedResult::Buffer::NOT_ACQUIRED);
    ASSERT_NO_FATAL_FAILURE(waitForCallback(transactionCallback, expected));
    EXPECT_NO_FATAL_FAILURE(releaseCallback->verifyNoCallbacks());

    // if state doesn't change, no release callbacks are expected
    Transaction t;
    t.addTransactionCompletedCallback(transactionCallback.function,
                                      transactionCallback.getContext());
    t.apply();
    ASSERT_NO_FATAL_FAILURE(waitForCallback(transactionCallback, ExpectedResult()));
    EXPECT_NO_FATAL_FAILURE(releaseCallback->verifyNoCallbacks());

    // If a presented buffer is replaced, we should emit a release callback for the
    // previously presented buffer.
    sp<GraphicBuffer> secondBuffer = getBuffer();
    ReleaseCallbackId secondBufferCallbackId(secondBuffer->getId(), generateFrameNumber());
    submitBuffer(layer, secondBuffer, Fence::NO_FENCE, transactionCallback, secondBufferCallbackId,
                 *releaseCallback);
    expected = ExpectedResult();
    expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer,
                        ExpectedResult::Buffer::NOT_ACQUIRED,
                        ExpectedResult::PreviousBuffer::RELEASED);
    ASSERT_NO_FATAL_FAILURE(waitForCallback(transactionCallback, expected));
    ASSERT_NO_FATAL_FAILURE(waitForReleaseBufferCallback(*releaseCallback, firstBufferCallbackId));
}

TEST_F(ReleaseBufferCallbackTest, DISABLED_OffScreenLayer) {
    sp<SurfaceControl> layer = createBufferStateLayer();

    CallbackHelper transactionCallback;
    ReleaseBufferCallbackHelper* releaseCallback = getReleaseBufferCallbackHelper();

    // If a buffer is being presented, we should not emit a release callback.
    sp<GraphicBuffer> firstBuffer = getBuffer();
    ReleaseCallbackId firstBufferCallbackId(firstBuffer->getId(), generateFrameNumber());
    submitBuffer(layer, firstBuffer, Fence::NO_FENCE, transactionCallback, firstBufferCallbackId,
                 *releaseCallback);
    ExpectedResult expected;
    expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer,
                        ExpectedResult::Buffer::NOT_ACQUIRED);
    ASSERT_NO_FATAL_FAILURE(waitForCallback(transactionCallback, expected));
    releaseCallback->verifyNoCallbacks();

    // If a layer is parented offscreen then it should not emit a callback since sf still owns
    // the buffer and can render it again.
    Transaction t;
    t.reparent(layer, nullptr);
    t.addTransactionCompletedCallback(transactionCallback.function,
                                      transactionCallback.getContext());
    t.apply();
    expected = ExpectedResult();
    expected.addSurface(ExpectedResult::Transaction::NOT_PRESENTED, layer,
                        ExpectedResult::Buffer::NOT_ACQUIRED,
                        ExpectedResult::PreviousBuffer::NOT_RELEASED);
    ASSERT_NO_FATAL_FAILURE(waitForCallback(transactionCallback, expected));
    ASSERT_NO_FATAL_FAILURE(releaseCallback->verifyNoCallbacks());

    // If a presented buffer is replaced, we should emit a release callback for the
    // previously presented buffer.
    sp<GraphicBuffer> secondBuffer = getBuffer();
    ReleaseCallbackId secondBufferCallbackId(secondBuffer->getId(), generateFrameNumber());
    submitBuffer(layer, secondBuffer, Fence::NO_FENCE, transactionCallback, secondBufferCallbackId,
                 *releaseCallback);
    expected = ExpectedResult();
    expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer,
                        ExpectedResult::Buffer::NOT_ACQUIRED,
                        ExpectedResult::PreviousBuffer::NOT_RELEASED);
    ASSERT_NO_FATAL_FAILURE(waitForCallback(transactionCallback, expected));
    ASSERT_NO_FATAL_FAILURE(waitForReleaseBufferCallback(*releaseCallback, firstBufferCallbackId));

    // If continue to submit buffer we continue to get release callbacks
    sp<GraphicBuffer> thirdBuffer = getBuffer();
    ReleaseCallbackId thirdBufferCallbackId(secondBuffer->getId(), generateFrameNumber());
    submitBuffer(layer, thirdBuffer, Fence::NO_FENCE, transactionCallback, thirdBufferCallbackId,
                 *releaseCallback);
    expected = ExpectedResult();
    expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer,
                        ExpectedResult::Buffer::NOT_ACQUIRED,
                        ExpectedResult::PreviousBuffer::NOT_RELEASED);
    ASSERT_NO_FATAL_FAILURE(waitForCallback(transactionCallback, expected));
    ASSERT_NO_FATAL_FAILURE(waitForReleaseBufferCallback(*releaseCallback, secondBufferCallbackId));
}

TEST_F(ReleaseBufferCallbackTest, DISABLED_LayerLifecycle_layerdestroy) {
    sp<SurfaceControl> layer = createBufferStateLayer();
    CallbackHelper* transactionCallback = new CallbackHelper();
    ReleaseBufferCallbackHelper* releaseCallback = getReleaseBufferCallbackHelper();

    // If a buffer is being presented, we should not emit a release callback.
    sp<GraphicBuffer> firstBuffer = getBuffer();
    ReleaseCallbackId firstBufferCallbackId(firstBuffer->getId(), generateFrameNumber());
    submitBuffer(layer, firstBuffer, Fence::NO_FENCE, *transactionCallback, firstBufferCallbackId,
                 *releaseCallback);
    {
        ExpectedResult expected;
        expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer,
                            ExpectedResult::Buffer::NOT_ACQUIRED);
        ASSERT_NO_FATAL_FAILURE(waitForCallback(*transactionCallback, expected));
        ASSERT_NO_FATAL_FAILURE(releaseCallback->verifyNoCallbacks());
    }

    // Destroying a currently presenting layer emits a callback.
    Transaction t;
    t.reparent(layer, nullptr);
    t.apply();
    layer = nullptr;

    ASSERT_NO_FATAL_FAILURE(waitForReleaseBufferCallback(*releaseCallback, firstBufferCallbackId));
}

// Destroying a never presented layer emits a callback.
TEST_F(ReleaseBufferCallbackTest, DISABLED_LayerLifecycle_OffScreenLayerDestroy) {
    sp<SurfaceControl> layer = createBufferStateLayer();

    // make layer offscreen
    Transaction t;
    t.reparent(layer, nullptr);
    t.apply();

    CallbackHelper* transactionCallback = new CallbackHelper();
    ReleaseBufferCallbackHelper* releaseCallback = getReleaseBufferCallbackHelper();

    // Submitting a buffer does not emit a callback.
    sp<GraphicBuffer> firstBuffer = getBuffer();
    ReleaseCallbackId firstBufferCallbackId(firstBuffer->getId(), generateFrameNumber());
    submitBuffer(layer, firstBuffer, Fence::NO_FENCE, *transactionCallback, firstBufferCallbackId,
                 *releaseCallback);
    {
        ExpectedResult expected;
        expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer,
                            ExpectedResult::Buffer::NOT_ACQUIRED);
        ASSERT_NO_FATAL_FAILURE(waitForCallback(*transactionCallback, expected));
        ASSERT_NO_FATAL_FAILURE(releaseCallback->verifyNoCallbacks());
    }

    // Submitting a second buffer will replace the drawing state buffer and emit a callback.
    sp<GraphicBuffer> secondBuffer = getBuffer();
    ReleaseCallbackId secondBufferCallbackId(secondBuffer->getId(), generateFrameNumber());
    submitBuffer(layer, secondBuffer, Fence::NO_FENCE, *transactionCallback, secondBufferCallbackId,
                 *releaseCallback);
    {
        ExpectedResult expected;
        expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer,
                            ExpectedResult::Buffer::NOT_ACQUIRED);
        ASSERT_NO_FATAL_FAILURE(waitForCallback(*transactionCallback, expected));
        ASSERT_NO_FATAL_FAILURE(
                waitForReleaseBufferCallback(*releaseCallback, firstBufferCallbackId));
    }

    // Destroying the offscreen layer emits a callback.
    layer = nullptr;
    ASSERT_NO_FATAL_FAILURE(waitForReleaseBufferCallback(*releaseCallback, secondBufferCallbackId));
}

TEST_F(ReleaseBufferCallbackTest, DISABLED_FrameDropping) {
    sp<SurfaceControl> layer = createBufferStateLayer();
    CallbackHelper transactionCallback;
    ReleaseBufferCallbackHelper* releaseCallback = getReleaseBufferCallbackHelper();

    // If a buffer is being presented, we should not emit a release callback.
    sp<GraphicBuffer> firstBuffer = getBuffer();
    ReleaseCallbackId firstBufferCallbackId(firstBuffer->getId(), generateFrameNumber());

    // Try to present 100ms in the future
    nsecs_t time = systemTime() + std::chrono::nanoseconds(100ms).count();

    Transaction t;
    t.setBuffer(layer, firstBuffer, std::nullopt, firstBufferCallbackId.framenumber,
                releaseCallback->getCallback());
    t.addTransactionCompletedCallback(transactionCallback.function,
                                      transactionCallback.getContext());
    t.setDesiredPresentTime(time);
    t.apply();

    ExpectedResult expected;
    expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer,
                        ExpectedResult::Buffer::NOT_ACQUIRED);
    ASSERT_NO_FATAL_FAILURE(waitForCallback(transactionCallback, expected));
    EXPECT_NO_FATAL_FAILURE(releaseCallback->verifyNoCallbacks());

    // Dropping frames in transaction queue emits a callback
    sp<GraphicBuffer> secondBuffer = getBuffer();
    ReleaseCallbackId secondBufferCallbackId(secondBuffer->getId(), generateFrameNumber());
    t.setBuffer(layer, secondBuffer, std::nullopt, secondBufferCallbackId.framenumber,
                releaseCallback->getCallback());
    t.addTransactionCompletedCallback(transactionCallback.function,
                                      transactionCallback.getContext());
    t.setDesiredPresentTime(time);
    t.apply();

    expected = ExpectedResult();
    expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer,
                        ExpectedResult::Buffer::NOT_ACQUIRED,
                        ExpectedResult::PreviousBuffer::RELEASED);
    ASSERT_NO_FATAL_FAILURE(waitForCallback(transactionCallback, expected));
    ASSERT_NO_FATAL_FAILURE(waitForReleaseBufferCallback(*releaseCallback, firstBufferCallbackId));
}

TEST_F(ReleaseBufferCallbackTest, DISABLED_Merge_Different_Processes) {
    sp<TransactionCompletedListener> firstCompletedListener = new TransactionCompletedListener();
    sp<TransactionCompletedListener> secondCompletedListener = new TransactionCompletedListener();

    CallbackHelper callback1, callback2;

    TransactionCompletedListener::setInstance(firstCompletedListener);

    sp<SurfaceControl> layer = createBufferStateLayer();
    ReleaseBufferCallbackHelper* releaseCallback = getReleaseBufferCallbackHelper();

    sp<GraphicBuffer> firstBuffer = getBuffer();
    ReleaseCallbackId firstBufferCallbackId(firstBuffer->getId(), generateFrameNumber());

    // Send initial buffer for the layer
    submitBuffer(layer, firstBuffer, Fence::NO_FENCE, callback1, firstBufferCallbackId,
                 *releaseCallback);

    ExpectedResult expected;
    expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer,
                        ExpectedResult::Buffer::NOT_ACQUIRED);
    ASSERT_NO_FATAL_FAILURE(waitForCallback(callback1, expected));

    // Sent a second buffer to allow the first buffer to get released.
    sp<GraphicBuffer> secondBuffer = getBuffer();
    ReleaseCallbackId secondBufferCallbackId(secondBuffer->getId(), generateFrameNumber());

    Transaction transaction1;
    transaction1.setBuffer(layer, secondBuffer, std::nullopt, secondBufferCallbackId.framenumber,
                           releaseCallback->getCallback());
    transaction1.addTransactionCompletedCallback(callback1.function, callback1.getContext());

    // Set a different TransactionCompletedListener to mimic a second process
    TransactionCompletedListener::setInstance(secondCompletedListener);

    // Make sure the second "process" has a callback set up.
    Transaction transaction2;
    transaction2.addTransactionCompletedCallback(callback2.function, callback2.getContext());

    // This merging order, merge transaction1 first then transaction2, seems to ensure the listener
    // for transaction2 is ordered first. This makes sure the wrong process is added first to the
    // layer's vector of listeners. With the bug, only the secondCompletedListener will get the
    // release callback id, since it's ordered first. Then firstCompletedListener would fail to get
    // the release callback id and not invoke the release callback.
    Transaction().merge(std::move(transaction1)).merge(std::move(transaction2)).apply();

    expected = ExpectedResult();
    expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer,
                        ExpectedResult::Buffer::NOT_ACQUIRED,
                        ExpectedResult::PreviousBuffer::RELEASED);
    ASSERT_NO_FATAL_FAILURE(waitForCallback(callback1, expected));
    ASSERT_NO_FATAL_FAILURE(waitForReleaseBufferCallback(*releaseCallback, firstBufferCallbackId));
}

TEST_F(ReleaseBufferCallbackTest, DISABLED_SetBuffer_OverwriteBuffers) {
    sp<SurfaceControl> layer = createBufferStateLayer();
    ReleaseBufferCallbackHelper* releaseCallback = getReleaseBufferCallbackHelper();

    sp<GraphicBuffer> firstBuffer = getBuffer();
    ReleaseCallbackId firstBufferCallbackId(firstBuffer->getId(), generateFrameNumber());

    // Create transaction with a buffer.
    Transaction transaction;
    transaction.setBuffer(layer, firstBuffer, std::nullopt, firstBufferCallbackId.framenumber,
                          releaseCallback->getCallback());

    sp<GraphicBuffer> secondBuffer = getBuffer();
    ReleaseCallbackId secondBufferCallbackId(secondBuffer->getId(), generateFrameNumber());

    // Call setBuffer on the same transaction with a different buffer.
    transaction.setBuffer(layer, secondBuffer, std::nullopt, secondBufferCallbackId.framenumber,
                          releaseCallback->getCallback());

    ASSERT_NO_FATAL_FAILURE(waitForReleaseBufferCallback(*releaseCallback, firstBufferCallbackId));
}

TEST_F(ReleaseBufferCallbackTest, DISABLED_Merge_Transactions_OverwriteBuffers) {
    sp<SurfaceControl> layer = createBufferStateLayer();
    ReleaseBufferCallbackHelper* releaseCallback = getReleaseBufferCallbackHelper();

    sp<GraphicBuffer> firstBuffer = getBuffer();
    ReleaseCallbackId firstBufferCallbackId(firstBuffer->getId(), generateFrameNumber());

    // Create transaction with a buffer.
    Transaction transaction1;
    transaction1.setBuffer(layer, firstBuffer, std::nullopt, firstBufferCallbackId.framenumber,
                           releaseCallback->getCallback());

    sp<GraphicBuffer> secondBuffer = getBuffer();
    ReleaseCallbackId secondBufferCallbackId(secondBuffer->getId(), generateFrameNumber());

    // Create a second transaction with a new buffer for the same layer.
    Transaction transaction2;
    transaction2.setBuffer(layer, secondBuffer, std::nullopt, secondBufferCallbackId.framenumber,
                           releaseCallback->getCallback());

    // merge transaction1 into transaction2 so ensure we get a proper buffer release callback.
    transaction1.merge(std::move(transaction2));
    ASSERT_NO_FATAL_FAILURE(waitForReleaseBufferCallback(*releaseCallback, firstBufferCallbackId));
}

TEST_F(ReleaseBufferCallbackTest, DISABLED_MergeBuffers_Different_Processes) {
    sp<TransactionCompletedListener> firstCompletedListener = new TransactionCompletedListener();
    sp<TransactionCompletedListener> secondCompletedListener = new TransactionCompletedListener();

    TransactionCompletedListener::setInstance(firstCompletedListener);

    sp<SurfaceControl> layer = createBufferStateLayer();
    ReleaseBufferCallbackHelper* releaseCallback = getReleaseBufferCallbackHelper();

    sp<GraphicBuffer> firstBuffer = getBuffer();
    ReleaseCallbackId firstBufferCallbackId(firstBuffer->getId(), generateFrameNumber());

    Transaction transaction1;
    transaction1.setBuffer(layer, firstBuffer, std::nullopt, firstBufferCallbackId.framenumber,
                           releaseCallback->getCallback());

    // Sent a second buffer to allow the first buffer to get released.
    sp<GraphicBuffer> secondBuffer = getBuffer();
    ReleaseCallbackId secondBufferCallbackId(secondBuffer->getId(), generateFrameNumber());

    Transaction transaction2;
    transaction2.setBuffer(layer, secondBuffer, std::nullopt, secondBufferCallbackId.framenumber,
                           releaseCallback->getCallback());

    // Set a different TransactionCompletedListener to mimic a second process
    TransactionCompletedListener::setInstance(secondCompletedListener);
    Transaction().merge(std::move(transaction1)).merge(std::move(transaction2)).apply();

    // Make sure we can still get the release callback even though the merge happened in a different
    // process.
    ASSERT_NO_FATAL_FAILURE(waitForReleaseBufferCallback(*releaseCallback, firstBufferCallbackId));
}

TEST_F(ReleaseBufferCallbackTest, SetBuffer_OverwriteBuffersWithNull) {
    sp<SurfaceControl> layer = createBufferStateLayer();
    ReleaseBufferCallbackHelper* releaseCallback = getReleaseBufferCallbackHelper();

    sp<GraphicBuffer> firstBuffer = getBuffer();
    ReleaseCallbackId firstBufferCallbackId(firstBuffer->getId(), generateFrameNumber());

    // Create transaction with a buffer.
    Transaction transaction;
    transaction.setBuffer(layer, firstBuffer, std::nullopt, firstBufferCallbackId.framenumber,
                          releaseCallback->getCallback());

    // Call setBuffer on the same transaction with a null buffer.
    transaction.setBuffer(layer, nullptr, std::nullopt, 0, releaseCallback->getCallback());

    ASSERT_NO_FATAL_FAILURE(waitForReleaseBufferCallback(*releaseCallback, firstBufferCallbackId));
}

} // namespace android
