Merge Android R (rvc-dev-plus-aosp-without-vendor@6692709)

Bug: 166295507
Merged-In: I70ea776b8589ac3a7982c710c5c8b2941d86e55b
Change-Id: Ic1d535e9d2d6f80d95215240dbdb024995b045f8
diff --git a/libs/gui/tests/BLASTBufferQueue_test.cpp b/libs/gui/tests/BLASTBufferQueue_test.cpp
new file mode 100644
index 0000000..da5bbdd
--- /dev/null
+++ b/libs/gui/tests/BLASTBufferQueue_test.cpp
@@ -0,0 +1,729 @@
+/*
+ * Copyright (C) 2019 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 "BLASTBufferQueue_test"
+
+#include <gui/BLASTBufferQueue.h>
+
+#include <android/hardware/graphics/common/1.2/types.h>
+#include <gui/BufferQueueCore.h>
+#include <gui/BufferQueueProducer.h>
+#include <gui/FrameTimestamps.h>
+#include <gui/IGraphicBufferProducer.h>
+#include <gui/IProducerListener.h>
+#include <gui/SurfaceComposerClient.h>
+#include <private/gui/ComposerService.h>
+#include <ui/DisplayConfig.h>
+#include <ui/GraphicBuffer.h>
+#include <ui/GraphicTypes.h>
+#include <ui/Transform.h>
+
+#include <gtest/gtest.h>
+
+using namespace std::chrono_literals;
+
+namespace android {
+
+using Transaction = SurfaceComposerClient::Transaction;
+using android::hardware::graphics::common::V1_2::BufferUsage;
+
+class BLASTBufferQueueHelper {
+public:
+    BLASTBufferQueueHelper(const sp<SurfaceControl>& sc, int width, int height) {
+        mBlastBufferQueueAdapter = new BLASTBufferQueue(sc, width, height);
+    }
+
+    void update(const sp<SurfaceControl>& sc, int width, int height) {
+        mBlastBufferQueueAdapter->update(sc, width, height);
+    }
+
+    void setNextTransaction(Transaction* next) {
+        mBlastBufferQueueAdapter->setNextTransaction(next);
+    }
+
+    int getWidth() { return mBlastBufferQueueAdapter->mWidth; }
+
+    int getHeight() { return mBlastBufferQueueAdapter->mHeight; }
+
+    Transaction* getNextTransaction() { return mBlastBufferQueueAdapter->mNextTransaction; }
+
+    sp<IGraphicBufferProducer> getIGraphicBufferProducer() {
+        return mBlastBufferQueueAdapter->getIGraphicBufferProducer();
+    }
+
+    const sp<SurfaceControl> getSurfaceControl() {
+        return mBlastBufferQueueAdapter->mSurfaceControl;
+    }
+
+    void waitForCallbacks() {
+        std::unique_lock lock{mBlastBufferQueueAdapter->mMutex};
+        while (mBlastBufferQueueAdapter->mSubmitted.size() > 0) {
+            mBlastBufferQueueAdapter->mCallbackCV.wait(lock);
+        }
+    }
+
+private:
+    sp<BLASTBufferQueue> mBlastBufferQueueAdapter;
+};
+
+class BLASTBufferQueueTest : public ::testing::Test {
+public:
+protected:
+    BLASTBufferQueueTest() {
+        const ::testing::TestInfo* const testInfo =
+                ::testing::UnitTest::GetInstance()->current_test_info();
+        ALOGV("Begin test: %s.%s", testInfo->test_case_name(), testInfo->name());
+    }
+
+    ~BLASTBufferQueueTest() {
+        const ::testing::TestInfo* const testInfo =
+                ::testing::UnitTest::GetInstance()->current_test_info();
+        ALOGV("End test:   %s.%s", testInfo->test_case_name(), testInfo->name());
+    }
+
+    void SetUp() {
+        mComposer = ComposerService::getComposerService();
+        mClient = new SurfaceComposerClient();
+        mDisplayToken = mClient->getInternalDisplayToken();
+        ASSERT_NE(nullptr, mDisplayToken.get());
+        Transaction t;
+        t.setDisplayLayerStack(mDisplayToken, 0);
+        t.apply();
+        t.clear();
+
+        DisplayConfig config;
+        ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayConfig(mDisplayToken, &config));
+        const ui::Size& resolution = config.resolution;
+        mDisplayWidth = resolution.getWidth();
+        mDisplayHeight = resolution.getHeight();
+
+        mSurfaceControl = mClient->createSurface(String8("TestSurface"), mDisplayWidth,
+                                                 mDisplayHeight, PIXEL_FORMAT_RGBA_8888,
+                                                 ISurfaceComposerClient::eFXSurfaceBufferState,
+                                                 /*parent*/ nullptr);
+        t.setLayerStack(mSurfaceControl, 0)
+                .setLayer(mSurfaceControl, std::numeric_limits<int32_t>::max())
+                .setFrame(mSurfaceControl, Rect(resolution))
+                .show(mSurfaceControl)
+                .setDataspace(mSurfaceControl, ui::Dataspace::V0_SRGB)
+                .apply();
+    }
+
+    void setUpProducer(BLASTBufferQueueHelper adapter, sp<IGraphicBufferProducer>& producer) {
+        auto igbProducer = adapter.getIGraphicBufferProducer();
+        ASSERT_NE(nullptr, igbProducer.get());
+        ASSERT_EQ(NO_ERROR, igbProducer->setMaxDequeuedBufferCount(2));
+        IGraphicBufferProducer::QueueBufferOutput qbOutput;
+        ASSERT_EQ(NO_ERROR,
+                  igbProducer->connect(new StubProducerListener, NATIVE_WINDOW_API_CPU, false,
+                                       &qbOutput));
+        ASSERT_NE(ui::Transform::ROT_INVALID, qbOutput.transformHint);
+        producer = igbProducer;
+    }
+
+    void fillBuffer(uint32_t* bufData, Rect rect, uint32_t stride, uint8_t r, uint8_t g,
+                    uint8_t b) {
+        for (uint32_t row = rect.top; row < rect.bottom; row++) {
+            for (uint32_t col = rect.left; col < rect.right; col++) {
+                uint8_t* pixel = (uint8_t*)(bufData + (row * stride) + col);
+                *pixel = r;
+                *(pixel + 1) = g;
+                *(pixel + 2) = b;
+                *(pixel + 3) = 255;
+            }
+        }
+    }
+
+    void fillQuadrants(sp<GraphicBuffer>& buf) {
+        const auto bufWidth = buf->getWidth();
+        const auto bufHeight = buf->getHeight();
+        uint32_t* bufData;
+        buf->lock(static_cast<uint32_t>(GraphicBuffer::USAGE_SW_WRITE_OFTEN),
+                  reinterpret_cast<void**>(&bufData));
+        fillBuffer(bufData, Rect(0, 0, bufWidth / 2, bufHeight / 2), buf->getStride(), 0, 0, 0);
+        fillBuffer(bufData, Rect(bufWidth / 2, 0, bufWidth, bufHeight / 2), buf->getStride(), 255,
+                   0, 0);
+        fillBuffer(bufData, Rect(bufWidth / 2, bufHeight / 2, bufWidth, bufHeight),
+                   buf->getStride(), 0, 255, 0);
+        fillBuffer(bufData, Rect(0, bufHeight / 2, bufWidth / 2, bufHeight), buf->getStride(), 0, 0,
+                   255);
+        buf->unlock();
+    }
+
+    void checkScreenCapture(uint8_t r, uint8_t g, uint8_t b, Rect region, int32_t border = 0,
+                            bool outsideRegion = false) {
+        const auto epsilon = 3;
+        const auto width = mScreenCaptureBuf->getWidth();
+        const auto height = mScreenCaptureBuf->getHeight();
+        const auto stride = mScreenCaptureBuf->getStride();
+
+        uint32_t* bufData;
+        mScreenCaptureBuf->lock(static_cast<uint32_t>(GraphicBuffer::USAGE_SW_READ_OFTEN),
+                                reinterpret_cast<void**>(&bufData));
+
+        for (uint32_t row = 0; row < height; row++) {
+            for (uint32_t col = 0; col < width; col++) {
+                uint8_t* pixel = (uint8_t*)(bufData + (row * stride) + col);
+                bool inRegion;
+                if (!outsideRegion) {
+                    inRegion = row >= region.top + border && row < region.bottom - border &&
+                            col >= region.left + border && col < region.right - border;
+                } else {
+                    inRegion = row >= region.top - border && row < region.bottom + border &&
+                            col >= region.left - border && col < region.right + border;
+                }
+                if (!outsideRegion && inRegion) {
+                    EXPECT_GE(epsilon, abs(r - *(pixel)));
+                    EXPECT_GE(epsilon, abs(g - *(pixel + 1)));
+                    EXPECT_GE(epsilon, abs(b - *(pixel + 2)));
+                } else if (outsideRegion && !inRegion) {
+                    EXPECT_GE(epsilon, abs(r - *(pixel)));
+                    EXPECT_GE(epsilon, abs(g - *(pixel + 1)));
+                    EXPECT_GE(epsilon, abs(b - *(pixel + 2)));
+                }
+            }
+        }
+        mScreenCaptureBuf->unlock();
+        ASSERT_EQ(false, ::testing::Test::HasFailure());
+    }
+
+    sp<SurfaceComposerClient> mClient;
+    sp<ISurfaceComposer> mComposer;
+
+    sp<IBinder> mDisplayToken;
+
+    sp<SurfaceControl> mSurfaceControl;
+    sp<GraphicBuffer> mScreenCaptureBuf;
+
+    uint32_t mDisplayWidth;
+    uint32_t mDisplayHeight;
+};
+
+TEST_F(BLASTBufferQueueTest, CreateBLASTBufferQueue) {
+    // create BLASTBufferQueue adapter associated with this surface
+    BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight);
+    ASSERT_EQ(mSurfaceControl, adapter.getSurfaceControl());
+    ASSERT_EQ(mDisplayWidth, adapter.getWidth());
+    ASSERT_EQ(mDisplayHeight, adapter.getHeight());
+    ASSERT_EQ(nullptr, adapter.getNextTransaction());
+}
+
+TEST_F(BLASTBufferQueueTest, Update) {
+    BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight);
+    sp<SurfaceControl> updateSurface =
+            mClient->createSurface(String8("UpdateTest"), mDisplayWidth / 2, mDisplayHeight / 2,
+                                   PIXEL_FORMAT_RGBA_8888);
+    adapter.update(updateSurface, mDisplayWidth / 2, mDisplayHeight / 2);
+    ASSERT_EQ(updateSurface, adapter.getSurfaceControl());
+    ASSERT_EQ(mDisplayWidth / 2, adapter.getWidth());
+    ASSERT_EQ(mDisplayHeight / 2, adapter.getHeight());
+}
+
+TEST_F(BLASTBufferQueueTest, SetNextTransaction) {
+    BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight);
+    Transaction next;
+    adapter.setNextTransaction(&next);
+    ASSERT_EQ(&next, adapter.getNextTransaction());
+}
+
+TEST_F(BLASTBufferQueueTest, DISABLED_onFrameAvailable_ApplyDesiredPresentTime) {
+    BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight);
+    sp<IGraphicBufferProducer> igbProducer;
+    setUpProducer(adapter, igbProducer);
+
+    int slot;
+    sp<Fence> fence;
+    sp<GraphicBuffer> buf;
+    auto ret = igbProducer->dequeueBuffer(&slot, &fence, mDisplayWidth, mDisplayHeight,
+                                          PIXEL_FORMAT_RGBA_8888, GRALLOC_USAGE_SW_WRITE_OFTEN,
+                                          nullptr, nullptr);
+    ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, ret);
+    ASSERT_EQ(OK, igbProducer->requestBuffer(slot, &buf));
+
+    nsecs_t desiredPresentTime = systemTime() + nsecs_t(5 * 1e8);
+    IGraphicBufferProducer::QueueBufferOutput qbOutput;
+    IGraphicBufferProducer::QueueBufferInput input(desiredPresentTime, false, HAL_DATASPACE_UNKNOWN,
+                                                   Rect(mDisplayWidth, mDisplayHeight),
+                                                   NATIVE_WINDOW_SCALING_MODE_FREEZE, 0,
+                                                   Fence::NO_FENCE);
+    igbProducer->queueBuffer(slot, input, &qbOutput);
+    ASSERT_NE(ui::Transform::ROT_INVALID, qbOutput.transformHint);
+
+    adapter.waitForCallbacks();
+    ASSERT_GE(systemTime(), desiredPresentTime);
+}
+
+TEST_F(BLASTBufferQueueTest, onFrameAvailable_Apply) {
+    uint8_t r = 255;
+    uint8_t g = 0;
+    uint8_t b = 0;
+
+    BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight);
+    sp<IGraphicBufferProducer> igbProducer;
+    setUpProducer(adapter, igbProducer);
+
+    int slot;
+    sp<Fence> fence;
+    sp<GraphicBuffer> buf;
+    auto ret = igbProducer->dequeueBuffer(&slot, &fence, mDisplayWidth, mDisplayHeight,
+                                          PIXEL_FORMAT_RGBA_8888, GRALLOC_USAGE_SW_WRITE_OFTEN,
+                                          nullptr, nullptr);
+    ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, ret);
+    ASSERT_EQ(OK, igbProducer->requestBuffer(slot, &buf));
+
+    uint32_t* bufData;
+    buf->lock(static_cast<uint32_t>(GraphicBuffer::USAGE_SW_WRITE_OFTEN),
+              reinterpret_cast<void**>(&bufData));
+    fillBuffer(bufData, Rect(buf->getWidth(), buf->getHeight()), buf->getStride(), r, g, b);
+    buf->unlock();
+
+    IGraphicBufferProducer::QueueBufferOutput qbOutput;
+    IGraphicBufferProducer::QueueBufferInput input(systemTime(), false, HAL_DATASPACE_UNKNOWN,
+                                                   Rect(mDisplayWidth, mDisplayHeight),
+                                                   NATIVE_WINDOW_SCALING_MODE_FREEZE, 0,
+                                                   Fence::NO_FENCE);
+    igbProducer->queueBuffer(slot, input, &qbOutput);
+    ASSERT_NE(ui::Transform::ROT_INVALID, qbOutput.transformHint);
+
+    adapter.waitForCallbacks();
+
+    // capture screen and verify that it is red
+    bool capturedSecureLayers;
+    ASSERT_EQ(NO_ERROR,
+              mComposer->captureScreen(mDisplayToken, &mScreenCaptureBuf, capturedSecureLayers,
+                                       ui::Dataspace::V0_SRGB, ui::PixelFormat::RGBA_8888, Rect(),
+                                       mDisplayWidth, mDisplayHeight,
+                                       /*useIdentityTransform*/ false));
+    ASSERT_NO_FATAL_FAILURE(
+            checkScreenCapture(r, g, b, {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight}));
+}
+
+TEST_F(BLASTBufferQueueTest, TripleBuffering) {
+    BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight);
+    sp<IGraphicBufferProducer> igbProducer;
+    setUpProducer(adapter, igbProducer);
+
+    std::vector<std::pair<int, sp<Fence>>> allocated;
+    for (int i = 0; i < 3; i++) {
+        int slot;
+        sp<Fence> fence;
+        sp<GraphicBuffer> buf;
+        auto ret = igbProducer->dequeueBuffer(&slot, &fence, mDisplayWidth, mDisplayHeight,
+                                              PIXEL_FORMAT_RGBA_8888, GRALLOC_USAGE_SW_WRITE_OFTEN,
+                                              nullptr, nullptr);
+        ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, ret);
+        ASSERT_EQ(OK, igbProducer->requestBuffer(slot, &buf));
+        allocated.push_back({slot, fence});
+    }
+    for (int i = 0; i < allocated.size(); i++) {
+        igbProducer->cancelBuffer(allocated[i].first, allocated[i].second);
+    }
+
+    for (int i = 0; i < 100; i++) {
+        int slot;
+        sp<Fence> fence;
+        sp<GraphicBuffer> buf;
+        auto ret = igbProducer->dequeueBuffer(&slot, &fence, mDisplayWidth, mDisplayHeight,
+                                              PIXEL_FORMAT_RGBA_8888, GRALLOC_USAGE_SW_WRITE_OFTEN,
+                                              nullptr, nullptr);
+        ASSERT_EQ(NO_ERROR, ret);
+        IGraphicBufferProducer::QueueBufferOutput qbOutput;
+        IGraphicBufferProducer::QueueBufferInput input(systemTime(), false, HAL_DATASPACE_UNKNOWN,
+                                                       Rect(mDisplayWidth, mDisplayHeight),
+                                                       NATIVE_WINDOW_SCALING_MODE_FREEZE, 0,
+                                                       Fence::NO_FENCE);
+        igbProducer->queueBuffer(slot, input, &qbOutput);
+    }
+    adapter.waitForCallbacks();
+}
+
+TEST_F(BLASTBufferQueueTest, SetCrop_Item) {
+    uint8_t r = 255;
+    uint8_t g = 0;
+    uint8_t b = 0;
+
+    BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight);
+    sp<IGraphicBufferProducer> igbProducer;
+    setUpProducer(adapter, igbProducer);
+    int slot;
+    sp<Fence> fence;
+    sp<GraphicBuffer> buf;
+    auto ret = igbProducer->dequeueBuffer(&slot, &fence, mDisplayWidth, mDisplayHeight,
+                                          PIXEL_FORMAT_RGBA_8888, GRALLOC_USAGE_SW_WRITE_OFTEN,
+                                          nullptr, nullptr);
+    ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, ret);
+    ASSERT_EQ(OK, igbProducer->requestBuffer(slot, &buf));
+
+    uint32_t* bufData;
+    buf->lock(static_cast<uint32_t>(GraphicBuffer::USAGE_SW_WRITE_OFTEN),
+              reinterpret_cast<void**>(&bufData));
+    fillBuffer(bufData, Rect(buf->getWidth(), buf->getHeight() / 2), buf->getStride(), r, g, b);
+    buf->unlock();
+
+    IGraphicBufferProducer::QueueBufferOutput qbOutput;
+    IGraphicBufferProducer::QueueBufferInput input(systemTime(), false, HAL_DATASPACE_UNKNOWN,
+                                                   Rect(mDisplayWidth, mDisplayHeight / 2),
+                                                   NATIVE_WINDOW_SCALING_MODE_FREEZE, 0,
+                                                   Fence::NO_FENCE);
+    igbProducer->queueBuffer(slot, input, &qbOutput);
+    ASSERT_NE(ui::Transform::ROT_INVALID, qbOutput.transformHint);
+
+    adapter.waitForCallbacks();
+    // capture screen and verify that it is red
+    bool capturedSecureLayers;
+    ASSERT_EQ(NO_ERROR,
+              mComposer->captureScreen(mDisplayToken, &mScreenCaptureBuf, capturedSecureLayers,
+                                       ui::Dataspace::V0_SRGB, ui::PixelFormat::RGBA_8888, Rect(),
+                                       mDisplayWidth, mDisplayHeight,
+                                       /*useIdentityTransform*/ false));
+    ASSERT_NO_FATAL_FAILURE(
+            checkScreenCapture(r, g, b, {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight}));
+}
+
+TEST_F(BLASTBufferQueueTest, SetCrop_ScalingModeScaleCrop) {
+    uint8_t r = 255;
+    uint8_t g = 0;
+    uint8_t b = 0;
+
+    int32_t bufferSideLength =
+            (mDisplayWidth < mDisplayHeight) ? mDisplayWidth / 2 : mDisplayHeight / 2;
+    int32_t finalCropSideLength = bufferSideLength / 2;
+
+    auto bg = mClient->createSurface(String8("BGTest"), 0, 0, PIXEL_FORMAT_RGBA_8888,
+                                     ISurfaceComposerClient::eFXSurfaceEffect);
+    ASSERT_NE(nullptr, bg.get());
+    Transaction t;
+    t.setLayerStack(bg, 0)
+            .setCrop_legacy(bg, Rect(0, 0, mDisplayWidth, mDisplayHeight))
+            .setColor(bg, half3{0, 0, 0})
+            .setLayer(bg, 0)
+            .apply();
+
+    BLASTBufferQueueHelper adapter(mSurfaceControl, bufferSideLength, bufferSideLength);
+    sp<IGraphicBufferProducer> igbProducer;
+    setUpProducer(adapter, igbProducer);
+    int slot;
+    sp<Fence> fence;
+    sp<GraphicBuffer> buf;
+    auto ret = igbProducer->dequeueBuffer(&slot, &fence, bufferSideLength, bufferSideLength,
+                                          PIXEL_FORMAT_RGBA_8888, GRALLOC_USAGE_SW_WRITE_OFTEN,
+                                          nullptr, nullptr);
+    ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, ret);
+    ASSERT_EQ(OK, igbProducer->requestBuffer(slot, &buf));
+
+    uint32_t* bufData;
+    buf->lock(static_cast<uint32_t>(GraphicBuffer::USAGE_SW_WRITE_OFTEN),
+              reinterpret_cast<void**>(&bufData));
+    fillBuffer(bufData, Rect(buf->getWidth(), buf->getHeight()), buf->getStride(), 0, 0, 0);
+    fillBuffer(bufData,
+               Rect(finalCropSideLength / 2, 0, buf->getWidth() - finalCropSideLength / 2,
+                    buf->getHeight()),
+               buf->getStride(), r, g, b);
+    buf->unlock();
+
+    IGraphicBufferProducer::QueueBufferOutput qbOutput;
+    IGraphicBufferProducer::QueueBufferInput input(systemTime(), false, HAL_DATASPACE_UNKNOWN,
+                                                   Rect(bufferSideLength, finalCropSideLength),
+                                                   NATIVE_WINDOW_SCALING_MODE_SCALE_CROP, 0,
+                                                   Fence::NO_FENCE);
+    igbProducer->queueBuffer(slot, input, &qbOutput);
+    ASSERT_NE(ui::Transform::ROT_INVALID, qbOutput.transformHint);
+
+    adapter.waitForCallbacks();
+    // capture screen and verify that it is red
+    bool capturedSecureLayers;
+    ASSERT_EQ(NO_ERROR,
+              mComposer->captureScreen(mDisplayToken, &mScreenCaptureBuf, capturedSecureLayers,
+                                       ui::Dataspace::V0_SRGB, ui::PixelFormat::RGBA_8888, Rect(),
+                                       mDisplayWidth, mDisplayHeight,
+                                       /*useIdentityTransform*/ false));
+    ASSERT_NO_FATAL_FAILURE(
+            checkScreenCapture(r, g, b,
+                               {0, 0, (int32_t)bufferSideLength, (int32_t)bufferSideLength}));
+    ASSERT_NO_FATAL_FAILURE(
+            checkScreenCapture(0, 0, 0,
+                               {0, 0, (int32_t)bufferSideLength, (int32_t)bufferSideLength},
+                               /*border*/ 0, /*outsideRegion*/ true));
+}
+
+class BLASTBufferQueueTransformTest : public BLASTBufferQueueTest {
+public:
+    void test(uint32_t tr) {
+        BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight);
+        sp<IGraphicBufferProducer> igbProducer;
+        setUpProducer(adapter, igbProducer);
+
+        auto bufWidth = mDisplayWidth;
+        auto bufHeight = mDisplayHeight;
+        int slot;
+        sp<Fence> fence;
+        sp<GraphicBuffer> buf;
+
+        auto ret = igbProducer->dequeueBuffer(&slot, &fence, bufWidth, bufHeight,
+                                              PIXEL_FORMAT_RGBA_8888, GRALLOC_USAGE_SW_WRITE_OFTEN,
+                                              nullptr, nullptr);
+        ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, ret);
+        ASSERT_EQ(OK, igbProducer->requestBuffer(slot, &buf));
+
+        fillQuadrants(buf);
+
+        IGraphicBufferProducer::QueueBufferOutput qbOutput;
+        IGraphicBufferProducer::QueueBufferInput input(systemTime(), false, HAL_DATASPACE_UNKNOWN,
+                                                       Rect(bufWidth, bufHeight),
+                                                       NATIVE_WINDOW_SCALING_MODE_FREEZE, tr,
+                                                       Fence::NO_FENCE);
+        igbProducer->queueBuffer(slot, input, &qbOutput);
+        ASSERT_NE(ui::Transform::ROT_INVALID, qbOutput.transformHint);
+
+        adapter.waitForCallbacks();
+        bool capturedSecureLayers;
+        ASSERT_EQ(NO_ERROR,
+                  mComposer->captureScreen(mDisplayToken, &mScreenCaptureBuf, capturedSecureLayers,
+                                           ui::Dataspace::V0_SRGB, ui::PixelFormat::RGBA_8888,
+                                           Rect(), mDisplayWidth, mDisplayHeight,
+                                           /*useIdentityTransform*/ false));
+        switch (tr) {
+            case ui::Transform::ROT_0:
+                ASSERT_NO_FATAL_FAILURE(checkScreenCapture(0, 0, 0,
+                                                           {0, 0, (int32_t)mDisplayWidth / 2,
+                                                            (int32_t)mDisplayHeight / 2},
+                                                           1));
+                ASSERT_NO_FATAL_FAILURE(
+                        checkScreenCapture(255, 0, 0,
+                                           {(int32_t)mDisplayWidth / 2, 0, (int32_t)mDisplayWidth,
+                                            (int32_t)mDisplayHeight / 2},
+                                           1));
+                ASSERT_NO_FATAL_FAILURE(
+                        checkScreenCapture(0, 255, 0,
+                                           {(int32_t)mDisplayWidth / 2, (int32_t)mDisplayHeight / 2,
+                                            (int32_t)mDisplayWidth, (int32_t)mDisplayHeight},
+                                           1));
+                ASSERT_NO_FATAL_FAILURE(
+                        checkScreenCapture(0, 0, 255,
+                                           {0, (int32_t)mDisplayHeight / 2,
+                                            (int32_t)mDisplayWidth / 2, (int32_t)mDisplayHeight},
+                                           1));
+                break;
+            case ui::Transform::FLIP_H:
+                ASSERT_NO_FATAL_FAILURE(checkScreenCapture(255, 0, 0,
+                                                           {0, 0, (int32_t)mDisplayWidth / 2,
+                                                            (int32_t)mDisplayHeight / 2},
+                                                           1));
+                ASSERT_NO_FATAL_FAILURE(
+                        checkScreenCapture(0, 0, 0,
+                                           {(int32_t)mDisplayWidth / 2, 0, (int32_t)mDisplayWidth,
+                                            (int32_t)mDisplayHeight / 2},
+                                           1));
+                ASSERT_NO_FATAL_FAILURE(
+                        checkScreenCapture(0, 0, 255,
+                                           {(int32_t)mDisplayWidth / 2, (int32_t)mDisplayHeight / 2,
+                                            (int32_t)mDisplayWidth, (int32_t)mDisplayHeight},
+                                           1));
+                ASSERT_NO_FATAL_FAILURE(
+                        checkScreenCapture(0, 255, 0,
+                                           {0, (int32_t)mDisplayHeight / 2,
+                                            (int32_t)mDisplayWidth / 2, (int32_t)mDisplayHeight},
+                                           1));
+                break;
+            case ui::Transform::FLIP_V:
+                ASSERT_NO_FATAL_FAILURE(checkScreenCapture(0, 0, 255,
+                                                           {0, 0, (int32_t)mDisplayWidth / 2,
+                                                            (int32_t)mDisplayHeight / 2},
+                                                           1));
+                ASSERT_NO_FATAL_FAILURE(
+                        checkScreenCapture(0, 255, 0,
+                                           {(int32_t)mDisplayWidth / 2, 0, (int32_t)mDisplayWidth,
+                                            (int32_t)mDisplayHeight / 2},
+                                           1));
+                ASSERT_NO_FATAL_FAILURE(
+                        checkScreenCapture(255, 0, 0,
+                                           {(int32_t)mDisplayWidth / 2, (int32_t)mDisplayHeight / 2,
+                                            (int32_t)mDisplayWidth, (int32_t)mDisplayHeight},
+                                           1));
+                ASSERT_NO_FATAL_FAILURE(
+                        checkScreenCapture(0, 0, 0,
+                                           {0, (int32_t)mDisplayHeight / 2,
+                                            (int32_t)mDisplayWidth / 2, (int32_t)mDisplayHeight},
+                                           1));
+                break;
+            case ui::Transform::ROT_90:
+                ASSERT_NO_FATAL_FAILURE(checkScreenCapture(0, 0, 255,
+                                                           {0, 0, (int32_t)mDisplayWidth / 2,
+                                                            (int32_t)mDisplayHeight / 2},
+                                                           1));
+                ASSERT_NO_FATAL_FAILURE(
+                        checkScreenCapture(0, 0, 0,
+                                           {(int32_t)mDisplayWidth / 2, 0, (int32_t)mDisplayWidth,
+                                            (int32_t)mDisplayHeight / 2},
+                                           1));
+                ASSERT_NO_FATAL_FAILURE(
+                        checkScreenCapture(255, 0, 0,
+                                           {(int32_t)mDisplayWidth / 2, (int32_t)mDisplayHeight / 2,
+                                            (int32_t)mDisplayWidth, (int32_t)mDisplayHeight},
+                                           1));
+                ASSERT_NO_FATAL_FAILURE(
+                        checkScreenCapture(0, 255, 0,
+                                           {0, (int32_t)mDisplayHeight / 2,
+                                            (int32_t)mDisplayWidth / 2, (int32_t)mDisplayHeight},
+                                           1));
+                break;
+            case ui::Transform::ROT_180:
+                ASSERT_NO_FATAL_FAILURE(checkScreenCapture(0, 255, 0,
+                                                           {0, 0, (int32_t)mDisplayWidth / 2,
+                                                            (int32_t)mDisplayHeight / 2},
+                                                           1));
+                ASSERT_NO_FATAL_FAILURE(
+                        checkScreenCapture(0, 0, 255,
+                                           {(int32_t)mDisplayWidth / 2, 0, (int32_t)mDisplayWidth,
+                                            (int32_t)mDisplayHeight / 2},
+                                           1));
+                ASSERT_NO_FATAL_FAILURE(
+                        checkScreenCapture(0, 0, 0,
+                                           {(int32_t)mDisplayWidth / 2, (int32_t)mDisplayHeight / 2,
+                                            (int32_t)mDisplayWidth, (int32_t)mDisplayHeight},
+                                           1));
+                ASSERT_NO_FATAL_FAILURE(
+                        checkScreenCapture(255, 0, 0,
+                                           {0, (int32_t)mDisplayHeight / 2,
+                                            (int32_t)mDisplayWidth / 2, (int32_t)mDisplayHeight},
+                                           1));
+                break;
+            case ui::Transform::ROT_270:
+                ASSERT_NO_FATAL_FAILURE(checkScreenCapture(255, 0, 0,
+                                                           {0, 0, (int32_t)mDisplayWidth / 2,
+                                                            (int32_t)mDisplayHeight / 2},
+                                                           1));
+                ASSERT_NO_FATAL_FAILURE(
+                        checkScreenCapture(0, 255, 0,
+                                           {(int32_t)mDisplayWidth / 2, 0, (int32_t)mDisplayWidth,
+                                            (int32_t)mDisplayHeight / 2},
+                                           1));
+                ASSERT_NO_FATAL_FAILURE(
+                        checkScreenCapture(0, 0, 255,
+                                           {(int32_t)mDisplayWidth / 2, (int32_t)mDisplayHeight / 2,
+                                            (int32_t)mDisplayWidth, (int32_t)mDisplayHeight},
+                                           1));
+                ASSERT_NO_FATAL_FAILURE(
+                        checkScreenCapture(0, 0, 0,
+                                           {0, (int32_t)mDisplayHeight / 2,
+                                            (int32_t)mDisplayWidth / 2, (int32_t)mDisplayHeight},
+                                           1));
+        }
+    }
+};
+
+TEST_F(BLASTBufferQueueTransformTest, setTransform_ROT_0) {
+    test(ui::Transform::ROT_0);
+}
+
+TEST_F(BLASTBufferQueueTransformTest, setTransform_FLIP_H) {
+    test(ui::Transform::FLIP_H);
+}
+
+TEST_F(BLASTBufferQueueTransformTest, setTransform_FLIP_V) {
+    test(ui::Transform::FLIP_V);
+}
+
+TEST_F(BLASTBufferQueueTransformTest, setTransform_ROT_90) {
+    test(ui::Transform::ROT_90);
+}
+
+TEST_F(BLASTBufferQueueTransformTest, setTransform_ROT_180) {
+    test(ui::Transform::ROT_180);
+}
+
+TEST_F(BLASTBufferQueueTransformTest, setTransform_ROT_270) {
+    test(ui::Transform::ROT_270);
+}
+
+class BLASTFrameEventHistoryTest : public BLASTBufferQueueTest {
+public:
+    void setUpAndQueueBuffer(const sp<IGraphicBufferProducer>& igbProducer,
+                             nsecs_t* requestedPresentTime, nsecs_t* postedTime,
+                             IGraphicBufferProducer::QueueBufferOutput* qbOutput,
+                             bool getFrameTimestamps) {
+        int slot;
+        sp<Fence> fence;
+        sp<GraphicBuffer> buf;
+        auto ret = igbProducer->dequeueBuffer(&slot, &fence, mDisplayWidth, mDisplayHeight,
+                                              PIXEL_FORMAT_RGBA_8888, GRALLOC_USAGE_SW_WRITE_OFTEN,
+                                              nullptr, nullptr);
+        ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, ret);
+        ASSERT_EQ(OK, igbProducer->requestBuffer(slot, &buf));
+
+        nsecs_t requestedTime = systemTime();
+        if (requestedPresentTime) *requestedPresentTime = requestedTime;
+        IGraphicBufferProducer::QueueBufferInput input(requestedTime, false, HAL_DATASPACE_UNKNOWN,
+                                                       Rect(mDisplayWidth, mDisplayHeight),
+                                                       NATIVE_WINDOW_SCALING_MODE_FREEZE, 0,
+                                                       Fence::NO_FENCE, /*sticky*/ 0,
+                                                       getFrameTimestamps);
+        if (postedTime) *postedTime = systemTime();
+        igbProducer->queueBuffer(slot, input, qbOutput);
+    }
+};
+
+TEST_F(BLASTFrameEventHistoryTest, FrameEventHistory_Basic) {
+    BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight);
+    sp<IGraphicBufferProducer> igbProducer;
+    ProducerFrameEventHistory history;
+    setUpProducer(adapter, igbProducer);
+
+    IGraphicBufferProducer::QueueBufferOutput qbOutput;
+    nsecs_t requestedPresentTimeA = 0;
+    nsecs_t postedTimeA = 0;
+    setUpAndQueueBuffer(igbProducer, &requestedPresentTimeA, &postedTimeA, &qbOutput, true);
+    history.applyDelta(qbOutput.frameTimestamps);
+
+    FrameEvents* events = nullptr;
+    events = history.getFrame(1);
+    ASSERT_NE(nullptr, events);
+    ASSERT_EQ(1, events->frameNumber);
+    ASSERT_EQ(requestedPresentTimeA, events->requestedPresentTime);
+    ASSERT_GE(events->postedTime, postedTimeA);
+
+    adapter.waitForCallbacks();
+
+    // queue another buffer so we query for frame event deltas
+    nsecs_t requestedPresentTimeB = 0;
+    nsecs_t postedTimeB = 0;
+    setUpAndQueueBuffer(igbProducer, &requestedPresentTimeB, &postedTimeB, &qbOutput, true);
+    history.applyDelta(qbOutput.frameTimestamps);
+    events = history.getFrame(1);
+    ASSERT_NE(nullptr, events);
+
+    // frame number, requestedPresentTime, and postTime should not have changed
+    ASSERT_EQ(1, events->frameNumber);
+    ASSERT_EQ(requestedPresentTimeA, events->requestedPresentTime);
+    ASSERT_GE(events->postedTime, postedTimeA);
+
+    ASSERT_GE(events->latchTime, postedTimeA);
+    ASSERT_GE(events->dequeueReadyTime, events->latchTime);
+    ASSERT_NE(nullptr, events->gpuCompositionDoneFence);
+    ASSERT_NE(nullptr, events->displayPresentFence);
+    ASSERT_NE(nullptr, events->releaseFence);
+
+    // we should also have gotten the initial values for the next frame
+    events = history.getFrame(2);
+    ASSERT_NE(nullptr, events);
+    ASSERT_EQ(2, events->frameNumber);
+    ASSERT_EQ(requestedPresentTimeB, events->requestedPresentTime);
+    ASSERT_GE(events->postedTime, postedTimeB);
+
+    // wait for any callbacks that have not been received
+    adapter.waitForCallbacks();
+}
+} // namespace android