Break up TransactionTest helpers into util directory
Bug: 140128949
Test: build, boot, SurfaceFlinger_test
Change-Id: I10d29ae47cf29979289469832e2fcd1acefbe45f
diff --git a/services/surfaceflinger/tests/Android.bp b/services/surfaceflinger/tests/Android.bp
index 53a3611..159c2a4 100644
--- a/services/surfaceflinger/tests/Android.bp
+++ b/services/surfaceflinger/tests/Android.bp
@@ -50,6 +50,7 @@
"fakehwc",
"hwc2",
"unittests",
+ "utils",
"vsync",
"waitforvsync",
]
diff --git a/services/surfaceflinger/tests/Transaction_test.cpp b/services/surfaceflinger/tests/Transaction_test.cpp
index b1fde22..57e5c51 100644
--- a/services/surfaceflinger/tests/Transaction_test.cpp
+++ b/services/surfaceflinger/tests/Transaction_test.cpp
@@ -20,7 +20,6 @@
#include <functional>
#include <limits>
#include <ostream>
-#include <thread>
#include <gtest/gtest.h>
@@ -37,10 +36,7 @@
#include <private/android_filesystem_config.h>
#include <private/gui/ComposerService.h>
-#include <ui/ColorSpace.h>
#include <ui/DisplayInfo.h>
-#include <ui/Rect.h>
-#include <utils/String8.h>
#include <math.h>
#include <math/vec3.h>
@@ -48,293 +44,14 @@
#include <unistd.h>
#include "BufferGenerator.h"
+#include "utils/CallbackUtils.h"
+#include "utils/ColorUtils.h"
+#include "utils/ScreenshotUtils.h"
+#include "utils/TransactionUtils.h"
namespace android {
-namespace {
-
-struct Color {
- uint8_t r;
- uint8_t g;
- uint8_t b;
- uint8_t a;
-
- static const Color RED;
- static const Color GREEN;
- static const Color BLUE;
- static const Color WHITE;
- static const Color BLACK;
- static const Color TRANSPARENT;
-};
-
-const Color Color::RED{255, 0, 0, 255};
-const Color Color::GREEN{0, 255, 0, 255};
-const Color Color::BLUE{0, 0, 255, 255};
-const Color Color::WHITE{255, 255, 255, 255};
-const Color Color::BLACK{0, 0, 0, 255};
-const Color Color::TRANSPARENT{0, 0, 0, 0};
-
using android::hardware::graphics::common::V1_1::BufferUsage;
-using namespace std::chrono_literals;
-
-std::ostream& operator<<(std::ostream& os, const Color& color) {
- os << int(color.r) << ", " << int(color.g) << ", " << int(color.b) << ", " << int(color.a);
- return os;
-}
-
-// Fill a region with the specified color.
-void fillANativeWindowBufferColor(const ANativeWindow_Buffer& buffer, const Rect& rect,
- const Color& color) {
- Rect r(0, 0, buffer.width, buffer.height);
- if (!r.intersect(rect, &r)) {
- return;
- }
-
- int32_t width = r.right - r.left;
- int32_t height = r.bottom - r.top;
-
- for (int32_t row = 0; row < height; row++) {
- uint8_t* dst =
- static_cast<uint8_t*>(buffer.bits) + (buffer.stride * (r.top + row) + r.left) * 4;
- for (int32_t column = 0; column < width; column++) {
- dst[0] = color.r;
- dst[1] = color.g;
- dst[2] = color.b;
- dst[3] = color.a;
- dst += 4;
- }
- }
-}
-
-// Fill a region with the specified color.
-void fillGraphicBufferColor(const sp<GraphicBuffer>& buffer, const Rect& rect, const Color& color) {
- Rect r(0, 0, buffer->width, buffer->height);
- if (!r.intersect(rect, &r)) {
- return;
- }
-
- int32_t width = r.right - r.left;
- int32_t height = r.bottom - r.top;
-
- uint8_t* pixels;
- buffer->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
- reinterpret_cast<void**>(&pixels));
-
- for (int32_t row = 0; row < height; row++) {
- uint8_t* dst = pixels + (buffer->getStride() * (r.top + row) + r.left) * 4;
- for (int32_t column = 0; column < width; column++) {
- dst[0] = color.r;
- dst[1] = color.g;
- dst[2] = color.b;
- dst[3] = color.a;
- dst += 4;
- }
- }
- buffer->unlock();
-}
-
-// Check if a region has the specified color.
-void expectBufferColor(const sp<GraphicBuffer>& outBuffer, uint8_t* pixels, const Rect& rect,
- const Color& color, uint8_t tolerance) {
- int32_t x = rect.left;
- int32_t y = rect.top;
- int32_t width = rect.right - rect.left;
- int32_t height = rect.bottom - rect.top;
-
- int32_t bufferWidth = int32_t(outBuffer->getWidth());
- int32_t bufferHeight = int32_t(outBuffer->getHeight());
- if (x + width > bufferWidth) {
- x = std::min(x, bufferWidth);
- width = bufferWidth - x;
- }
- if (y + height > bufferHeight) {
- y = std::min(y, bufferHeight);
- height = bufferHeight - y;
- }
-
- auto colorCompare = [tolerance](uint8_t a, uint8_t b) {
- uint8_t tmp = a >= b ? a - b : b - a;
- return tmp <= tolerance;
- };
- for (int32_t j = 0; j < height; j++) {
- const uint8_t* src = pixels + (outBuffer->getStride() * (y + j) + x) * 4;
- for (int32_t i = 0; i < width; i++) {
- const uint8_t expected[4] = {color.r, color.g, color.b, color.a};
- EXPECT_TRUE(std::equal(src, src + 4, expected, colorCompare))
- << "pixel @ (" << x + i << ", " << y + j << "): "
- << "expected (" << color << "), "
- << "got (" << Color{src[0], src[1], src[2], src[3]} << ")";
- src += 4;
- }
- }
-}
-
-} // anonymous namespace
-
-using Transaction = SurfaceComposerClient::Transaction;
-
-// Fill an RGBA_8888 formatted surface with a single color.
-static void fillSurfaceRGBA8(const sp<SurfaceControl>& sc, uint8_t r, uint8_t g, uint8_t b,
- bool unlock = true) {
- ANativeWindow_Buffer outBuffer;
- sp<Surface> s = sc->getSurface();
- ASSERT_TRUE(s != nullptr);
- ASSERT_EQ(NO_ERROR, s->lock(&outBuffer, nullptr));
- uint8_t* img = reinterpret_cast<uint8_t*>(outBuffer.bits);
- for (int y = 0; y < outBuffer.height; y++) {
- for (int x = 0; x < outBuffer.width; x++) {
- uint8_t* pixel = img + (4 * (y * outBuffer.stride + x));
- pixel[0] = r;
- pixel[1] = g;
- pixel[2] = b;
- pixel[3] = 255;
- }
- }
- if (unlock) {
- ASSERT_EQ(NO_ERROR, s->unlockAndPost());
- }
-}
-
-// A ScreenCapture is a screenshot from SurfaceFlinger that can be used to check
-// individual pixel values for testing purposes.
-class ScreenCapture : public RefBase {
-public:
- static void captureScreen(std::unique_ptr<ScreenCapture>* sc) {
- captureScreen(sc, SurfaceComposerClient::getInternalDisplayToken());
- }
-
- static void captureScreen(std::unique_ptr<ScreenCapture>* sc, sp<IBinder> displayToken) {
- const auto sf = ComposerService::getComposerService();
- SurfaceComposerClient::Transaction().apply(true);
-
- sp<GraphicBuffer> outBuffer;
- ASSERT_EQ(NO_ERROR, sf->captureScreen(displayToken, &outBuffer, Rect(), 0, 0, false));
- *sc = std::make_unique<ScreenCapture>(outBuffer);
- }
-
- static void captureLayers(std::unique_ptr<ScreenCapture>* sc, sp<IBinder>& parentHandle,
- Rect crop = Rect::EMPTY_RECT, float frameScale = 1.0) {
- sp<ISurfaceComposer> sf(ComposerService::getComposerService());
- SurfaceComposerClient::Transaction().apply(true);
-
- sp<GraphicBuffer> outBuffer;
- ASSERT_EQ(NO_ERROR, sf->captureLayers(parentHandle, &outBuffer, crop, frameScale));
- *sc = std::make_unique<ScreenCapture>(outBuffer);
- }
-
- static void captureChildLayers(std::unique_ptr<ScreenCapture>* sc, sp<IBinder>& parentHandle,
- Rect crop = Rect::EMPTY_RECT, float frameScale = 1.0) {
- sp<ISurfaceComposer> sf(ComposerService::getComposerService());
- SurfaceComposerClient::Transaction().apply(true);
-
- sp<GraphicBuffer> outBuffer;
- ASSERT_EQ(NO_ERROR, sf->captureLayers(parentHandle, &outBuffer, crop, frameScale, true));
- *sc = std::make_unique<ScreenCapture>(outBuffer);
- }
-
- static void captureChildLayersExcluding(
- std::unique_ptr<ScreenCapture>* sc, sp<IBinder>& parentHandle,
- std::unordered_set<sp<IBinder>, ISurfaceComposer::SpHash<IBinder>> excludeLayers) {
- sp<ISurfaceComposer> sf(ComposerService::getComposerService());
- SurfaceComposerClient::Transaction().apply(true);
-
- sp<GraphicBuffer> outBuffer;
- ASSERT_EQ(NO_ERROR,
- sf->captureLayers(parentHandle, &outBuffer, ui::Dataspace::V0_SRGB,
- ui::PixelFormat::RGBA_8888, Rect::EMPTY_RECT, excludeLayers,
- 1.0f, true));
- *sc = std::make_unique<ScreenCapture>(outBuffer);
- }
-
- void expectColor(const Rect& rect, const Color& color, uint8_t tolerance = 0) {
- ASSERT_EQ(HAL_PIXEL_FORMAT_RGBA_8888, mOutBuffer->getPixelFormat());
- expectBufferColor(mOutBuffer, mPixels, rect, color, tolerance);
- }
-
- void expectBorder(const Rect& rect, const Color& color, uint8_t tolerance = 0) {
- ASSERT_EQ(HAL_PIXEL_FORMAT_RGBA_8888, mOutBuffer->getPixelFormat());
- const bool leftBorder = rect.left > 0;
- const bool topBorder = rect.top > 0;
- const bool rightBorder = rect.right < int32_t(mOutBuffer->getWidth());
- const bool bottomBorder = rect.bottom < int32_t(mOutBuffer->getHeight());
-
- if (topBorder) {
- Rect top(rect.left, rect.top - 1, rect.right, rect.top);
- if (leftBorder) {
- top.left -= 1;
- }
- if (rightBorder) {
- top.right += 1;
- }
- expectColor(top, color, tolerance);
- }
- if (leftBorder) {
- Rect left(rect.left - 1, rect.top, rect.left, rect.bottom);
- expectColor(left, color, tolerance);
- }
- if (rightBorder) {
- Rect right(rect.right, rect.top, rect.right + 1, rect.bottom);
- expectColor(right, color, tolerance);
- }
- if (bottomBorder) {
- Rect bottom(rect.left, rect.bottom, rect.right, rect.bottom + 1);
- if (leftBorder) {
- bottom.left -= 1;
- }
- if (rightBorder) {
- bottom.right += 1;
- }
- expectColor(bottom, color, tolerance);
- }
- }
-
- void expectQuadrant(const Rect& rect, const Color& topLeft, const Color& topRight,
- const Color& bottomLeft, const Color& bottomRight, bool filtered = false,
- uint8_t tolerance = 0) {
- ASSERT_TRUE((rect.right - rect.left) % 2 == 0 && (rect.bottom - rect.top) % 2 == 0);
-
- const int32_t centerX = rect.left + (rect.right - rect.left) / 2;
- const int32_t centerY = rect.top + (rect.bottom - rect.top) / 2;
- // avoid checking borders due to unspecified filtering behavior
- const int32_t offsetX = filtered ? 2 : 0;
- const int32_t offsetY = filtered ? 2 : 0;
- expectColor(Rect(rect.left, rect.top, centerX - offsetX, centerY - offsetY), topLeft,
- tolerance);
- expectColor(Rect(centerX + offsetX, rect.top, rect.right, centerY - offsetY), topRight,
- tolerance);
- expectColor(Rect(rect.left, centerY + offsetY, centerX - offsetX, rect.bottom), bottomLeft,
- tolerance);
- expectColor(Rect(centerX + offsetX, centerY + offsetY, rect.right, rect.bottom),
- bottomRight, tolerance);
- }
-
- void checkPixel(uint32_t x, uint32_t y, uint8_t r, uint8_t g, uint8_t b) {
- ASSERT_EQ(HAL_PIXEL_FORMAT_RGBA_8888, mOutBuffer->getPixelFormat());
- const uint8_t* pixel = mPixels + (4 * (y * mOutBuffer->getStride() + x));
- if (r != pixel[0] || g != pixel[1] || b != pixel[2]) {
- String8 err(String8::format("pixel @ (%3d, %3d): "
- "expected [%3d, %3d, %3d], got [%3d, %3d, %3d]",
- x, y, r, g, b, pixel[0], pixel[1], pixel[2]));
- EXPECT_EQ(String8(), err) << err.string();
- }
- }
-
- void expectFGColor(uint32_t x, uint32_t y) { checkPixel(x, y, 195, 63, 63); }
-
- void expectBGColor(uint32_t x, uint32_t y) { checkPixel(x, y, 63, 63, 195); }
-
- void expectChildColor(uint32_t x, uint32_t y) { checkPixel(x, y, 200, 200, 200); }
-
- explicit ScreenCapture(const sp<GraphicBuffer>& outBuffer) : mOutBuffer(outBuffer) {
- mOutBuffer->lock(GRALLOC_USAGE_SW_READ_OFTEN, reinterpret_cast<void**>(&mPixels));
- }
-
- ~ScreenCapture() { mOutBuffer->unlock(); }
-
-private:
- sp<GraphicBuffer> mOutBuffer;
- uint8_t* mPixels = nullptr;
-};
class LayerTransactionTest : public ::testing::Test {
protected:
@@ -583,7 +300,6 @@
friend class LayerRenderPathTestHarness;
};
-enum class RenderPath { SCREENSHOT, VIRTUAL_DISPLAY };
class LayerRenderPathTestHarness {
public:
@@ -693,13 +409,6 @@
LayerRenderPathTestHarness mRenderPathHarness;
};
-// Environment for starting up binder threads. This is required for testing
-// virtual displays, as BufferQueue parameters may be queried over binder.
-class BinderEnvironment : public ::testing::Environment {
-public:
- void SetUp() override { ProcessState::self()->startThreadPool(); }
-};
-
::testing::Environment* const binderEnv =
::testing::AddGlobalTestEnvironment(new BinderEnvironment());
@@ -1338,19 +1047,6 @@
composer->captureScreen(mDisplay, &outBuffer, Rect(), 0, 0, false));
}
-/** RAII Wrapper around get/seteuid */
-class UIDFaker {
- uid_t oldId;
-public:
- UIDFaker(uid_t uid) {
- oldId = geteuid();
- seteuid(uid);
- }
- ~UIDFaker() {
- seteuid(oldId);
- }
-};
-
TEST_F(LayerTransactionTest, SetFlagsSecureEUidSystem) {
sp<SurfaceControl> layer;
ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
@@ -2934,47 +2630,6 @@
}
}
-class ColorTransformHelper {
-public:
- static void DegammaColorSingle(half& s) {
- if (s <= 0.03928f)
- s = s / 12.92f;
- else
- s = pow((s + 0.055f) / 1.055f, 2.4f);
- }
-
- static void DegammaColor(half3& color) {
- DegammaColorSingle(color.r);
- DegammaColorSingle(color.g);
- DegammaColorSingle(color.b);
- }
-
- static void GammaColorSingle(half& s) {
- if (s <= 0.0031308f) {
- s = s * 12.92f;
- } else {
- s = 1.055f * pow(s, (1.0f / 2.4f)) - 0.055f;
- }
- }
-
- static void GammaColor(half3& color) {
- GammaColorSingle(color.r);
- GammaColorSingle(color.g);
- GammaColorSingle(color.b);
- }
-
- static void applyMatrix(half3& color, const mat3& mat) {
- half3 ret = half3(0);
-
- for (int i = 0; i < 3; i++) {
- for (int j = 0; j < 3; j++) {
- ret[i] = ret[i] + color[j] * mat[j][i];
- }
- }
- color = ret;
- }
-};
-
TEST_P(LayerRenderTypeTransactionTest, SetColorTransformBasic) {
sp<SurfaceControl> colorLayer;
ASSERT_NO_FATAL_FAILURE(colorLayer =
@@ -3138,173 +2793,6 @@
}
}
-struct CallbackData {
- CallbackData() = default;
- CallbackData(nsecs_t time, const sp<Fence>& fence,
- const std::vector<SurfaceControlStats>& stats)
- : latchTime(time), presentFence(fence), surfaceControlStats(stats) {}
-
- nsecs_t latchTime;
- sp<Fence> presentFence;
- std::vector<SurfaceControlStats> surfaceControlStats;
-};
-
-class ExpectedResult {
-public:
- enum Transaction {
- NOT_PRESENTED = 0,
- PRESENTED,
- };
-
- enum Buffer {
- NOT_ACQUIRED = 0,
- ACQUIRED,
- };
-
- enum PreviousBuffer {
- NOT_RELEASED = 0,
- RELEASED,
- UNKNOWN,
- };
-
- void reset() {
- mTransactionResult = ExpectedResult::Transaction::NOT_PRESENTED;
- mExpectedSurfaceResults.clear();
- }
-
- void addSurface(ExpectedResult::Transaction transactionResult, const sp<SurfaceControl>& layer,
- ExpectedResult::Buffer bufferResult = ACQUIRED,
- ExpectedResult::PreviousBuffer previousBufferResult = NOT_RELEASED) {
- mTransactionResult = transactionResult;
- mExpectedSurfaceResults.emplace(std::piecewise_construct, std::forward_as_tuple(layer),
- std::forward_as_tuple(bufferResult, previousBufferResult));
- }
-
- void addSurfaces(ExpectedResult::Transaction transactionResult,
- const std::vector<sp<SurfaceControl>>& layers,
- ExpectedResult::Buffer bufferResult = ACQUIRED,
- ExpectedResult::PreviousBuffer previousBufferResult = NOT_RELEASED) {
- for (const auto& layer : layers) {
- addSurface(transactionResult, layer, bufferResult, previousBufferResult);
- }
- }
-
- void addExpectedPresentTime(nsecs_t expectedPresentTime) {
- mExpectedPresentTime = expectedPresentTime;
- }
-
- void verifyCallbackData(const CallbackData& callbackData) const {
- const auto& [latchTime, presentFence, surfaceControlStats] = callbackData;
- if (mTransactionResult == ExpectedResult::Transaction::PRESENTED) {
- ASSERT_GE(latchTime, 0) << "bad latch time";
- ASSERT_NE(presentFence, nullptr);
- if (mExpectedPresentTime >= 0) {
- ASSERT_EQ(presentFence->wait(3000), NO_ERROR);
- ASSERT_GE(presentFence->getSignalTime(), mExpectedPresentTime - nsecs_t(5 * 1e6));
- // if the panel is running at 30 hz, at the worst case, our expected time just
- // misses vsync and we have to wait another 33.3ms
- ASSERT_LE(presentFence->getSignalTime(),
- mExpectedPresentTime + nsecs_t(66.666666 * 1e6));
- }
- } else {
- ASSERT_EQ(presentFence, nullptr) << "transaction shouldn't have been presented";
- ASSERT_EQ(latchTime, -1) << "unpresented transactions shouldn't be latched";
- }
-
- ASSERT_EQ(surfaceControlStats.size(), mExpectedSurfaceResults.size())
- << "wrong number of surfaces";
-
- for (const auto& stats : surfaceControlStats) {
- ASSERT_NE(stats.surfaceControl, nullptr) << "returned null surface control";
-
- const auto& expectedSurfaceResult = mExpectedSurfaceResults.find(stats.surfaceControl);
- ASSERT_NE(expectedSurfaceResult, mExpectedSurfaceResults.end())
- << "unexpected surface control";
- expectedSurfaceResult->second.verifySurfaceControlStats(stats, latchTime);
- }
- }
-
-private:
- class ExpectedSurfaceResult {
- public:
- ExpectedSurfaceResult(ExpectedResult::Buffer bufferResult,
- ExpectedResult::PreviousBuffer previousBufferResult)
- : mBufferResult(bufferResult), mPreviousBufferResult(previousBufferResult) {}
-
- void verifySurfaceControlStats(const SurfaceControlStats& surfaceControlStats,
- nsecs_t latchTime) const {
- const auto& [surfaceControl, acquireTime, previousReleaseFence] = surfaceControlStats;
-
- ASSERT_EQ(acquireTime > 0, mBufferResult == ExpectedResult::Buffer::ACQUIRED)
- << "bad acquire time";
- ASSERT_LE(acquireTime, latchTime) << "acquire time should be <= latch time";
-
- if (mPreviousBufferResult == ExpectedResult::PreviousBuffer::RELEASED) {
- ASSERT_NE(previousReleaseFence, nullptr)
- << "failed to set release prev buffer fence";
- } else if (mPreviousBufferResult == ExpectedResult::PreviousBuffer::NOT_RELEASED) {
- ASSERT_EQ(previousReleaseFence, nullptr)
- << "should not have set released prev buffer fence";
- }
- }
-
- private:
- ExpectedResult::Buffer mBufferResult;
- ExpectedResult::PreviousBuffer mPreviousBufferResult;
- };
-
- struct SCHash {
- std::size_t operator()(const sp<SurfaceControl>& sc) const {
- return std::hash<IBinder*>{}(sc->getHandle().get());
- }
- };
- ExpectedResult::Transaction mTransactionResult = ExpectedResult::Transaction::NOT_PRESENTED;
- nsecs_t mExpectedPresentTime = -1;
- std::unordered_map<sp<SurfaceControl>, ExpectedSurfaceResult, SCHash> mExpectedSurfaceResults;
-};
-
-class CallbackHelper {
-public:
- static void function(void* callbackContext, nsecs_t latchTime, const sp<Fence>& presentFence,
- const std::vector<SurfaceControlStats>& stats) {
- if (!callbackContext) {
- ALOGE("failed to get callback context");
- }
- CallbackHelper* helper = static_cast<CallbackHelper*>(callbackContext);
- std::lock_guard lock(helper->mMutex);
- helper->mCallbackDataQueue.emplace(latchTime, presentFence, stats);
- helper->mConditionVariable.notify_all();
- }
-
- void getCallbackData(CallbackData* outData) {
- std::unique_lock lock(mMutex);
-
- if (mCallbackDataQueue.empty()) {
- ASSERT_NE(mConditionVariable.wait_for(lock, std::chrono::seconds(3)),
- std::cv_status::timeout)
- << "did not receive callback";
- }
-
- *outData = std::move(mCallbackDataQueue.front());
- mCallbackDataQueue.pop();
- }
-
- void verifyFinalState() {
- // Wait to see if there are extra callbacks
- std::this_thread::sleep_for(500ms);
-
- std::lock_guard lock(mMutex);
- EXPECT_EQ(mCallbackDataQueue.size(), 0) << "extra callbacks received";
- mCallbackDataQueue = {};
- }
-
- void* getContext() { return static_cast<void*>(this); }
-
- std::mutex mMutex;
- std::condition_variable mConditionVariable;
- std::queue<CallbackData> mCallbackDataQueue;
-};
-
class LayerCallbackTest : public LayerTransactionTest {
public:
virtual sp<SurfaceControl> createBufferStateLayer() {
diff --git a/services/surfaceflinger/tests/utils/CallbackUtils.h b/services/surfaceflinger/tests/utils/CallbackUtils.h
new file mode 100644
index 0000000..51ae8c4
--- /dev/null
+++ b/services/surfaceflinger/tests/utils/CallbackUtils.h
@@ -0,0 +1,196 @@
+/*
+ * 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.
+ */
+#pragma once
+
+#include <gtest/gtest.h>
+#include <gui/SurfaceComposerClient.h>
+#include <gui/SurfaceControl.h>
+#include <ui/Fence.h>
+#include <utils/Timers.h>
+#include <thread>
+
+namespace android {
+
+namespace {
+
+struct CallbackData {
+ CallbackData() = default;
+ CallbackData(nsecs_t time, const sp<Fence>& fence,
+ const std::vector<SurfaceControlStats>& stats)
+ : latchTime(time), presentFence(fence), surfaceControlStats(stats) {}
+
+ nsecs_t latchTime;
+ sp<Fence> presentFence;
+ std::vector<SurfaceControlStats> surfaceControlStats;
+};
+
+class ExpectedResult {
+public:
+ enum Transaction {
+ NOT_PRESENTED = 0,
+ PRESENTED,
+ };
+
+ enum Buffer {
+ NOT_ACQUIRED = 0,
+ ACQUIRED,
+ };
+
+ enum PreviousBuffer {
+ NOT_RELEASED = 0,
+ RELEASED,
+ UNKNOWN,
+ };
+
+ void reset() {
+ mTransactionResult = ExpectedResult::Transaction::NOT_PRESENTED;
+ mExpectedSurfaceResults.clear();
+ }
+
+ void addSurface(ExpectedResult::Transaction transactionResult, const sp<SurfaceControl>& layer,
+ ExpectedResult::Buffer bufferResult = ACQUIRED,
+ ExpectedResult::PreviousBuffer previousBufferResult = NOT_RELEASED) {
+ mTransactionResult = transactionResult;
+ mExpectedSurfaceResults.emplace(std::piecewise_construct, std::forward_as_tuple(layer),
+ std::forward_as_tuple(bufferResult, previousBufferResult));
+ }
+
+ void addSurfaces(ExpectedResult::Transaction transactionResult,
+ const std::vector<sp<SurfaceControl>>& layers,
+ ExpectedResult::Buffer bufferResult = ACQUIRED,
+ ExpectedResult::PreviousBuffer previousBufferResult = NOT_RELEASED) {
+ for (const auto& layer : layers) {
+ addSurface(transactionResult, layer, bufferResult, previousBufferResult);
+ }
+ }
+
+ void addExpectedPresentTime(nsecs_t expectedPresentTime) {
+ mExpectedPresentTime = expectedPresentTime;
+ }
+
+ void verifyCallbackData(const CallbackData& callbackData) const {
+ const auto& [latchTime, presentFence, surfaceControlStats] = callbackData;
+ if (mTransactionResult == ExpectedResult::Transaction::PRESENTED) {
+ ASSERT_GE(latchTime, 0) << "bad latch time";
+ ASSERT_NE(presentFence, nullptr);
+ if (mExpectedPresentTime >= 0) {
+ ASSERT_EQ(presentFence->wait(3000), NO_ERROR);
+ ASSERT_GE(presentFence->getSignalTime(), mExpectedPresentTime - nsecs_t(5 * 1e6));
+ // if the panel is running at 30 hz, at the worst case, our expected time just
+ // misses vsync and we have to wait another 33.3ms
+ ASSERT_LE(presentFence->getSignalTime(),
+ mExpectedPresentTime + nsecs_t(66.666666 * 1e6));
+ }
+ } else {
+ ASSERT_EQ(presentFence, nullptr) << "transaction shouldn't have been presented";
+ ASSERT_EQ(latchTime, -1) << "unpresented transactions shouldn't be latched";
+ }
+
+ ASSERT_EQ(surfaceControlStats.size(), mExpectedSurfaceResults.size())
+ << "wrong number of surfaces";
+
+ for (const auto& stats : surfaceControlStats) {
+ ASSERT_NE(stats.surfaceControl, nullptr) << "returned null surface control";
+
+ const auto& expectedSurfaceResult = mExpectedSurfaceResults.find(stats.surfaceControl);
+ ASSERT_NE(expectedSurfaceResult, mExpectedSurfaceResults.end())
+ << "unexpected surface control";
+ expectedSurfaceResult->second.verifySurfaceControlStats(stats, latchTime);
+ }
+ }
+
+private:
+ class ExpectedSurfaceResult {
+ public:
+ ExpectedSurfaceResult(ExpectedResult::Buffer bufferResult,
+ ExpectedResult::PreviousBuffer previousBufferResult)
+ : mBufferResult(bufferResult), mPreviousBufferResult(previousBufferResult) {}
+
+ void verifySurfaceControlStats(const SurfaceControlStats& surfaceControlStats,
+ nsecs_t latchTime) const {
+ const auto& [surfaceControl, acquireTime, previousReleaseFence] = surfaceControlStats;
+
+ ASSERT_EQ(acquireTime > 0, mBufferResult == ExpectedResult::Buffer::ACQUIRED)
+ << "bad acquire time";
+ ASSERT_LE(acquireTime, latchTime) << "acquire time should be <= latch time";
+
+ if (mPreviousBufferResult == ExpectedResult::PreviousBuffer::RELEASED) {
+ ASSERT_NE(previousReleaseFence, nullptr)
+ << "failed to set release prev buffer fence";
+ } else if (mPreviousBufferResult == ExpectedResult::PreviousBuffer::NOT_RELEASED) {
+ ASSERT_EQ(previousReleaseFence, nullptr)
+ << "should not have set released prev buffer fence";
+ }
+ }
+
+ private:
+ ExpectedResult::Buffer mBufferResult;
+ ExpectedResult::PreviousBuffer mPreviousBufferResult;
+ };
+
+ struct SCHash {
+ std::size_t operator()(const sp<SurfaceControl>& sc) const {
+ return std::hash<IBinder*>{}(sc->getHandle().get());
+ }
+ };
+ ExpectedResult::Transaction mTransactionResult = ExpectedResult::Transaction::NOT_PRESENTED;
+ nsecs_t mExpectedPresentTime = -1;
+ std::unordered_map<sp<SurfaceControl>, ExpectedSurfaceResult, SCHash> mExpectedSurfaceResults;
+};
+
+class CallbackHelper {
+public:
+ static void function(void* callbackContext, nsecs_t latchTime, const sp<Fence>& presentFence,
+ const std::vector<SurfaceControlStats>& stats) {
+ if (!callbackContext) {
+ ALOGE("failed to get callback context");
+ }
+ CallbackHelper* helper = static_cast<CallbackHelper*>(callbackContext);
+ std::lock_guard lock(helper->mMutex);
+ helper->mCallbackDataQueue.emplace(latchTime, presentFence, stats);
+ helper->mConditionVariable.notify_all();
+ }
+
+ void getCallbackData(CallbackData* outData) {
+ std::unique_lock lock(mMutex);
+
+ if (mCallbackDataQueue.empty()) {
+ ASSERT_NE(mConditionVariable.wait_for(lock, std::chrono::seconds(3)),
+ std::cv_status::timeout)
+ << "did not receive callback";
+ }
+
+ *outData = std::move(mCallbackDataQueue.front());
+ mCallbackDataQueue.pop();
+ }
+
+ void verifyFinalState() {
+ // Wait to see if there are extra callbacks
+ std::this_thread::sleep_for(500ms);
+
+ std::lock_guard lock(mMutex);
+ EXPECT_EQ(mCallbackDataQueue.size(), 0) << "extra callbacks received";
+ mCallbackDataQueue = {};
+ }
+
+ void* getContext() { return static_cast<void*>(this); }
+
+ std::mutex mMutex;
+ std::condition_variable mConditionVariable;
+ std::queue<CallbackData> mCallbackDataQueue;
+};
+}
+} // namespace android
diff --git a/services/surfaceflinger/tests/utils/ColorUtils.h b/services/surfaceflinger/tests/utils/ColorUtils.h
new file mode 100644
index 0000000..07916b6
--- /dev/null
+++ b/services/surfaceflinger/tests/utils/ColorUtils.h
@@ -0,0 +1,86 @@
+/*
+ * 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.
+ */
+#pragma once
+
+#include <ui/ColorSpace.h>
+
+namespace android {
+
+namespace {
+
+struct Color {
+ uint8_t r;
+ uint8_t g;
+ uint8_t b;
+ uint8_t a;
+
+ static const Color RED;
+ static const Color GREEN;
+ static const Color BLUE;
+ static const Color WHITE;
+ static const Color BLACK;
+ static const Color TRANSPARENT;
+};
+
+const Color Color::RED{255, 0, 0, 255};
+const Color Color::GREEN{0, 255, 0, 255};
+const Color Color::BLUE{0, 0, 255, 255};
+const Color Color::WHITE{255, 255, 255, 255};
+const Color Color::BLACK{0, 0, 0, 255};
+const Color Color::TRANSPARENT{0, 0, 0, 0};
+
+class ColorTransformHelper {
+public:
+ static void DegammaColorSingle(half& s) {
+ if (s <= 0.03928f)
+ s = s / 12.92f;
+ else
+ s = pow((s + 0.055f) / 1.055f, 2.4f);
+ }
+
+ static void DegammaColor(half3& color) {
+ DegammaColorSingle(color.r);
+ DegammaColorSingle(color.g);
+ DegammaColorSingle(color.b);
+ }
+
+ static void GammaColorSingle(half& s) {
+ if (s <= 0.0031308f) {
+ s = s * 12.92f;
+ } else {
+ s = 1.055f * pow(s, (1.0f / 2.4f)) - 0.055f;
+ }
+ }
+
+ static void GammaColor(half3& color) {
+ GammaColorSingle(color.r);
+ GammaColorSingle(color.g);
+ GammaColorSingle(color.b);
+ }
+
+ static void applyMatrix(half3& color, const mat3& mat) {
+ half3 ret = half3(0);
+
+ for (int i = 0; i < 3; i++) {
+ for (int j = 0; j < 3; j++) {
+ ret[i] = ret[i] + color[j] * mat[j][i];
+ }
+ }
+ color = ret;
+ }
+};
+} // namespace
+} // namespace android
diff --git a/services/surfaceflinger/tests/utils/ScreenshotUtils.h b/services/surfaceflinger/tests/utils/ScreenshotUtils.h
new file mode 100644
index 0000000..02e7623
--- /dev/null
+++ b/services/surfaceflinger/tests/utils/ScreenshotUtils.h
@@ -0,0 +1,167 @@
+/*
+ * 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.
+ */
+#pragma once
+
+#include <ui/Rect.h>
+#include <utils/String8.h>
+#include "TransactionUtils.h"
+
+namespace android {
+
+namespace {
+
+// A ScreenCapture is a screenshot from SurfaceFlinger that can be used to check
+// individual pixel values for testing purposes.
+class ScreenCapture : public RefBase {
+public:
+ static void captureScreen(std::unique_ptr<ScreenCapture>* sc) {
+ captureScreen(sc, SurfaceComposerClient::getInternalDisplayToken());
+ }
+
+ static void captureScreen(std::unique_ptr<ScreenCapture>* sc, sp<IBinder> displayToken) {
+ const auto sf = ComposerService::getComposerService();
+ SurfaceComposerClient::Transaction().apply(true);
+
+ sp<GraphicBuffer> outBuffer;
+ ASSERT_EQ(NO_ERROR, sf->captureScreen(displayToken, &outBuffer, Rect(), 0, 0, false));
+ *sc = std::make_unique<ScreenCapture>(outBuffer);
+ }
+
+ static void captureLayers(std::unique_ptr<ScreenCapture>* sc, sp<IBinder>& parentHandle,
+ Rect crop = Rect::EMPTY_RECT, float frameScale = 1.0) {
+ sp<ISurfaceComposer> sf(ComposerService::getComposerService());
+ SurfaceComposerClient::Transaction().apply(true);
+
+ sp<GraphicBuffer> outBuffer;
+ ASSERT_EQ(NO_ERROR, sf->captureLayers(parentHandle, &outBuffer, crop, frameScale));
+ *sc = std::make_unique<ScreenCapture>(outBuffer);
+ }
+
+ static void captureChildLayers(std::unique_ptr<ScreenCapture>* sc, sp<IBinder>& parentHandle,
+ Rect crop = Rect::EMPTY_RECT, float frameScale = 1.0) {
+ sp<ISurfaceComposer> sf(ComposerService::getComposerService());
+ SurfaceComposerClient::Transaction().apply(true);
+
+ sp<GraphicBuffer> outBuffer;
+ ASSERT_EQ(NO_ERROR, sf->captureLayers(parentHandle, &outBuffer, crop, frameScale, true));
+ *sc = std::make_unique<ScreenCapture>(outBuffer);
+ }
+
+ static void captureChildLayersExcluding(
+ std::unique_ptr<ScreenCapture>* sc, sp<IBinder>& parentHandle,
+ std::unordered_set<sp<IBinder>, ISurfaceComposer::SpHash<IBinder>> excludeLayers) {
+ sp<ISurfaceComposer> sf(ComposerService::getComposerService());
+ SurfaceComposerClient::Transaction().apply(true);
+
+ sp<GraphicBuffer> outBuffer;
+ ASSERT_EQ(NO_ERROR,
+ sf->captureLayers(parentHandle, &outBuffer, ui::Dataspace::V0_SRGB,
+ ui::PixelFormat::RGBA_8888, Rect::EMPTY_RECT, excludeLayers,
+ 1.0f, true));
+ *sc = std::make_unique<ScreenCapture>(outBuffer);
+ }
+
+ void expectColor(const Rect& rect, const Color& color, uint8_t tolerance = 0) {
+ ASSERT_EQ(HAL_PIXEL_FORMAT_RGBA_8888, mOutBuffer->getPixelFormat());
+ expectBufferColor(mOutBuffer, mPixels, rect, color, tolerance);
+ }
+
+ void expectBorder(const Rect& rect, const Color& color, uint8_t tolerance = 0) {
+ ASSERT_EQ(HAL_PIXEL_FORMAT_RGBA_8888, mOutBuffer->getPixelFormat());
+ const bool leftBorder = rect.left > 0;
+ const bool topBorder = rect.top > 0;
+ const bool rightBorder = rect.right < int32_t(mOutBuffer->getWidth());
+ const bool bottomBorder = rect.bottom < int32_t(mOutBuffer->getHeight());
+
+ if (topBorder) {
+ Rect top(rect.left, rect.top - 1, rect.right, rect.top);
+ if (leftBorder) {
+ top.left -= 1;
+ }
+ if (rightBorder) {
+ top.right += 1;
+ }
+ expectColor(top, color, tolerance);
+ }
+ if (leftBorder) {
+ Rect left(rect.left - 1, rect.top, rect.left, rect.bottom);
+ expectColor(left, color, tolerance);
+ }
+ if (rightBorder) {
+ Rect right(rect.right, rect.top, rect.right + 1, rect.bottom);
+ expectColor(right, color, tolerance);
+ }
+ if (bottomBorder) {
+ Rect bottom(rect.left, rect.bottom, rect.right, rect.bottom + 1);
+ if (leftBorder) {
+ bottom.left -= 1;
+ }
+ if (rightBorder) {
+ bottom.right += 1;
+ }
+ expectColor(bottom, color, tolerance);
+ }
+ }
+
+ void expectQuadrant(const Rect& rect, const Color& topLeft, const Color& topRight,
+ const Color& bottomLeft, const Color& bottomRight, bool filtered = false,
+ uint8_t tolerance = 0) {
+ ASSERT_TRUE((rect.right - rect.left) % 2 == 0 && (rect.bottom - rect.top) % 2 == 0);
+
+ const int32_t centerX = rect.left + (rect.right - rect.left) / 2;
+ const int32_t centerY = rect.top + (rect.bottom - rect.top) / 2;
+ // avoid checking borders due to unspecified filtering behavior
+ const int32_t offsetX = filtered ? 2 : 0;
+ const int32_t offsetY = filtered ? 2 : 0;
+ expectColor(Rect(rect.left, rect.top, centerX - offsetX, centerY - offsetY), topLeft,
+ tolerance);
+ expectColor(Rect(centerX + offsetX, rect.top, rect.right, centerY - offsetY), topRight,
+ tolerance);
+ expectColor(Rect(rect.left, centerY + offsetY, centerX - offsetX, rect.bottom), bottomLeft,
+ tolerance);
+ expectColor(Rect(centerX + offsetX, centerY + offsetY, rect.right, rect.bottom),
+ bottomRight, tolerance);
+ }
+
+ void checkPixel(uint32_t x, uint32_t y, uint8_t r, uint8_t g, uint8_t b) {
+ ASSERT_EQ(HAL_PIXEL_FORMAT_RGBA_8888, mOutBuffer->getPixelFormat());
+ const uint8_t* pixel = mPixels + (4 * (y * mOutBuffer->getStride() + x));
+ if (r != pixel[0] || g != pixel[1] || b != pixel[2]) {
+ String8 err(String8::format("pixel @ (%3d, %3d): "
+ "expected [%3d, %3d, %3d], got [%3d, %3d, %3d]",
+ x, y, r, g, b, pixel[0], pixel[1], pixel[2]));
+ EXPECT_EQ(String8(), err) << err.string();
+ }
+ }
+
+ void expectFGColor(uint32_t x, uint32_t y) { checkPixel(x, y, 195, 63, 63); }
+
+ void expectBGColor(uint32_t x, uint32_t y) { checkPixel(x, y, 63, 63, 195); }
+
+ void expectChildColor(uint32_t x, uint32_t y) { checkPixel(x, y, 200, 200, 200); }
+
+ explicit ScreenCapture(const sp<GraphicBuffer>& outBuffer) : mOutBuffer(outBuffer) {
+ mOutBuffer->lock(GRALLOC_USAGE_SW_READ_OFTEN, reinterpret_cast<void**>(&mPixels));
+ }
+
+ ~ScreenCapture() { mOutBuffer->unlock(); }
+
+private:
+ sp<GraphicBuffer> mOutBuffer;
+ uint8_t* mPixels = nullptr;
+};
+} // namespace
+} // namespace android
diff --git a/services/surfaceflinger/tests/utils/TransactionUtils.h b/services/surfaceflinger/tests/utils/TransactionUtils.h
new file mode 100644
index 0000000..f6b33a9
--- /dev/null
+++ b/services/surfaceflinger/tests/utils/TransactionUtils.h
@@ -0,0 +1,188 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+//#include <algorithm>
+#include <chrono>
+//#include <cinttypes>
+//#include <functional>
+//#include <limits>
+//#include <ostream>
+//#include <thread>
+#include <gtest/gtest.h>
+
+#include <android/native_window.h>
+#include <hardware/hwcomposer_defs.h>
+
+#include <binder/IPCThreadState.h>
+
+#include <gui/Surface.h>
+#include <gui/SurfaceComposerClient.h>
+
+#include <private/gui/ComposerService.h>
+
+#include <ui/GraphicBuffer.h>
+#include <ui/Rect.h>
+
+#include "ColorUtils.h"
+//#include <sys/types.h>
+//#include <unistd.h>
+
+namespace android {
+
+namespace {
+
+using namespace std::chrono_literals;
+
+std::ostream& operator<<(std::ostream& os, const Color& color) {
+ os << int(color.r) << ", " << int(color.g) << ", " << int(color.b) << ", " << int(color.a);
+ return os;
+}
+
+// Fill a region with the specified color.
+void fillANativeWindowBufferColor(const ANativeWindow_Buffer& buffer, const Rect& rect,
+ const Color& color) {
+ Rect r(0, 0, buffer.width, buffer.height);
+ if (!r.intersect(rect, &r)) {
+ return;
+ }
+
+ int32_t width = r.right - r.left;
+ int32_t height = r.bottom - r.top;
+
+ for (int32_t row = 0; row < height; row++) {
+ uint8_t* dst =
+ static_cast<uint8_t*>(buffer.bits) + (buffer.stride * (r.top + row) + r.left) * 4;
+ for (int32_t column = 0; column < width; column++) {
+ dst[0] = color.r;
+ dst[1] = color.g;
+ dst[2] = color.b;
+ dst[3] = color.a;
+ dst += 4;
+ }
+ }
+}
+
+// Fill a region with the specified color.
+void fillGraphicBufferColor(const sp<GraphicBuffer>& buffer, const Rect& rect, const Color& color) {
+ Rect r(0, 0, buffer->width, buffer->height);
+ if (!r.intersect(rect, &r)) {
+ return;
+ }
+
+ int32_t width = r.right - r.left;
+ int32_t height = r.bottom - r.top;
+
+ uint8_t* pixels;
+ buffer->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
+ reinterpret_cast<void**>(&pixels));
+
+ for (int32_t row = 0; row < height; row++) {
+ uint8_t* dst = pixels + (buffer->getStride() * (r.top + row) + r.left) * 4;
+ for (int32_t column = 0; column < width; column++) {
+ dst[0] = color.r;
+ dst[1] = color.g;
+ dst[2] = color.b;
+ dst[3] = color.a;
+ dst += 4;
+ }
+ }
+ buffer->unlock();
+}
+
+// Check if a region has the specified color.
+void expectBufferColor(const sp<GraphicBuffer>& outBuffer, uint8_t* pixels, const Rect& rect,
+ const Color& color, uint8_t tolerance) {
+ int32_t x = rect.left;
+ int32_t y = rect.top;
+ int32_t width = rect.right - rect.left;
+ int32_t height = rect.bottom - rect.top;
+
+ int32_t bufferWidth = int32_t(outBuffer->getWidth());
+ int32_t bufferHeight = int32_t(outBuffer->getHeight());
+ if (x + width > bufferWidth) {
+ x = std::min(x, bufferWidth);
+ width = bufferWidth - x;
+ }
+ if (y + height > bufferHeight) {
+ y = std::min(y, bufferHeight);
+ height = bufferHeight - y;
+ }
+
+ auto colorCompare = [tolerance](uint8_t a, uint8_t b) {
+ uint8_t tmp = a >= b ? a - b : b - a;
+ return tmp <= tolerance;
+ };
+ for (int32_t j = 0; j < height; j++) {
+ const uint8_t* src = pixels + (outBuffer->getStride() * (y + j) + x) * 4;
+ for (int32_t i = 0; i < width; i++) {
+ const uint8_t expected[4] = {color.r, color.g, color.b, color.a};
+ EXPECT_TRUE(std::equal(src, src + 4, expected, colorCompare))
+ << "pixel @ (" << x + i << ", " << y + j << "): "
+ << "expected (" << color << "), "
+ << "got (" << Color{src[0], src[1], src[2], src[3]} << ")";
+ src += 4;
+ }
+ }
+}
+
+using Transaction = SurfaceComposerClient::Transaction;
+
+// Fill an RGBA_8888 formatted surface with a single color.
+static void fillSurfaceRGBA8(const sp<SurfaceControl>& sc, uint8_t r, uint8_t g, uint8_t b,
+ bool unlock = true) {
+ ANativeWindow_Buffer outBuffer;
+ sp<Surface> s = sc->getSurface();
+ ASSERT_TRUE(s != nullptr);
+ ASSERT_EQ(NO_ERROR, s->lock(&outBuffer, nullptr));
+ uint8_t* img = reinterpret_cast<uint8_t*>(outBuffer.bits);
+ for (int y = 0; y < outBuffer.height; y++) {
+ for (int x = 0; x < outBuffer.width; x++) {
+ uint8_t* pixel = img + (4 * (y * outBuffer.stride + x));
+ pixel[0] = r;
+ pixel[1] = g;
+ pixel[2] = b;
+ pixel[3] = 255;
+ }
+ }
+ if (unlock) {
+ ASSERT_EQ(NO_ERROR, s->unlockAndPost());
+ }
+}
+
+enum class RenderPath { SCREENSHOT, VIRTUAL_DISPLAY };
+
+// Environment for starting up binder threads. This is required for testing
+// virtual displays, as BufferQueue parameters may be queried over binder.
+class BinderEnvironment : public ::testing::Environment {
+public:
+ void SetUp() override { ProcessState::self()->startThreadPool(); }
+};
+
+/** RAII Wrapper around get/seteuid */
+class UIDFaker {
+ uid_t oldId;
+
+public:
+ UIDFaker(uid_t uid) {
+ oldId = geteuid();
+ seteuid(uid);
+ }
+ ~UIDFaker() { seteuid(oldId); }
+};
+} // namespace
+} // namespace android