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

#define LOG_TAG "graphics_composer_hidl_hal_test"

#include <android-base/logging.h>
#include <android-base/properties.h>
#include <composer-vts/2.1/ComposerVts.h>
#include <composer-vts/2.1/GraphicsComposerCallback.h>
#include <composer-vts/2.1/TestCommandReader.h>
#include <gtest/gtest.h>
#include <hardware/hwcomposer2.h>
#include <hidl/GtestPrinter.h>
#include <hidl/ServiceManagement.h>
#include <ui/GraphicBuffer.h>

#include <unistd.h>

#include <algorithm>
#include <array>
#include <memory>
#include <mutex>
#include <unordered_set>
#include <vector>

namespace android {
namespace hardware {
namespace graphics {
namespace composer {
namespace V2_1 {
namespace vts {
namespace {

using android::hardware::graphics::common::V1_0::BufferUsage;
using android::hardware::graphics::common::V1_0::ColorMode;
using android::hardware::graphics::common::V1_0::ColorTransform;
using android::hardware::graphics::common::V1_0::Dataspace;
using android::hardware::graphics::common::V1_0::PixelFormat;
using android::hardware::graphics::common::V1_0::Transform;

class GraphicsComposerHidlTest : public ::testing::TestWithParam<std::string> {
  protected:
    void SetUp() override {
        ASSERT_NO_FATAL_FAILURE(
                mComposer = std::make_unique<Composer>(IComposer::getService(GetParam())));
        ASSERT_NO_FATAL_FAILURE(mComposerClient = mComposer->createClient());

        mComposerCallback = new GraphicsComposerCallback;
        mComposerClient->registerCallback(mComposerCallback);

        // assume the first display is primary and is never removed
        mPrimaryDisplay = waitForFirstDisplay();

        // explicitly disable vsync
        mComposerClient->setVsyncEnabled(mPrimaryDisplay, false);
        mComposerCallback->setVsyncAllowed(false);

        mInvalidDisplayId = GetInvalidDisplayId();

        // Although 0 could be an invalid display, a return value of 0
        // from GetInvalidDisplayId means all other ids are in use, a condition which
        // we are assuming a device will never have
        ASSERT_NE(0, mInvalidDisplayId);
    }

    void TearDown() override {
        if (mComposerCallback != nullptr) {
            EXPECT_EQ(0, mComposerCallback->getInvalidHotplugCount());
            EXPECT_EQ(0, mComposerCallback->getInvalidRefreshCount());
            EXPECT_EQ(0, mComposerCallback->getInvalidVsyncCount());
        }
    }

    // returns an invalid display id (one that has not been registered to a
    // display.  Currently assuming that a device will never have close to
    // std::numeric_limit<uint64_t>::max() displays registered while running tests
    Display GetInvalidDisplayId() {
        std::vector<Display> validDisplays = mComposerCallback->getDisplays();

        uint64_t id = std::numeric_limits<uint64_t>::max();
        while (id > 0) {
            if (std::find(validDisplays.begin(), validDisplays.end(), id) == validDisplays.end()) {
                return id;
            }
            id--;
        }

        return 0;
    }

    // use the slot count usually set by SF
    static constexpr uint32_t kBufferSlotCount = 64;

    std::unique_ptr<Composer> mComposer;
    std::unique_ptr<ComposerClient> mComposerClient;
    sp<GraphicsComposerCallback> mComposerCallback;
    // the first display and is assumed never to be removed
    Display mPrimaryDisplay;
    Display mInvalidDisplayId;

   private:
    Display waitForFirstDisplay() {
        while (true) {
            std::vector<Display> displays = mComposerCallback->getDisplays();
            if (displays.empty()) {
                usleep(5 * 1000);
                continue;
            }

            return displays[0];
        }
    }
};

/**
 * Test IComposer::getCapabilities.
 *
 * Test that IComposer::getCapabilities returns no invalid capabilities.
 */
TEST_P(GraphicsComposerHidlTest, GetCapabilities) {
    auto capabilities = mComposer->getCapabilities();
    ASSERT_EQ(capabilities.end(),
              std::find(capabilities.begin(), capabilities.end(), IComposer::Capability::INVALID));
}

/**
 * Test IComposer::dumpDebugInfo.
 */
TEST_P(GraphicsComposerHidlTest, DumpDebugInfo) {
    mComposer->dumpDebugInfo();
}

/**
 * Test IComposer::createClient.
 *
 * Test that IComposerClient is a singleton.
 */
TEST_P(GraphicsComposerHidlTest, CreateClientSingleton) {
    mComposer->getRaw()->createClient(
        [&](const auto& tmpError, const auto&) { EXPECT_EQ(Error::NO_RESOURCES, tmpError); });
}

/**
 * Test IComposerClient::createVirtualDisplay and
 * IComposerClient::destroyVirtualDisplay.
 *
 * Test that virtual displays can be created and has the correct display type.
 */
TEST_P(GraphicsComposerHidlTest, CreateVirtualDisplay) {
    if (mComposerClient->getMaxVirtualDisplayCount() == 0) {
        GTEST_SUCCEED() << "no virtual display support";
        return;
    }

    Display display;
    PixelFormat format;
    ASSERT_NO_FATAL_FAILURE(
        display = mComposerClient->createVirtualDisplay(64, 64, PixelFormat::IMPLEMENTATION_DEFINED,
                                                        kBufferSlotCount, &format));

    // test display type
    IComposerClient::DisplayType type = mComposerClient->getDisplayType(display);
    EXPECT_EQ(IComposerClient::DisplayType::VIRTUAL, type);

    mComposerClient->destroyVirtualDisplay(display);
}

/**
 * Test IComposerClient::destroyVirtualDisplay
 *
 * Test that passing a bad display handle to destroyVirtualDisplay
 * returns a BAD_DISPLAY error
 */
TEST_P(GraphicsComposerHidlTest, DestroyVirtualDisplayBadDisplay) {
    if (mComposerClient->getMaxVirtualDisplayCount() == 0) {
        GTEST_SUCCEED() << "no virtual display support";
        return;
    }

    Error error = mComposerClient->getRaw()->destroyVirtualDisplay(mInvalidDisplayId);
    ASSERT_EQ(Error::BAD_DISPLAY, error);
}

/**
 * Test IComposerClient::createLayer and IComposerClient::destroyLayer.
 *
 * Test that layers can be created and destroyed.
 */
TEST_P(GraphicsComposerHidlTest, CreateLayer) {
    Layer layer;
    ASSERT_NO_FATAL_FAILURE(layer =
                                mComposerClient->createLayer(mPrimaryDisplay, kBufferSlotCount));

    mComposerClient->destroyLayer(mPrimaryDisplay, layer);
}

/**
 * Test IComposerClient::createLayer
 *
 * Test that passing in an invalid display handle to createLayer returns
 * BAD_DISPLAY.
 */
TEST_P(GraphicsComposerHidlTest, CreateLayerBadDisplay) {
    Error error;
    mComposerClient->getRaw()->createLayer(
        mInvalidDisplayId, kBufferSlotCount,
        [&](const auto& tmpOutError, const auto&) { error = tmpOutError; });
    ASSERT_EQ(Error::BAD_DISPLAY, error);
}

/**
 * Test IComposerClient::destroyLayer
 *
 * Test that passing in an invalid display handle to destroyLayer returns
 * BAD_DISPLAY
 */
TEST_P(GraphicsComposerHidlTest, DestroyLayerBadDisplay) {
    Error error;
    Layer layer;
    ASSERT_NO_FATAL_FAILURE(layer =
                                mComposerClient->createLayer(mPrimaryDisplay, kBufferSlotCount));

    error = mComposerClient->getRaw()->destroyLayer(mInvalidDisplayId, layer);

    EXPECT_EQ(Error::BAD_DISPLAY, error);

    ASSERT_NO_FATAL_FAILURE(mComposerClient->destroyLayer(mPrimaryDisplay, layer));
}

/**
 * Test IComposerClient::destroyLayer
 *
 * Test that passing in an invalid layer handle to destroyLayer returns
 * BAD_LAYER
 */
TEST_P(GraphicsComposerHidlTest, DestroyLayerBadLayerError) {
    // We haven't created any layers yet, so any id should be invalid
    Error error = mComposerClient->getRaw()->destroyLayer(mPrimaryDisplay, 1);

    EXPECT_EQ(Error::BAD_LAYER, error);
}

/**
 * Test IComposerClient::getActiveConfig
 *
 * Test that passing in a bad display handle to getActiveConfig generates a
 * BAD_DISPLAY error
 */
TEST_P(GraphicsComposerHidlTest, GetActiveConfigBadDisplay) {
    Error error;
    mComposerClient->getRaw()->getActiveConfig(
        mInvalidDisplayId, [&](const auto& tmpOutError, const auto&) { error = tmpOutError; });
    ASSERT_EQ(Error::BAD_DISPLAY, error);
}

/**
 * Test IComposerClient::getDisplayConfigs
 *
 * Test IComposerClient::getDisplayConfigs returns no error
 * when passed in a valid display
 */
TEST_P(GraphicsComposerHidlTest, GetDisplayConfig) {
    std::vector<Config> configs;
    ASSERT_NO_FATAL_FAILURE(configs = mComposerClient->getDisplayConfigs(mPrimaryDisplay));
}

/**
 * Test IComposerClient::getDisplayConfigs
 *
 * Test IComposerClient::getDisplayConfigs returns BAD_DISPLAY
 * when passed in an invalid display handle
 */
TEST_P(GraphicsComposerHidlTest, GetDisplayConfigBadDisplay) {
    Error error;
    mComposerClient->getRaw()->getDisplayConfigs(
        mInvalidDisplayId, [&](const auto& tmpOutError, const auto&) { error = tmpOutError; });
    ASSERT_EQ(Error::BAD_DISPLAY, error);
}

/**
 * Test IComposerClient::getDisplayName.
 */
TEST_P(GraphicsComposerHidlTest, GetDisplayName) {
    mComposerClient->getDisplayName(mPrimaryDisplay);
}

/**
 * Test IComposerClient::getDisplayType.
 *
 * Test that IComposerClient::getDisplayType returns the correct display type
 * for the primary display.
 */
TEST_P(GraphicsComposerHidlTest, GetDisplayType) {
    ASSERT_EQ(IComposerClient::DisplayType::PHYSICAL,
              mComposerClient->getDisplayType(mPrimaryDisplay));
}

/**
 * Test IComposerClient::getClientTargetSupport.
 *
 * Test that IComposerClient::getClientTargetSupport returns true for the
 * required client targets.
 */
TEST_P(GraphicsComposerHidlTest, GetClientTargetSupport) {
    ASSERT_NO_FATAL_FAILURE(
            mComposerClient->setPowerMode(mPrimaryDisplay, IComposerClient::PowerMode::ON));
    std::vector<Config> configs = mComposerClient->getDisplayConfigs(mPrimaryDisplay);
    for (auto config : configs) {
        int32_t width = mComposerClient->getDisplayAttribute(mPrimaryDisplay, config,
                                                             IComposerClient::Attribute::WIDTH);
        int32_t height = mComposerClient->getDisplayAttribute(mPrimaryDisplay, config,
                                                              IComposerClient::Attribute::HEIGHT);
        ASSERT_LT(0, width);
        ASSERT_LT(0, height);

        mComposerClient->setActiveConfig(mPrimaryDisplay, config);

        ASSERT_TRUE(mComposerClient->getClientTargetSupport(
            mPrimaryDisplay, width, height, PixelFormat::RGBA_8888, Dataspace::UNKNOWN));
    }
}

/**
 * Test IComposerClient::getClientTargetSupport
 *
 * Test that IComposerClient::getClientTargetSupport returns BAD_DISPLAY when
 * passed an invalid display handle
 */
TEST_P(GraphicsComposerHidlTest, GetClientTargetSupportBadDisplay) {
    std::vector<Config> configs = mComposerClient->getDisplayConfigs(mPrimaryDisplay);
    for (auto config : configs) {
        int32_t width = mComposerClient->getDisplayAttribute(mPrimaryDisplay, config,
                                                             IComposerClient::Attribute::WIDTH);
        int32_t height = mComposerClient->getDisplayAttribute(mPrimaryDisplay, config,
                                                              IComposerClient::Attribute::HEIGHT);
        ASSERT_LT(0, width);
        ASSERT_LT(0, height);

        mComposerClient->setActiveConfig(mPrimaryDisplay, config);

        Error error = mComposerClient->getRaw()->getClientTargetSupport(
            mInvalidDisplayId, width, height, PixelFormat::RGBA_8888, Dataspace::UNKNOWN);
        EXPECT_EQ(Error::BAD_DISPLAY, error);
    }
}

/**
 * Test IComposerClient::getDisplayAttribute.
 *
 * Test that IComposerClient::getDisplayAttribute succeeds for the required
 * formats, and succeeds or fails correctly for optional attributes.
 */
TEST_P(GraphicsComposerHidlTest, GetDisplayAttribute) {
    std::vector<Config> configs = mComposerClient->getDisplayConfigs(mPrimaryDisplay);
    for (auto config : configs) {
        const std::array<IComposerClient::Attribute, 3> requiredAttributes = {{
            IComposerClient::Attribute::WIDTH, IComposerClient::Attribute::HEIGHT,
            IComposerClient::Attribute::VSYNC_PERIOD,
        }};
        for (auto attribute : requiredAttributes) {
            mComposerClient->getDisplayAttribute(mPrimaryDisplay, config, attribute);
        }

        const std::array<IComposerClient::Attribute, 2> optionalAttributes = {{
            IComposerClient::Attribute::DPI_X, IComposerClient::Attribute::DPI_Y,
        }};
        for (auto attribute : optionalAttributes) {
            mComposerClient->getRaw()->getDisplayAttribute(
                mPrimaryDisplay, config, attribute, [&](const auto& tmpError, const auto&) {
                    EXPECT_TRUE(tmpError == Error::NONE || tmpError == Error::UNSUPPORTED);
                });
        }
    }
}

/**
 * Test IComposerClient::getHdrCapabilities.
 */
TEST_P(GraphicsComposerHidlTest, GetHdrCapabilities) {
    float maxLuminance;
    float maxAverageLuminance;
    float minLuminance;
    mComposerClient->getHdrCapabilities(mPrimaryDisplay, &maxLuminance, &maxAverageLuminance,
                                        &minLuminance);
}

/**
 * Test IComposerClient::setClientTargetSlotCount.
 */
TEST_P(GraphicsComposerHidlTest, SetClientTargetSlotCount) {
    mComposerClient->setClientTargetSlotCount(mPrimaryDisplay, kBufferSlotCount);
}

/**
 * Test IComposerClient::setActiveConfig.
 *
 * Test that IComposerClient::setActiveConfig succeeds for all display
 * configs.
 */
TEST_P(GraphicsComposerHidlTest, SetActiveConfig) {
    std::vector<Config> configs = mComposerClient->getDisplayConfigs(mPrimaryDisplay);
    for (auto config : configs) {
        mComposerClient->setActiveConfig(mPrimaryDisplay, config);
        ASSERT_EQ(config, mComposerClient->getActiveConfig(mPrimaryDisplay));
    }
}

/**
 * Test IComposerClient::setActiveConfig
 *
 * Test that config set during IComposerClient::setActiveConfig is maintained
 * during a display on/off power cycle
 */
TEST_P(GraphicsComposerHidlTest, SetActiveConfigPowerCycle) {
    ASSERT_NO_FATAL_FAILURE(
        mComposerClient->setPowerMode(mPrimaryDisplay, IComposerClient::PowerMode::OFF));
    ASSERT_NO_FATAL_FAILURE(
        mComposerClient->setPowerMode(mPrimaryDisplay, IComposerClient::PowerMode::ON));

    std::vector<Config> configs = mComposerClient->getDisplayConfigs(mPrimaryDisplay);
    for (auto config : configs) {
        mComposerClient->setActiveConfig(mPrimaryDisplay, config);
        ASSERT_EQ(config, mComposerClient->getActiveConfig(mPrimaryDisplay));

        ASSERT_NO_FATAL_FAILURE(
            mComposerClient->setPowerMode(mPrimaryDisplay, IComposerClient::PowerMode::OFF));
        ASSERT_NO_FATAL_FAILURE(
            mComposerClient->setPowerMode(mPrimaryDisplay, IComposerClient::PowerMode::ON));
        ASSERT_EQ(config, mComposerClient->getActiveConfig(mPrimaryDisplay));
    }
}

/**
 * Test IComposerClient::getColorMode
 *
 * Test that IComposerClient::getColorMode always returns ColorMode::NATIVE
 */
TEST_P(GraphicsComposerHidlTest, GetColorModes) {
    std::vector<ColorMode> modes = mComposerClient->getColorModes(mPrimaryDisplay);
    auto nativeModeLocation = std::find(modes.begin(), modes.end(), ColorMode::NATIVE);

    ASSERT_NE(modes.end(), nativeModeLocation);
}

/**
 * Test IComposerClient::setColorMode.
 *
 * Test that IComposerClient::setColorMode succeeds for all color modes.
 */
TEST_P(GraphicsComposerHidlTest, SetColorMode) {
    std::unordered_set<ColorMode> validModes;
    for (auto mode : hidl_enum_range<ColorMode>()) {
        validModes.insert(mode);
    }

    std::vector<ColorMode> modes = mComposerClient->getColorModes(mPrimaryDisplay);
    for (auto mode : modes) {
        if (validModes.count(mode)) {
            mComposerClient->setColorMode(mPrimaryDisplay, mode);
        }
    }
}

/**
 * Test IComposerClient::setColorMode
 *
 * Test that IComposerClient::setColorMode returns BAD_DISPLAY for
 * an invalid display handle
 */
TEST_P(GraphicsComposerHidlTest, SetColorModeBadDisplay) {
    std::vector<ColorMode> modes = mComposerClient->getColorModes(mPrimaryDisplay);
    for (auto mode : modes) {
        Error error = mComposerClient->getRaw()->setColorMode(mInvalidDisplayId, mode);
        EXPECT_EQ(Error::BAD_DISPLAY, error);
    }
}

/**
 * Test IComposerClient::setColorMode
 *
 * Test that IComposerClient::setColorMode returns BAD_PARAMETER when passed in
 * an invalid color mode
 */
TEST_P(GraphicsComposerHidlTest, SetColorModeBadParameter) {
    Error error =
        mComposerClient->getRaw()->setColorMode(mPrimaryDisplay, static_cast<ColorMode>(-1));
    ASSERT_EQ(Error::BAD_PARAMETER, error);
}

/**
 * Test IComposerClient::getDozeSupport
 *
 * Test that IComposerClient::getDozeSupport returns
 * BAD_DISPLAY when passed an invalid display handle
 */
TEST_P(GraphicsComposerHidlTest, GetDozeSupportBadDisplay) {
    Error error;
    mComposerClient->getRaw()->getDozeSupport(
        mInvalidDisplayId, [&](const auto& tmpOutError, const auto&) { error = tmpOutError; });
    ASSERT_EQ(Error::BAD_DISPLAY, error);
}

/**
 * Test IComposerClient::setPowerMode.
 *
 * Test that IComposerClient::setPowerMode succeeds for all power modes.
 */
TEST_P(GraphicsComposerHidlTest, SetPowerMode) {
    std::vector<IComposerClient::PowerMode> modes;
    modes.push_back(IComposerClient::PowerMode::OFF);

    if (mComposerClient->getDozeSupport(mPrimaryDisplay)) {
        modes.push_back(IComposerClient::PowerMode::DOZE);
        modes.push_back(IComposerClient::PowerMode::DOZE_SUSPEND);
    }

    // push ON last
    modes.push_back(IComposerClient::PowerMode::ON);

    for (auto mode : modes) {
        mComposerClient->setPowerMode(mPrimaryDisplay, mode);
    }
}

/**
 * Test IComposerClient::setPowerMode
 *
 * Test IComposerClient::setPowerMode succeeds with different
 * orderings of power modes
 */
TEST_P(GraphicsComposerHidlTest, SetPowerModeVariations) {
    std::vector<IComposerClient::PowerMode> modes;
    modes.push_back(IComposerClient::PowerMode::OFF);
    modes.push_back(IComposerClient::PowerMode::ON);
    modes.push_back(IComposerClient::PowerMode::OFF);
    for (auto mode : modes) {
        ASSERT_NO_FATAL_FAILURE(mComposerClient->setPowerMode(mPrimaryDisplay, mode));
    }

    modes.clear();

    modes.push_back(IComposerClient::PowerMode::OFF);
    modes.push_back(IComposerClient::PowerMode::OFF);
    for (auto mode : modes) {
        ASSERT_NO_FATAL_FAILURE(mComposerClient->setPowerMode(mPrimaryDisplay, mode));
    }

    modes.clear();
    if (mComposerClient->getDozeSupport(mPrimaryDisplay)) {
        modes.push_back(IComposerClient::PowerMode::DOZE);
        modes.push_back(IComposerClient::PowerMode::DOZE);

        for (auto mode : modes) {
            ASSERT_NO_FATAL_FAILURE(mComposerClient->setPowerMode(mPrimaryDisplay, mode));
        }

        modes.clear();

        modes.push_back(IComposerClient::PowerMode::DOZE_SUSPEND);
        modes.push_back(IComposerClient::PowerMode::DOZE_SUSPEND);

        for (auto mode : modes) {
            ASSERT_NO_FATAL_FAILURE(mComposerClient->setPowerMode(mPrimaryDisplay, mode));
        }
    }

    modes.clear();

    modes.push_back(IComposerClient::PowerMode::ON);
    modes.push_back(IComposerClient::PowerMode::ON);
    for (auto mode : modes) {
        ASSERT_NO_FATAL_FAILURE(mComposerClient->setPowerMode(mPrimaryDisplay, mode));
    }
}

/**
 * Test IComposerClient::setPowerMode
 *
 * Test IComposerClient::setPowerMode returns BAD_DISPLAY when passed an invalid
 * display handle
 */
TEST_P(GraphicsComposerHidlTest, SetPowerModeBadDisplay) {
    Error error =
        mComposerClient->getRaw()->setPowerMode(mInvalidDisplayId, IComposerClient::PowerMode::ON);
    ASSERT_EQ(Error::BAD_DISPLAY, error);
}

/**
 * Test IComposerClient::setPowerMode
 *
 * Test that IComposerClient::setPowerMode returns UNSUPPORTED when passed DOZE
 * or DOZE_SUSPEND on devices that do not support DOZE/DOZE_SUSPEND
 */
TEST_P(GraphicsComposerHidlTest, SetPowerModeUnsupported) {
    if (!mComposerClient->getDozeSupport(mPrimaryDisplay)) {
        Error error = mComposerClient->getRaw()->setPowerMode(mPrimaryDisplay,
                                                              IComposerClient::PowerMode::DOZE);
        EXPECT_EQ(Error::UNSUPPORTED, error);

        error = mComposerClient->getRaw()->setPowerMode(mPrimaryDisplay,
                                                        IComposerClient::PowerMode::DOZE_SUSPEND);
        EXPECT_EQ(Error::UNSUPPORTED, error);
    }
}

/**
 * Test IComposerClient::setPowerMode
 *
 * Tests that IComposerClient::setPowerMode returns BAD_PARAMETER when passed an invalid
 * PowerMode
 */
TEST_P(GraphicsComposerHidlTest, SetPowerModeBadParameter) {
    Error error = mComposerClient->getRaw()->setPowerMode(
        mPrimaryDisplay, static_cast<IComposerClient::PowerMode>(-1));
    ASSERT_EQ(Error::BAD_PARAMETER, error);
}

/**
 * Test IComposerClient::setVsyncEnabled.
 *
 * Test that IComposerClient::setVsyncEnabled succeeds and there is no
 * spurious vsync events.
 */
TEST_P(GraphicsComposerHidlTest, SetVsyncEnabled) {
    mComposerCallback->setVsyncAllowed(true);

    mComposerClient->setVsyncEnabled(mPrimaryDisplay, true);
    usleep(60 * 1000);
    mComposerClient->setVsyncEnabled(mPrimaryDisplay, false);

    mComposerCallback->setVsyncAllowed(false);
}

// Tests for IComposerClient::Command.
class GraphicsComposerHidlCommandTest : public GraphicsComposerHidlTest {
   protected:
    void SetUp() override {
        ASSERT_NO_FATAL_FAILURE(GraphicsComposerHidlTest::SetUp());

        Config activeConfig = mComposerClient->getActiveConfig(mPrimaryDisplay);
        mDisplayWidth = mComposerClient->getDisplayAttribute(mPrimaryDisplay, activeConfig,
                                                             IComposerClient::Attribute::WIDTH);
        mDisplayHeight = mComposerClient->getDisplayAttribute(mPrimaryDisplay, activeConfig,
                                                              IComposerClient::Attribute::HEIGHT);
        mWriter = std::make_unique<CommandWriterBase>(1024);
        mReader = std::make_unique<TestCommandReader>();
    }

    void TearDown() override {
        ASSERT_EQ(0, mReader->mErrors.size());
        ASSERT_NO_FATAL_FAILURE(GraphicsComposerHidlTest::TearDown());
    }

    sp<GraphicBuffer> allocate() { return allocate(mDisplayWidth, mDisplayHeight); }

    sp<GraphicBuffer> allocate(int32_t width, int32_t height) {
        auto result = sp<GraphicBuffer>::make(
                width, height, static_cast<int32_t>(PixelFormat::RGBA_8888), /*layerCount*/ 1,
                static_cast<uint64_t>(BufferUsage::CPU_WRITE_OFTEN | BufferUsage::CPU_READ_OFTEN |
                                      BufferUsage::COMPOSER_OVERLAY));
        if (result->initCheck() != STATUS_OK) {
            return nullptr;
        }
        return result;
    }

    void execute() { mComposerClient->execute(mReader.get(), mWriter.get()); }

    std::unique_ptr<CommandWriterBase> mWriter;
    std::unique_ptr<TestCommandReader> mReader;
    int32_t mDisplayWidth;
    int32_t mDisplayHeight;
};

/**
 * Test IComposerClient::Command::SET_COLOR_TRANSFORM.
 */
TEST_P(GraphicsComposerHidlCommandTest, SET_COLOR_TRANSFORM) {
    const std::array<float, 16> identity = {{
        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,
    }};

    mWriter->selectDisplay(mPrimaryDisplay);
    mWriter->setColorTransform(identity.data(), ColorTransform::IDENTITY);

    execute();
}

/**
 * Test IComposerClient::Command::SET_CLIENT_TARGET.
 */
TEST_P(GraphicsComposerHidlCommandTest, SET_CLIENT_TARGET) {
    mComposerClient->setClientTargetSlotCount(mPrimaryDisplay, kBufferSlotCount);

    mWriter->selectDisplay(mPrimaryDisplay);
    mWriter->setClientTarget(0, nullptr, -1, Dataspace::UNKNOWN,
                             std::vector<IComposerClient::Rect>());

    execute();
}

/**
 * Test IComposerClient::Command::SET_OUTPUT_BUFFER.
 */
TEST_P(GraphicsComposerHidlCommandTest, SET_OUTPUT_BUFFER) {
    if (mComposerClient->getMaxVirtualDisplayCount() == 0) {
        GTEST_SUCCEED() << "no virtual display support";
        return;
    }

    Display display;
    PixelFormat format;
    ASSERT_NO_FATAL_FAILURE(
        display = mComposerClient->createVirtualDisplay(64, 64, PixelFormat::IMPLEMENTATION_DEFINED,
                                                        kBufferSlotCount, &format));

    auto handle = allocate();
    ASSERT_TRUE(handle);

    mWriter->selectDisplay(display);
    mWriter->setOutputBuffer(0, handle->handle, -1);
    execute();
}

/**
 * Test IComposerClient::Command::VALIDATE_DISPLAY.
 */
TEST_P(GraphicsComposerHidlCommandTest, VALIDATE_DISPLAY) {
    mWriter->selectDisplay(mPrimaryDisplay);
    mWriter->validateDisplay();
    execute();
}

/**
 * Test IComposerClient::Command::ACCEPT_DISPLAY_CHANGES.
 */
TEST_P(GraphicsComposerHidlCommandTest, ACCEPT_DISPLAY_CHANGES) {
    mWriter->selectDisplay(mPrimaryDisplay);
    mWriter->validateDisplay();
    mWriter->acceptDisplayChanges();
    execute();
}

/**
 * Test IComposerClient::Command::PRESENT_DISPLAY.
 */
TEST_P(GraphicsComposerHidlCommandTest, PRESENT_DISPLAY) {
    mWriter->selectDisplay(mPrimaryDisplay);
    mWriter->validateDisplay();
    mWriter->presentDisplay();
    execute();
}

/**
 * Test IComposerClient::Command::PRESENT_DISPLAY
 *
 * Test that IComposerClient::Command::PRESENT_DISPLAY works without
 * additional call to validateDisplay when only the layer buffer handle and
 * surface damage have been set
 */
TEST_P(GraphicsComposerHidlCommandTest, PRESENT_DISPLAY_NO_LAYER_STATE_CHANGES) {
    if (!mComposer->hasCapability(
                static_cast<IComposer::Capability>(HWC2_CAPABILITY_SKIP_VALIDATE))) {
        std::cout << "Device does not have skip validate capability, skipping" << std::endl;
        GTEST_SUCCEED();
        return;
    }
    mWriter->selectDisplay(mPrimaryDisplay);
    mComposerClient->setPowerMode(mPrimaryDisplay, IComposerClient::PowerMode::ON);
    mComposerClient->setColorMode(mPrimaryDisplay, ColorMode::NATIVE);

    auto handle = allocate();
    ASSERT_NE(nullptr, handle.get());

    IComposerClient::Rect displayFrame{0, 0, mDisplayWidth, mDisplayHeight};

    Layer layer;
    ASSERT_NO_FATAL_FAILURE(layer =
                                mComposerClient->createLayer(mPrimaryDisplay, kBufferSlotCount));
    mWriter->selectLayer(layer);
    mWriter->setLayerCompositionType(IComposerClient::Composition::DEVICE);
    mWriter->setLayerDisplayFrame(displayFrame);
    mWriter->setLayerPlaneAlpha(1);
    mWriter->setLayerSourceCrop({0, 0, (float)mDisplayWidth, (float)mDisplayHeight});
    mWriter->setLayerTransform(static_cast<Transform>(0));
    mWriter->setLayerVisibleRegion(std::vector<IComposerClient::Rect>(1, displayFrame));
    mWriter->setLayerZOrder(10);
    mWriter->setLayerBlendMode(IComposerClient::BlendMode::NONE);
    mWriter->setLayerSurfaceDamage(std::vector<IComposerClient::Rect>(1, displayFrame));
    mWriter->setLayerBuffer(0, handle->handle, -1);
    mWriter->setLayerDataspace(Dataspace::UNKNOWN);

    mWriter->validateDisplay();
    execute();
    if (mReader->mCompositionChanges.size() != 0) {
        GTEST_SUCCEED() << "Composition change requested, skipping test";
        return;
    }

    ASSERT_EQ(0, mReader->mErrors.size());
    mWriter->presentDisplay();
    execute();
    ASSERT_EQ(0, mReader->mErrors.size());

    mWriter->selectLayer(layer);
    auto handle2 = allocate();
    ASSERT_NE(nullptr, handle2.get());
    mWriter->setLayerBuffer(0, handle2->handle, -1);
    mWriter->setLayerSurfaceDamage(std::vector<IComposerClient::Rect>(1, {0, 0, 10, 10}));
    mWriter->presentDisplay();
    execute();
}

/**
 * Test IComposerClient::Command::SET_LAYER_CURSOR_POSITION.
 */
TEST_P(GraphicsComposerHidlCommandTest, SET_LAYER_CURSOR_POSITION) {
    Layer layer;
    ASSERT_NO_FATAL_FAILURE(layer =
                                mComposerClient->createLayer(mPrimaryDisplay, kBufferSlotCount));

    auto handle = allocate();
    ASSERT_NE(nullptr, handle.get());
    IComposerClient::Rect displayFrame{0, 0, mDisplayWidth, mDisplayHeight};

    mWriter->selectDisplay(mPrimaryDisplay);
    mWriter->selectLayer(layer);
    mWriter->setLayerBuffer(0, handle->handle, -1);
    mWriter->setLayerCompositionType(IComposerClient::Composition::CURSOR);
    mWriter->setLayerDisplayFrame(displayFrame);
    mWriter->setLayerPlaneAlpha(1);
    mWriter->setLayerSourceCrop({0, 0, (float)mDisplayWidth, (float)mDisplayHeight});
    mWriter->setLayerTransform(static_cast<Transform>(0));
    mWriter->setLayerVisibleRegion(std::vector<IComposerClient::Rect>(1, displayFrame));
    mWriter->setLayerZOrder(10);
    mWriter->setLayerBlendMode(IComposerClient::BlendMode::NONE);
    mWriter->setLayerSurfaceDamage(std::vector<IComposerClient::Rect>(1, displayFrame));
    mWriter->setLayerDataspace(Dataspace::UNKNOWN);
    mWriter->validateDisplay();

    execute();
    if (mReader->mCompositionChanges.size() != 0) {
        GTEST_SUCCEED() << "Composition change requested, skipping test";
        return;
    }
    mWriter->presentDisplay();
    ASSERT_EQ(0, mReader->mErrors.size());

    mWriter->setLayerCursorPosition(1, 1);
    mWriter->setLayerCursorPosition(0, 0);
    mWriter->validateDisplay();
    mWriter->presentDisplay();
    execute();
}

/**
 * Test IComposerClient::Command::SET_LAYER_BUFFER.
 */
TEST_P(GraphicsComposerHidlCommandTest, SET_LAYER_BUFFER) {
    auto handle = allocate();
    ASSERT_NE(nullptr, handle.get());

    Layer layer;
    ASSERT_NO_FATAL_FAILURE(layer =
                                mComposerClient->createLayer(mPrimaryDisplay, kBufferSlotCount));

    mWriter->selectDisplay(mPrimaryDisplay);
    mWriter->selectLayer(layer);
    mWriter->setLayerBuffer(0, handle->handle, -1);
    execute();
}

/**
 * Test IComposerClient::Command::SET_LAYER_BUFFER with the behavior used for clearing buffer slots.
 */
TEST_P(GraphicsComposerHidlCommandTest, SET_LAYER_BUFFER_multipleTimes) {
    // A placeholder buffer used to clear buffer slots
    auto clearSlotBuffer = allocate(1u, 1u);

    //
    // Set the layer buffer to the first buffer
    //
    auto handle1 = allocate();
    ASSERT_NE(nullptr, handle1.get());
    IComposerClient::Rect displayFrame{0, 0, mDisplayWidth, mDisplayHeight};
    Layer layer;
    ASSERT_NO_FATAL_FAILURE(
            layer = mComposerClient->createLayer(mPrimaryDisplay, kBufferSlotCount));
    mWriter->selectDisplay(mPrimaryDisplay);
    mWriter->selectLayer(layer);
    mWriter->setLayerCompositionType(IComposerClient::Composition::DEVICE);
    mWriter->setLayerDisplayFrame(displayFrame);
    mWriter->setLayerBuffer(0, handle1->handle, -1);
    mWriter->setLayerDataspace(Dataspace::UNKNOWN);
    mWriter->validateDisplay();
    execute();
    if (mReader->mCompositionChanges.size() != 0) {
        GTEST_SUCCEED() << "Composition change requested, skipping test";
        return;
    }
    ASSERT_EQ(0, mReader->mErrors.size());
    mWriter->selectDisplay(mPrimaryDisplay);
    mWriter->presentDisplay();
    execute();
    ASSERT_EQ(0, mReader->mErrors.size());

    //
    // Set the layer buffer to the second buffer
    //
    auto handle2 = allocate();
    ASSERT_NE(nullptr, handle2.get());
    mWriter->selectDisplay(mPrimaryDisplay);
    mWriter->selectLayer(layer);
    mWriter->setLayerCompositionType(IComposerClient::Composition::DEVICE);
    mWriter->setLayerDisplayFrame(displayFrame);
    mWriter->setLayerBuffer(1, handle2->handle, -1);
    mWriter->setLayerDataspace(Dataspace::UNKNOWN);
    mWriter->validateDisplay();
    execute();
    if (mReader->mCompositionChanges.size() != 0) {
        GTEST_SUCCEED() << "Composition change requested, skipping test";
        return;
    }
    ASSERT_EQ(0, mReader->mErrors.size());
    mWriter->selectDisplay(mPrimaryDisplay);
    mWriter->presentDisplay();
    execute();
    ASSERT_EQ(0, mReader->mErrors.size());

    //
    // Set the layer buffer to the third buffer
    //
    auto handle3 = allocate();
    ASSERT_NE(nullptr, handle3.get());
    mWriter->selectDisplay(mPrimaryDisplay);
    mWriter->selectLayer(layer);
    mWriter->setLayerCompositionType(IComposerClient::Composition::DEVICE);
    mWriter->setLayerDisplayFrame(displayFrame);
    mWriter->setLayerBuffer(2, handle3->handle, -1);
    mWriter->setLayerDataspace(Dataspace::UNKNOWN);
    mWriter->validateDisplay();
    execute();
    if (mReader->mCompositionChanges.size() != 0) {
        GTEST_SUCCEED() << "Composition change requested, skipping test";
        return;
    }
    ASSERT_EQ(0, mReader->mErrors.size());
    mWriter->selectDisplay(mPrimaryDisplay);
    mWriter->presentDisplay();
    execute();
    ASSERT_EQ(0, mReader->mErrors.size());

    // Ensure we can clear multiple buffer slots and then restore the active buffer at the end
    mWriter->selectDisplay(mPrimaryDisplay);
    mWriter->selectLayer(layer);
    mWriter->setLayerBuffer(0, clearSlotBuffer->handle, -1);
    mWriter->selectDisplay(mPrimaryDisplay);
    mWriter->selectLayer(layer);
    mWriter->setLayerBuffer(1, clearSlotBuffer->handle, -1);
    mWriter->selectDisplay(mPrimaryDisplay);
    mWriter->selectLayer(layer);
    mWriter->setLayerBuffer(2, nullptr, -1);
    mWriter->validateDisplay();
    execute();
    ASSERT_EQ(0, mReader->mErrors.size());

    mWriter->selectDisplay(mPrimaryDisplay);
    mWriter->presentDisplay();
    execute();
    ASSERT_EQ(0, mReader->mErrors.size());
}

/**
 * Test IComposerClient::Command::SET_LAYER_SURFACE_DAMAGE.
 */
TEST_P(GraphicsComposerHidlCommandTest, SET_LAYER_SURFACE_DAMAGE) {
    Layer layer;
    ASSERT_NO_FATAL_FAILURE(layer =
                                mComposerClient->createLayer(mPrimaryDisplay, kBufferSlotCount));

    IComposerClient::Rect empty{0, 0, 0, 0};
    IComposerClient::Rect unit{0, 0, 1, 1};

    mWriter->selectDisplay(mPrimaryDisplay);
    mWriter->selectLayer(layer);
    mWriter->setLayerSurfaceDamage(std::vector<IComposerClient::Rect>(1, empty));
    mWriter->setLayerSurfaceDamage(std::vector<IComposerClient::Rect>(1, unit));
    mWriter->setLayerSurfaceDamage(std::vector<IComposerClient::Rect>());
    execute();
}

/**
 * Test IComposerClient::Command::SET_LAYER_BLEND_MODE.
 */
TEST_P(GraphicsComposerHidlCommandTest, SET_LAYER_BLEND_MODE) {
    Layer layer;
    ASSERT_NO_FATAL_FAILURE(layer =
                                mComposerClient->createLayer(mPrimaryDisplay, kBufferSlotCount));

    mWriter->selectDisplay(mPrimaryDisplay);
    mWriter->selectLayer(layer);
    mWriter->setLayerBlendMode(IComposerClient::BlendMode::NONE);
    mWriter->setLayerBlendMode(IComposerClient::BlendMode::PREMULTIPLIED);
    mWriter->setLayerBlendMode(IComposerClient::BlendMode::COVERAGE);
    execute();
}

/**
 * Test IComposerClient::Command::SET_LAYER_COLOR.
 */
TEST_P(GraphicsComposerHidlCommandTest, SET_LAYER_COLOR) {
    Layer layer;
    ASSERT_NO_FATAL_FAILURE(layer =
                                mComposerClient->createLayer(mPrimaryDisplay, kBufferSlotCount));

    mWriter->selectDisplay(mPrimaryDisplay);
    mWriter->selectLayer(layer);
    mWriter->setLayerColor(IComposerClient::Color{0xff, 0xff, 0xff, 0xff});
    mWriter->setLayerColor(IComposerClient::Color{0, 0, 0, 0});
    execute();
}

/**
 * Test IComposerClient::Command::SET_LAYER_COMPOSITION_TYPE.
 */
TEST_P(GraphicsComposerHidlCommandTest, SET_LAYER_COMPOSITION_TYPE) {
    Layer layer;
    ASSERT_NO_FATAL_FAILURE(layer =
                                mComposerClient->createLayer(mPrimaryDisplay, kBufferSlotCount));

    mWriter->selectDisplay(mPrimaryDisplay);
    mWriter->selectLayer(layer);
    mWriter->setLayerCompositionType(IComposerClient::Composition::CLIENT);
    mWriter->setLayerCompositionType(IComposerClient::Composition::DEVICE);
    mWriter->setLayerCompositionType(IComposerClient::Composition::SOLID_COLOR);
    mWriter->setLayerCompositionType(IComposerClient::Composition::CURSOR);
    execute();
}

/**
 * Test IComposerClient::Command::SET_LAYER_DATASPACE.
 */
TEST_P(GraphicsComposerHidlCommandTest, SET_LAYER_DATASPACE) {
    Layer layer;
    ASSERT_NO_FATAL_FAILURE(layer =
                                mComposerClient->createLayer(mPrimaryDisplay, kBufferSlotCount));

    mWriter->selectDisplay(mPrimaryDisplay);
    mWriter->selectLayer(layer);
    mWriter->setLayerDataspace(Dataspace::UNKNOWN);
    execute();
}

/**
 * Test IComposerClient::Command::SET_LAYER_DISPLAY_FRAME.
 */
TEST_P(GraphicsComposerHidlCommandTest, SET_LAYER_DISPLAY_FRAME) {
    Layer layer;
    ASSERT_NO_FATAL_FAILURE(layer =
                                mComposerClient->createLayer(mPrimaryDisplay, kBufferSlotCount));

    mWriter->selectDisplay(mPrimaryDisplay);
    mWriter->selectLayer(layer);
    mWriter->setLayerDisplayFrame(IComposerClient::Rect{0, 0, 1, 1});
    execute();
}

/**
 * Test IComposerClient::Command::SET_LAYER_PLANE_ALPHA.
 */
TEST_P(GraphicsComposerHidlCommandTest, SET_LAYER_PLANE_ALPHA) {
    Layer layer;
    ASSERT_NO_FATAL_FAILURE(layer =
                                mComposerClient->createLayer(mPrimaryDisplay, kBufferSlotCount));

    mWriter->selectDisplay(mPrimaryDisplay);
    mWriter->selectLayer(layer);
    mWriter->setLayerPlaneAlpha(0.0f);
    mWriter->setLayerPlaneAlpha(1.0f);
    execute();
}

/**
 * Test IComposerClient::Command::SET_LAYER_SIDEBAND_STREAM.
 */
TEST_P(GraphicsComposerHidlCommandTest, SET_LAYER_SIDEBAND_STREAM) {
    if (!mComposer->hasCapability(IComposer::Capability::SIDEBAND_STREAM)) {
        GTEST_SUCCEED() << "no sideband stream support";
        return;
    }

    auto handle = allocate();
    ASSERT_NE(nullptr, handle.get());

    Layer layer;
    ASSERT_NO_FATAL_FAILURE(layer =
                                mComposerClient->createLayer(mPrimaryDisplay, kBufferSlotCount));

    mWriter->selectDisplay(mPrimaryDisplay);
    mWriter->selectLayer(layer);
    mWriter->setLayerSidebandStream(handle->handle);
    execute();
}

/**
 * Test IComposerClient::Command::SET_LAYER_SOURCE_CROP.
 */
TEST_P(GraphicsComposerHidlCommandTest, SET_LAYER_SOURCE_CROP) {
    Layer layer;
    ASSERT_NO_FATAL_FAILURE(layer =
                                mComposerClient->createLayer(mPrimaryDisplay, kBufferSlotCount));

    mWriter->selectDisplay(mPrimaryDisplay);
    mWriter->selectLayer(layer);
    mWriter->setLayerSourceCrop(IComposerClient::FRect{0.0f, 0.0f, 1.0f, 1.0f});
    execute();
}

/**
 * Test IComposerClient::Command::SET_LAYER_TRANSFORM.
 */
TEST_P(GraphicsComposerHidlCommandTest, SET_LAYER_TRANSFORM) {
    Layer layer;
    ASSERT_NO_FATAL_FAILURE(layer =
                                mComposerClient->createLayer(mPrimaryDisplay, kBufferSlotCount));

    mWriter->selectDisplay(mPrimaryDisplay);
    mWriter->selectLayer(layer);
    mWriter->setLayerTransform(static_cast<Transform>(0));
    mWriter->setLayerTransform(Transform::FLIP_H);
    mWriter->setLayerTransform(Transform::FLIP_V);
    mWriter->setLayerTransform(Transform::ROT_90);
    mWriter->setLayerTransform(Transform::ROT_180);
    mWriter->setLayerTransform(Transform::ROT_270);
    mWriter->setLayerTransform(static_cast<Transform>(Transform::FLIP_H | Transform::ROT_90));
    mWriter->setLayerTransform(static_cast<Transform>(Transform::FLIP_V | Transform::ROT_90));
    execute();
}

/**
 * Test IComposerClient::Command::SET_LAYER_VISIBLE_REGION.
 */
TEST_P(GraphicsComposerHidlCommandTest, SET_LAYER_VISIBLE_REGION) {
    Layer layer;
    ASSERT_NO_FATAL_FAILURE(layer =
                                mComposerClient->createLayer(mPrimaryDisplay, kBufferSlotCount));

    IComposerClient::Rect empty{0, 0, 0, 0};
    IComposerClient::Rect unit{0, 0, 1, 1};

    mWriter->selectDisplay(mPrimaryDisplay);
    mWriter->selectLayer(layer);
    mWriter->setLayerVisibleRegion(std::vector<IComposerClient::Rect>(1, empty));
    mWriter->setLayerVisibleRegion(std::vector<IComposerClient::Rect>(1, unit));
    mWriter->setLayerVisibleRegion(std::vector<IComposerClient::Rect>());
    execute();
}

/**
 * Test IComposerClient::Command::SET_LAYER_Z_ORDER.
 */
TEST_P(GraphicsComposerHidlCommandTest, SET_LAYER_Z_ORDER) {
    Layer layer;
    ASSERT_NO_FATAL_FAILURE(layer =
                                mComposerClient->createLayer(mPrimaryDisplay, kBufferSlotCount));

    mWriter->selectDisplay(mPrimaryDisplay);
    mWriter->selectLayer(layer);
    mWriter->setLayerZOrder(10);
    mWriter->setLayerZOrder(0);
    execute();
}

GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(GraphicsComposerHidlCommandTest);
INSTANTIATE_TEST_SUITE_P(
        PerInstance, GraphicsComposerHidlCommandTest,
        testing::ValuesIn(android::hardware::getAllHalInstanceNames(IComposer::descriptor)),
        android::hardware::PrintInstanceNameToString);

GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(GraphicsComposerHidlTest);
INSTANTIATE_TEST_SUITE_P(
        PerInstance, GraphicsComposerHidlTest,
        testing::ValuesIn(android::hardware::getAllHalInstanceNames(IComposer::descriptor)),
        android::hardware::PrintInstanceNameToString);

}  // namespace
}  // namespace vts
}  // namespace V2_1
}  // namespace composer
}  // namespace graphics
}  // namespace hardware
}  // namespace android

int main(int argc, char** argv) {
    ::testing::InitGoogleTest(&argc, argv);

    using namespace std::chrono_literals;
    if (!android::base::WaitForProperty("init.svc.surfaceflinger", "stopped", 10s)) {
        ALOGE("Failed to stop init.svc.surfaceflinger");
        return -1;
    }

    return RUN_ALL_TESTS();
}
