Merge "Add non platform services to telephony bugreport" into pi-dev
diff --git a/cmds/atrace/atrace.rc b/cmds/atrace/atrace.rc
index a9c5c82..d3d0711 100644
--- a/cmds/atrace/atrace.rc
+++ b/cmds/atrace/atrace.rc
@@ -83,6 +83,11 @@
chmod 0666 /sys/kernel/tracing/events/i2c/smbus_reply/enable
chmod 0666 /sys/kernel/debug/tracing/events/lowmemorykiller/enable
chmod 0666 /sys/kernel/tracing/events/lowmemorykiller/enable
+ chmod 0666 /sys/kernel/debug/tracing/events/sync/enable
+ chmod 0666 /sys/kernel/tracing/events/sync/enable
+ chmod 0666 /sys/kernel/debug/tracing/events/fence/enable
+ chmod 0666 /sys/kernel/tracing/events/fence/enable
+
# disk
chmod 0666 /sys/kernel/tracing/events/f2fs/f2fs_sync_file_enter/enable
diff --git a/cmds/atrace/atrace_userdebug.rc b/cmds/atrace/atrace_userdebug.rc
index 93a29ba..f4e5b98 100644
--- a/cmds/atrace/atrace_userdebug.rc
+++ b/cmds/atrace/atrace_userdebug.rc
@@ -5,8 +5,6 @@
# Access control to these files is now entirely in selinux policy.
on post-fs
- chmod 0666 /sys/kernel/tracing/events/sync/enable
- chmod 0666 /sys/kernel/debug/tracing/events/sync/enable
chmod 0666 /sys/kernel/tracing/events/workqueue/enable
chmod 0666 /sys/kernel/debug/tracing/events/workqueue/enable
chmod 0666 /sys/kernel/tracing/events/regulator/enable
diff --git a/opengl/libs/EGL/getProcAddress.cpp b/opengl/libs/EGL/getProcAddress.cpp
index 0183621..fedc789 100644
--- a/opengl/libs/EGL/getProcAddress.cpp
+++ b/opengl/libs/EGL/getProcAddress.cpp
@@ -80,46 +80,44 @@
#elif defined(__i386__)
- #define API_ENTRY(_api) __attribute__((noinline,optimize("omit-frame-pointer"))) _api
+ #define API_ENTRY(_api) __attribute__((naked)) _api
#define CALL_GL_EXTENSION_API(_api) \
- register void** fn; \
__asm__ volatile( \
- "mov %%gs:0, %[fn]\n" \
- "mov %P[tls](%[fn]), %[fn]\n" \
- "test %[fn], %[fn]\n" \
- "cmovne %P[api](%[fn]), %[fn]\n" \
- "test %[fn], %[fn]\n" \
+ "mov %%gs:0, %%eax\n" \
+ "mov %P[tls](%%eax), %%eax\n" \
+ "test %%eax, %%eax\n" \
+ "cmovne %P[api](%%eax), %%eax\n" \
+ "test %%eax, %%eax\n" \
"je 1f\n" \
- "jmp *%[fn]\n" \
- "1:\n" \
- : [fn] "=r" (fn) \
+ "jmp *%%eax\n" \
+ "1: ret\n" \
+ : \
: [tls] "i" (TLS_SLOT_OPENGL_API*sizeof(void*)), \
[api] "i" (__builtin_offsetof(gl_hooks_t, \
ext.extensions[_api])) \
- : "cc" \
+ : "eax", "cc" \
);
#elif defined(__x86_64__)
- #define API_ENTRY(_api) __attribute__((noinline,optimize("omit-frame-pointer"))) _api
+ #define API_ENTRY(_api) __attribute__((naked)) _api
#define CALL_GL_EXTENSION_API(_api) \
- register void** fn; \
__asm__ volatile( \
- "mov %%fs:0, %[fn]\n" \
- "mov %P[tls](%[fn]), %[fn]\n" \
- "test %[fn], %[fn]\n" \
- "cmovne %P[api](%[fn]), %[fn]\n" \
- "test %[fn], %[fn]\n" \
+ "mov %%fs:0, %%rax\n" \
+ "mov %P[tls](%%rax), %%rax\n" \
+ "test %%rax, %%rax\n" \
+ "cmovne %P[api](%%rax), %%rax\n" \
+ "test %%rax, %%rax\n" \
"je 1f\n" \
- "jmp *%[fn]\n" \
- "1:\n" \
- : [fn] "=r" (fn) \
+ "jmp *%%rax\n" \
+ "1: ret\n" \
+ : \
: [tls] "i" (TLS_SLOT_OPENGL_API*sizeof(void*)), \
[api] "i" (__builtin_offsetof(gl_hooks_t, \
ext.extensions[_api])) \
- : "cc" \
+ : "rax", "cc" \
);
#elif defined(__mips64)
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp
index 3947318..61758b6 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp
@@ -30,8 +30,9 @@
#include <android/configuration.h>
-#include <algorithm>
#include <inttypes.h>
+#include <algorithm>
+#include <iterator>
#include <set>
using android::Fence;
@@ -338,6 +339,31 @@
return Error::None;
}
+Error Display::getActiveConfigIndex(int* outIndex) const {
+ ALOGV("[%" PRIu64 "] getActiveConfigIndex", mId);
+ hwc2_config_t configId = 0;
+ auto intError = mComposer.getActiveConfig(mId, &configId);
+ auto error = static_cast<Error>(intError);
+
+ if (error != Error::None) {
+ ALOGE("Unable to get active config for mId:[%" PRIu64 "]", mId);
+ *outIndex = -1;
+ return error;
+ }
+
+ auto pos = mConfigs.find(configId);
+ if (pos != mConfigs.end()) {
+ *outIndex = std::distance(mConfigs.begin(), pos);
+ } else {
+ ALOGE("[%" PRIu64 "] getActiveConfig returned unknown config %u", mId, configId);
+ // Return no error, but the caller needs to check for a negative index
+ // to detect this case
+ *outIndex = -1;
+ }
+
+ return Error::None;
+}
+
Error Display::getChangedCompositionTypes(
std::unordered_map<Layer*, Composition>* outTypes)
{
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.h b/services/surfaceflinger/DisplayHardware/HWC2.h
index 3ac06ec..29d7a47 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.h
+++ b/services/surfaceflinger/DisplayHardware/HWC2.h
@@ -36,7 +36,6 @@
#include <unordered_map>
#include <unordered_set>
#include <vector>
-#include <map>
namespace android {
class Fence;
@@ -207,6 +206,7 @@
[[clang::warn_unused_result]] Error destroyLayer(Layer* layer);
[[clang::warn_unused_result]] Error getActiveConfig(
std::shared_ptr<const Config>* outConfig) const;
+ [[clang::warn_unused_result]] Error getActiveConfigIndex(int* outIndex) const;
[[clang::warn_unused_result]] Error getChangedCompositionTypes(
std::unordered_map<Layer*, Composition>* outTypes);
[[clang::warn_unused_result]] Error getColorModes(
@@ -288,9 +288,7 @@
bool mIsConnected;
DisplayType mType;
std::unordered_map<hwc2_layer_t, std::unique_ptr<Layer>> mLayers;
- // The ordering in this map matters, for getConfigs(), when it is
- // converted to a vector
- std::map<hwc2_config_t, std::shared_ptr<const Config>> mConfigs;
+ std::unordered_map<hwc2_config_t, std::shared_ptr<const Config>> mConfigs;
};
// Convenience C++ class to access hwc2_device_t Layer functions directly.
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index 96d691c..f5f7a82 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -318,6 +318,28 @@
return config;
}
+int HWComposer::getActiveConfigIndex(int32_t displayId) const {
+ if (!isValidDisplay(displayId)) {
+ ALOGV("getActiveConfigIndex: Attempted to access invalid display %d", displayId);
+ return -1;
+ }
+ int index;
+ auto error = mDisplayData[displayId].hwcDisplay->getActiveConfigIndex(&index);
+ if (error == HWC2::Error::BadConfig) {
+ ALOGE("getActiveConfigIndex: No config active, returning -1");
+ return -1;
+ } else if (error != HWC2::Error::None) {
+ ALOGE("getActiveConfigIndex failed for display %d: %s (%d)", displayId,
+ to_string(error).c_str(), static_cast<int32_t>(error));
+ return -1;
+ } else if (index < 0) {
+ ALOGE("getActiveConfigIndex returned an unknown config for display %d", displayId);
+ return -1;
+ }
+
+ return index;
+}
+
std::vector<ui::ColorMode> HWComposer::getColorModes(int32_t displayId) const {
RETURN_IF_INVALID_DISPLAY(displayId, {});
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
index d7f3b08..f968948 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -164,6 +164,7 @@
std::shared_ptr<const HWC2::Display::Config>
getActiveConfig(int32_t displayId) const;
+ int getActiveConfigIndex(int32_t displayId) const;
std::vector<ui::ColorMode> getColorModes(int32_t displayId) const;
diff --git a/services/surfaceflinger/EventControlThread.h b/services/surfaceflinger/EventControlThread.h
index 9be4e7c..cafae53 100644
--- a/services/surfaceflinger/EventControlThread.h
+++ b/services/surfaceflinger/EventControlThread.h
@@ -26,8 +26,6 @@
namespace android {
-class SurfaceFlinger;
-
class EventControlThread {
public:
virtual ~EventControlThread();
diff --git a/services/surfaceflinger/EventThread.cpp b/services/surfaceflinger/EventThread.cpp
index bb9c070..bc271c8 100644
--- a/services/surfaceflinger/EventThread.cpp
+++ b/services/surfaceflinger/EventThread.cpp
@@ -26,14 +26,12 @@
#include <cutils/sched_policy.h>
#include <gui/DisplayEventReceiver.h>
-#include <gui/IDisplayEventConnection.h>
#include <utils/Errors.h>
#include <utils/String8.h>
#include <utils/Trace.h>
#include "EventThread.h"
-#include "SurfaceFlinger.h"
using namespace std::chrono_literals;
@@ -47,9 +45,11 @@
namespace impl {
-EventThread::EventThread(VSyncSource* src, SurfaceFlinger& flinger, bool interceptVSyncs,
- const char* threadName)
- : mVSyncSource(src), mFlinger(flinger), mInterceptVSyncs(interceptVSyncs) {
+EventThread::EventThread(VSyncSource* src, ResyncWithRateLimitCallback resyncWithRateLimitCallback,
+ InterceptVSyncsCallback interceptVSyncsCallback, const char* threadName)
+ : mVSyncSource(src),
+ mResyncWithRateLimitCallback(resyncWithRateLimitCallback),
+ mInterceptVSyncsCallback(interceptVSyncsCallback) {
for (auto& event : mVSyncEvent) {
event.header.type = DisplayEventReceiver::DISPLAY_EVENT_VSYNC;
event.header.id = 0;
@@ -118,7 +118,9 @@
void EventThread::requestNextVsync(const sp<EventThread::Connection>& connection) {
std::lock_guard<std::mutex> lock(mMutex);
- mFlinger.resyncWithRateLimit();
+ if (mResyncWithRateLimitCallback) {
+ mResyncWithRateLimitCallback();
+ }
if (connection->count < 0) {
connection->count = 0;
@@ -216,8 +218,8 @@
timestamp = mVSyncEvent[i].header.timestamp;
if (timestamp) {
// we have a vsync event to dispatch
- if (mInterceptVSyncs) {
- mFlinger.mInterceptor->saveVSyncEvent(timestamp);
+ if (mInterceptVSyncsCallback) {
+ mInterceptVSyncsCallback(timestamp);
}
*event = mVSyncEvent[i];
mVSyncEvent[i].header.timestamp = 0;
diff --git a/services/surfaceflinger/EventThread.h b/services/surfaceflinger/EventThread.h
index 97f0a35..9c13ed2 100644
--- a/services/surfaceflinger/EventThread.h
+++ b/services/surfaceflinger/EventThread.h
@@ -37,6 +37,7 @@
namespace android {
// ---------------------------------------------------------------------------
+class EventThreadTest;
class SurfaceFlinger;
class String8;
@@ -82,7 +83,9 @@
class Connection : public BnDisplayEventConnection {
public:
explicit Connection(EventThread* eventThread);
- status_t postEvent(const DisplayEventReceiver::Event& event);
+ virtual ~Connection();
+
+ virtual status_t postEvent(const DisplayEventReceiver::Event& event);
// count >= 1 : continuous event. count is the vsync rate
// count == 0 : one-shot event that has not fired
@@ -90,7 +93,6 @@
int32_t count;
private:
- virtual ~Connection();
virtual void onFirstRef();
status_t stealReceiveChannel(gui::BitTube* outChannel) override;
status_t setVsyncRate(uint32_t count) override;
@@ -100,8 +102,11 @@
};
public:
- EventThread(VSyncSource* src, SurfaceFlinger& flinger, bool interceptVSyncs,
- const char* threadName);
+ using ResyncWithRateLimitCallback = std::function<void()>;
+ using InterceptVSyncsCallback = std::function<void(nsecs_t)>;
+
+ EventThread(VSyncSource* src, ResyncWithRateLimitCallback resyncWithRateLimitCallback,
+ InterceptVSyncsCallback interceptVSyncsCallback, const char* threadName);
~EventThread();
sp<BnDisplayEventConnection> createEventConnection() const override;
@@ -124,6 +129,8 @@
void setPhaseOffset(nsecs_t phaseOffset) override;
private:
+ friend EventThreadTest;
+
void threadMain();
Vector<sp<EventThread::Connection>> waitForEventLocked(std::unique_lock<std::mutex>* lock,
DisplayEventReceiver::Event* event)
@@ -137,8 +144,9 @@
void onVSyncEvent(nsecs_t timestamp) override;
// constants
- VSyncSource* mVSyncSource GUARDED_BY(mMutex) = nullptr;
- SurfaceFlinger& mFlinger;
+ VSyncSource* const mVSyncSource GUARDED_BY(mMutex) = nullptr;
+ const ResyncWithRateLimitCallback mResyncWithRateLimitCallback;
+ const InterceptVSyncsCallback mInterceptVSyncsCallback;
std::thread mThread;
mutable std::mutex mMutex;
@@ -155,8 +163,6 @@
// for debugging
bool mDebugVsyncEnabled GUARDED_BY(mMutex) = false;
-
- const bool mInterceptVSyncs = false;
};
// ---------------------------------------------------------------------------
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 9043234..2077598 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -1337,10 +1337,6 @@
return true;
}
-ui::Dataspace Layer::getDataSpace() const {
- return mCurrentState.dataSpace;
-}
-
uint32_t Layer::getLayerStack() const {
auto p = mDrawingParent.promote();
if (p == nullptr) {
@@ -1433,7 +1429,7 @@
info.mColor = ds.color;
info.mFlags = ds.flags;
info.mPixelFormat = getPixelFormat();
- info.mDataSpace = static_cast<android_dataspace>(getDataSpace());
+ info.mDataSpace = static_cast<android_dataspace>(ds.dataSpace);
info.mMatrix[0][0] = ds.active.transform[0][0];
info.mMatrix[0][1] = ds.active.transform[0][1];
info.mMatrix[1][0] = ds.active.transform[1][0];
@@ -1960,7 +1956,7 @@
layerInfo->set_is_opaque(isOpaque(state));
layerInfo->set_invalidate(contentDirty);
- layerInfo->set_dataspace(dataspaceDetails(static_cast<android_dataspace>(getDataSpace())));
+ layerInfo->set_dataspace(dataspaceDetails(static_cast<android_dataspace>(state.dataSpace)));
layerInfo->set_pixel_format(decodePixelFormat(getPixelFormat()));
LayerProtoHelper::writeToProto(getColor(), layerInfo->mutable_color());
LayerProtoHelper::writeToProto(state.color, layerInfo->mutable_requested_color());
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 632efbe..91eb15a 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -288,7 +288,6 @@
bool setFlags(uint8_t flags, uint8_t mask);
bool setLayerStack(uint32_t layerStack);
bool setDataSpace(ui::Dataspace dataSpace);
- ui::Dataspace getDataSpace() const;
uint32_t getLayerStack() const;
void deferTransactionUntil(const sp<IBinder>& barrierHandle, uint64_t frameNumber);
void deferTransactionUntil(const sp<Layer>& barrierLayer, uint64_t frameNumber);
diff --git a/services/surfaceflinger/RenderEngine/ProgramCache.cpp b/services/surfaceflinger/RenderEngine/ProgramCache.cpp
index 7c2cb5b..2808a1a 100644
--- a/services/surfaceflinger/RenderEngine/ProgramCache.cpp
+++ b/services/surfaceflinger/RenderEngine/ProgramCache.cpp
@@ -422,13 +422,13 @@
case Key::OUTPUT_TF_ST2084:
fs << R"__SHADER__(
vec3 OETF(const vec3 linear) {
- const float m1 = (2610.0 / 4096.0) / 4.0;
- const float m2 = (2523.0 / 4096.0) * 128.0;
- const float c1 = (3424.0 / 4096.0);
- const float c2 = (2413.0 / 4096.0) * 32.0;
- const float c3 = (2392.0 / 4096.0) * 32.0;
+ const highp float m1 = (2610.0 / 4096.0) / 4.0;
+ const highp float m2 = (2523.0 / 4096.0) * 128.0;
+ const highp float c1 = (3424.0 / 4096.0);
+ const highp float c2 = (2413.0 / 4096.0) * 32.0;
+ const highp float c3 = (2392.0 / 4096.0) * 32.0;
- vec3 tmp = pow(linear, vec3(m1));
+ highp vec3 tmp = pow(linear, vec3(m1));
tmp = (c1 + c2 * tmp) / (1.0 + c3 * tmp);
return pow(tmp, vec3(m2));
}
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 84893a7..9117207 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -635,14 +635,21 @@
mEventThreadSource =
std::make_unique<DispSyncSource>(&mPrimaryDispSync, SurfaceFlinger::vsyncPhaseOffsetNs,
true, "app");
- mEventThread = std::make_unique<impl::EventThread>(mEventThreadSource.get(), *this, false,
+ mEventThread = std::make_unique<impl::EventThread>(mEventThreadSource.get(),
+ [this]() { resyncWithRateLimit(); },
+ impl::EventThread::InterceptVSyncsCallback(),
"appEventThread");
mSfEventThreadSource =
std::make_unique<DispSyncSource>(&mPrimaryDispSync,
SurfaceFlinger::sfVsyncPhaseOffsetNs, true, "sf");
- mSFEventThread = std::make_unique<impl::EventThread>(mSfEventThreadSource.get(), *this, true,
- "sfEventThread");
+ mSFEventThread =
+ std::make_unique<impl::EventThread>(mSfEventThreadSource.get(),
+ [this]() { resyncWithRateLimit(); },
+ [this](nsecs_t timestamp) {
+ mInterceptor->saveVSyncEvent(timestamp);
+ },
+ "sfEventThread");
mEventQueue->setEventThread(mSFEventThread.get());
mVsyncModulator.setEventThread(mSFEventThread.get());
@@ -1131,9 +1138,11 @@
ALOGV("VSync Injections enabled");
if (mVSyncInjector.get() == nullptr) {
mVSyncInjector = std::make_unique<InjectVSyncSource>();
- mInjectorEventThread =
- std::make_unique<impl::EventThread>(mVSyncInjector.get(), *this, false,
- "injEventThread");
+ mInjectorEventThread = std::make_unique<
+ impl::EventThread>(mVSyncInjector.get(),
+ [this]() { resyncWithRateLimit(); },
+ impl::EventThread::InterceptVSyncsCallback(),
+ "injEventThread");
}
mEventQueue->setEventThread(mInjectorEventThread.get());
} else {
@@ -1870,7 +1879,7 @@
*outHdrDataSpace = Dataspace::UNKNOWN;
for (const auto& layer : displayDevice->getVisibleLayersSortedByZ()) {
- switch (layer->getDataSpace()) {
+ switch (layer->getDrawingState().dataSpace) {
case Dataspace::V0_SCRGB:
case Dataspace::V0_SCRGB_LINEAR:
bestDataSpace = Dataspace::V0_SCRGB_LINEAR;
@@ -2089,13 +2098,13 @@
"display %zd: %d", displayId, result);
}
for (auto& layer : displayDevice->getVisibleLayersSortedByZ()) {
- if ((layer->getDataSpace() == Dataspace::BT2020_PQ ||
- layer->getDataSpace() == Dataspace::BT2020_ITU_PQ) &&
+ if ((layer->getDrawingState().dataSpace == Dataspace::BT2020_PQ ||
+ layer->getDrawingState().dataSpace == Dataspace::BT2020_ITU_PQ) &&
!displayDevice->hasHDR10Support()) {
layer->forceClientComposition(hwcId);
}
- if ((layer->getDataSpace() == Dataspace::BT2020_HLG ||
- layer->getDataSpace() == Dataspace::BT2020_ITU_HLG) &&
+ if ((layer->getDrawingState().dataSpace == Dataspace::BT2020_HLG ||
+ layer->getDrawingState().dataSpace == Dataspace::BT2020_ITU_HLG) &&
!displayDevice->hasHLGSupport()) {
layer->forceClientComposition(hwcId);
}
@@ -2405,6 +2414,9 @@
}
setActiveColorModeInternal(hw, defaultColorMode, defaultDataSpace,
RenderIntent::COLORIMETRIC);
+ if (state.type < DisplayDevice::DISPLAY_VIRTUAL) {
+ hw->setActiveConfig(getHwComposer().getActiveConfigIndex(state.type));
+ }
hw->setLayerStack(state.layerStack);
hw->setProjection(state.orientation, state.viewport, state.frame);
hw->setDisplayName(state.displayName);
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index bcabe0d..39761dd 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -20,6 +20,8 @@
srcs: [
":libsurfaceflinger_sources",
"DisplayTransactionTest.cpp",
+ "EventControlThreadTest.cpp",
+ "EventThreadTest.cpp",
"mock/DisplayHardware/MockComposer.cpp",
"mock/DisplayHardware/MockDisplaySurface.cpp",
"mock/gui/MockGraphicBufferConsumer.cpp",
diff --git a/services/surfaceflinger/tests/unittests/AsyncCallRecorder.h b/services/surfaceflinger/tests/unittests/AsyncCallRecorder.h
new file mode 100644
index 0000000..2245ee1
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/AsyncCallRecorder.h
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <chrono>
+#include <deque>
+#include <mutex>
+#include <optional>
+#include <thread>
+#include <tuple>
+#include <type_traits>
+#include <utility>
+
+#include <android-base/thread_annotations.h>
+
+namespace android {
+
+// This class helps record calls made by another thread when they are made
+// asynchronously, with no other way for the tests to verify that the calls have
+// been made.
+//
+// A normal Google Mock recorder, while thread safe, does not allow you to wait
+// for asynchronous calls to be made.
+//
+// Usage:
+//
+// In the test, use a Google Mock expectation to invoke an instance of the
+// recorder:
+//
+// AsyncCallRecorder<void(int)> recorder;
+//
+// EXPECT_CALL(someMock, someFunction(_)).
+// .WillRepeatedly(Invoke(recorder.getInvocable()));
+//
+// Then you can invoke the functionality being tested:
+//
+// threadUnderTest.doSomethingAsync()
+//
+// And afterwards make a number of assertions using the recorder:
+//
+// // Wait for one call (with reasonable default timeout), and get the args
+// // as a std::tuple inside a std::optional.
+// auto args = recorder.waitForCall();
+// // The returned std::optional will have a value if the recorder function
+// // was called.
+// ASSERT_TRUE(args.has_value());
+// // The arguments can be checked if needed using standard tuple
+// // operations.
+// EXPECT_EQ(123, std::get<0>(args.value()));
+//
+// Alternatively maybe you want to assert that a call was not made.
+//
+// EXPECT_FALSE(recorder.waitForUnexpectedCall().has_value());
+//
+// However this check uses a really short timeout so as not to block the test
+// unnecessarily. And it could be possible for the check to return false and
+// then the recorder could observe a call being made after.
+template <typename Func>
+class AsyncCallRecorder;
+
+template <typename... Args>
+class AsyncCallRecorder<void (*)(Args...)> {
+public:
+ // For the tests, we expect the wait for an expected change to be signaled
+ // to be much shorter than this.
+ static constexpr std::chrono::milliseconds DEFAULT_CALL_EXPECTED_TIMEOUT{10};
+
+ // The wait here is tricky. We don't expect a change, but we don't want to
+ // wait forever (or for longer than the typical test function runtime). As
+ // even the simplest Google Test can take 1ms (1000us) to run, we wait for
+ // half that time.
+ static constexpr std::chrono::microseconds UNEXPECTED_CALL_TIMEOUT{500};
+
+ using ArgTuple = std::tuple<std::remove_cv_t<std::remove_reference_t<Args>>...>;
+
+ void recordCall(Args... args) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ mCalls.emplace_back(std::make_tuple(args...));
+ mCondition.notify_all();
+ }
+
+ // Returns a functor which can be used with the Google Mock Invoke()
+ // function, or as a std::function to record calls.
+ auto getInvocable() {
+ return [this](Args... args) { recordCall(args...); };
+ }
+
+ // Returns a set of arguments as a std::optional<std::tuple<...>> for the
+ // oldest call, waiting for the given timeout if necessary if there are no
+ // arguments in the FIFO.
+ std::optional<ArgTuple> waitForCall(
+ std::chrono::microseconds timeout = DEFAULT_CALL_EXPECTED_TIMEOUT)
+ NO_THREAD_SAFETY_ANALYSIS {
+ std::unique_lock<std::mutex> lock(mMutex);
+
+ // Wait if necessary for us to have a record from a call.
+ mCondition.wait_for(lock, timeout,
+ [this]() NO_THREAD_SAFETY_ANALYSIS { return !mCalls.empty(); });
+
+ // Return the arguments from the oldest call, if one was made
+ bool called = !mCalls.empty();
+ std::optional<ArgTuple> result;
+ if (called) {
+ result.emplace(std::move(mCalls.front()));
+ mCalls.pop_front();
+ }
+ return result;
+ }
+
+ // Waits using a small default timeout for when a call is not expected to be
+ // made. The returned std::optional<std:tuple<...>> should not have a value
+ // except if a set of arguments was unexpectedly received because a call was
+ // actually made.
+ //
+ // Note this function uses a small timeout to not block test execution, and
+ // it is possible the code under test could make the call AFTER the timeout
+ // expires.
+ std::optional<ArgTuple> waitForUnexpectedCall() { return waitForCall(UNEXPECTED_CALL_TIMEOUT); }
+
+private:
+ std::mutex mMutex;
+ std::condition_variable mCondition;
+ std::deque<ArgTuple> mCalls GUARDED_BY(mMutex);
+};
+
+// Like AsyncCallRecorder, but for when the function being invoked
+// asynchronously is expected to return a value.
+//
+// This helper allows a single constant return value to be set to be returned by
+// all calls that were made.
+template <typename Func>
+class AsyncCallRecorderWithCannedReturn;
+
+template <typename Ret, typename... Args>
+class AsyncCallRecorderWithCannedReturn<Ret (*)(Args...)>
+ : public AsyncCallRecorder<void (*)(Args...)> {
+public:
+ explicit AsyncCallRecorderWithCannedReturn(Ret returnvalue) : mReturnValue(returnvalue) {}
+
+ auto getInvocable() {
+ return [this](Args... args) {
+ this->recordCall(args...);
+ return mReturnValue;
+ };
+ }
+
+private:
+ const Ret mReturnValue;
+};
+
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
index 08da1a2..cd69128 100644
--- a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
@@ -45,12 +45,13 @@
using testing::Return;
using testing::SetArgPointee;
-using android::hardware::graphics::common::V1_0::Hdr;
-using android::hardware::graphics::common::V1_1::ColorMode;
-using android::hardware::graphics::common::V1_1::RenderIntent;
+using android::Hwc2::ColorMode;
using android::Hwc2::Error;
+using android::Hwc2::Hdr;
using android::Hwc2::IComposer;
using android::Hwc2::IComposerClient;
+using android::Hwc2::PerFrameMetadataKey;
+using android::Hwc2::RenderIntent;
using FakeDisplayDeviceInjector = TestableSurfaceFlinger::FakeDisplayDeviceInjector;
using FakeHwcDisplayInjector = TestableSurfaceFlinger::FakeHwcDisplayInjector;
@@ -61,6 +62,8 @@
constexpr int32_t DEFAULT_DPI = 320;
constexpr int DEFAULT_VIRTUAL_DISPLAY_SURFACE_FORMAT = HAL_PIXEL_FORMAT_RGB_565;
+constexpr int HWC_POWER_MODE_LEET = 1337; // An out of range power mode value
+
/* ------------------------------------------------------------------------
* Boolean avoidance
*
@@ -303,11 +306,7 @@
static constexpr HWC2::DisplayType HWC_DISPLAY_TYPE = hwcDisplayType;
// The HWC active configuration id
- // TODO(b/69807179): SurfaceFlinger does not correctly get the active
- // config. Once it does, change this to non-zero so that it is properly
- // covered.
- // static constexpr int HWC_ACTIVE_CONFIG_ID = 2001;
- static constexpr int HWC_ACTIVE_CONFIG_ID = 0;
+ static constexpr int HWC_ACTIVE_CONFIG_ID = 2001;
static void injectPendingHotplugEvent(DisplayTransactionTest* test,
HWC2::Connection connection) {
@@ -359,13 +358,11 @@
// Called by tests to set up HWC call expectations
static void setupHwcGetActiveConfigCallExpectations(DisplayTransactionTest* test) {
EXPECT_CALL(*test->mComposer, getActiveConfig(HWC_DISPLAY_ID, _))
- .WillOnce(DoAll(SetArgPointee<1>(HWC_ACTIVE_CONFIG_ID), Return(Error::NONE)));
+ .WillRepeatedly(DoAll(SetArgPointee<1>(HWC_ACTIVE_CONFIG_ID), Return(Error::NONE)));
}
};
struct NonHwcDisplayVariant {
- static constexpr int HWC_ACTIVE_CONFIG_ID = 0;
-
static void injectHwcDisplay(DisplayTransactionTest*) {}
static void setupHwcGetActiveConfigCallExpectations(DisplayTransactionTest* test) {
@@ -581,48 +578,118 @@
}
};
+struct NonHwcPerFrameMetadataSupportVariant {
+ static constexpr int PER_FRAME_METADATA_KEYS = 0;
+ static void setupComposerCallExpectations(DisplayTransactionTest* test) {
+ EXPECT_CALL(*test->mComposer, getPerFrameMetadataKeys(_, _)).Times(0);
+ }
+};
+
+template <typename Display>
+struct NoPerFrameMetadataSupportVariant {
+ static constexpr int PER_FRAME_METADATA_KEYS = 0;
+ static void setupComposerCallExpectations(DisplayTransactionTest* test) {
+ EXPECT_CALL(*test->mComposer, getPerFrameMetadataKeys(Display::HWC_DISPLAY_ID, _))
+ .WillOnce(DoAll(SetArgPointee<1>(std::vector<PerFrameMetadataKey>()),
+ Return(Error::NONE)));
+ }
+};
+
+template <typename Display>
+struct Smpte2086PerFrameMetadataSupportVariant {
+ static constexpr int PER_FRAME_METADATA_KEYS = HdrMetadata::Type::SMPTE2086;
+ static void setupComposerCallExpectations(DisplayTransactionTest* test) {
+ EXPECT_CALL(*test->mComposer, getPerFrameMetadataKeys(Display::HWC_DISPLAY_ID, _))
+ .WillOnce(DoAll(SetArgPointee<1>(std::vector<PerFrameMetadataKey>({
+ PerFrameMetadataKey::DISPLAY_RED_PRIMARY_X,
+ PerFrameMetadataKey::DISPLAY_RED_PRIMARY_Y,
+ PerFrameMetadataKey::DISPLAY_GREEN_PRIMARY_X,
+ PerFrameMetadataKey::DISPLAY_GREEN_PRIMARY_Y,
+ PerFrameMetadataKey::DISPLAY_BLUE_PRIMARY_X,
+ PerFrameMetadataKey::DISPLAY_BLUE_PRIMARY_Y,
+ PerFrameMetadataKey::WHITE_POINT_X,
+ PerFrameMetadataKey::WHITE_POINT_Y,
+ PerFrameMetadataKey::MAX_LUMINANCE,
+ PerFrameMetadataKey::MIN_LUMINANCE,
+ })),
+ Return(Error::NONE)));
+ }
+};
+
+template <typename Display>
+struct Cta861_3_PerFrameMetadataSupportVariant {
+ static constexpr int PER_FRAME_METADATA_KEYS = HdrMetadata::Type::CTA861_3;
+ static void setupComposerCallExpectations(DisplayTransactionTest* test) {
+ EXPECT_CALL(*test->mComposer, getPerFrameMetadataKeys(Display::HWC_DISPLAY_ID, _))
+ .WillOnce(DoAll(SetArgPointee<1>(std::vector<PerFrameMetadataKey>({
+ PerFrameMetadataKey::MAX_CONTENT_LIGHT_LEVEL,
+ PerFrameMetadataKey::MAX_FRAME_AVERAGE_LIGHT_LEVEL,
+ })),
+ Return(Error::NONE)));
+ }
+};
+
/* ------------------------------------------------------------------------
* Typical display configurations to test
*/
-template <typename DisplayPolicy, typename WideColorSupportPolicy, typename HdrSupportPolicy>
+template <typename DisplayPolicy, typename WideColorSupportPolicy, typename HdrSupportPolicy,
+ typename PerFrameMetadataSupportPolicy>
struct Case {
using Display = DisplayPolicy;
using WideColorSupport = WideColorSupportPolicy;
using HdrSupport = HdrSupportPolicy;
+ using PerFrameMetadataSupport = PerFrameMetadataSupportPolicy;
};
using SimplePrimaryDisplayCase =
Case<PrimaryDisplayVariant, WideColorNotSupportedVariant<PrimaryDisplayVariant>,
- HdrNotSupportedVariant<PrimaryDisplayVariant>>;
+ HdrNotSupportedVariant<PrimaryDisplayVariant>,
+ NoPerFrameMetadataSupportVariant<PrimaryDisplayVariant>>;
using SimpleExternalDisplayCase =
Case<ExternalDisplayVariant, WideColorNotSupportedVariant<ExternalDisplayVariant>,
- HdrNotSupportedVariant<ExternalDisplayVariant>>;
+ HdrNotSupportedVariant<ExternalDisplayVariant>,
+ NoPerFrameMetadataSupportVariant<ExternalDisplayVariant>>;
using SimpleTertiaryDisplayCase =
Case<TertiaryDisplayVariant, WideColorNotSupportedVariant<TertiaryDisplayVariant>,
- HdrNotSupportedVariant<TertiaryDisplayVariant>>;
+ HdrNotSupportedVariant<TertiaryDisplayVariant>,
+ NoPerFrameMetadataSupportVariant<TertiaryDisplayVariant>>;
using NonHwcVirtualDisplayCase =
Case<NonHwcVirtualDisplayVariant<1024, 768, Secure::FALSE>,
- WideColorSupportNotConfiguredVariant, NonHwcDisplayHdrSupportVariant>;
+ WideColorSupportNotConfiguredVariant, NonHwcDisplayHdrSupportVariant,
+ NonHwcPerFrameMetadataSupportVariant>;
using SimpleHwcVirtualDisplayVariant = HwcVirtualDisplayVariant<1024, 768, Secure::TRUE>;
using HwcVirtualDisplayCase =
Case<SimpleHwcVirtualDisplayVariant, WideColorSupportNotConfiguredVariant,
- HdrNotSupportedVariant<SimpleHwcVirtualDisplayVariant>>;
+ HdrNotSupportedVariant<SimpleHwcVirtualDisplayVariant>,
+ NoPerFrameMetadataSupportVariant<SimpleHwcVirtualDisplayVariant>>;
using WideColorP3ColorimetricDisplayCase =
Case<PrimaryDisplayVariant, WideColorP3ColorimetricSupportedVariant<PrimaryDisplayVariant>,
- HdrNotSupportedVariant<PrimaryDisplayVariant>>;
+ HdrNotSupportedVariant<PrimaryDisplayVariant>,
+ NoPerFrameMetadataSupportVariant<PrimaryDisplayVariant>>;
using Hdr10DisplayCase =
Case<PrimaryDisplayVariant, WideColorNotSupportedVariant<PrimaryDisplayVariant>,
- Hdr10SupportedVariant<PrimaryDisplayVariant>>;
+ Hdr10SupportedVariant<PrimaryDisplayVariant>,
+ NoPerFrameMetadataSupportVariant<PrimaryDisplayVariant>>;
using HdrHlgDisplayCase =
Case<PrimaryDisplayVariant, WideColorNotSupportedVariant<PrimaryDisplayVariant>,
- HdrHlgSupportedVariant<PrimaryDisplayVariant>>;
+ HdrHlgSupportedVariant<PrimaryDisplayVariant>,
+ NoPerFrameMetadataSupportVariant<PrimaryDisplayVariant>>;
using HdrDolbyVisionDisplayCase =
Case<PrimaryDisplayVariant, WideColorNotSupportedVariant<PrimaryDisplayVariant>,
- HdrDolbyVisionSupportedVariant<PrimaryDisplayVariant>>;
+ HdrDolbyVisionSupportedVariant<PrimaryDisplayVariant>,
+ NoPerFrameMetadataSupportVariant<PrimaryDisplayVariant>>;
+using HdrSmpte2086DisplayCase =
+ Case<PrimaryDisplayVariant, WideColorNotSupportedVariant<PrimaryDisplayVariant>,
+ HdrNotSupportedVariant<PrimaryDisplayVariant>,
+ Smpte2086PerFrameMetadataSupportVariant<PrimaryDisplayVariant>>;
+using HdrCta861_3_DisplayCase =
+ Case<PrimaryDisplayVariant, WideColorNotSupportedVariant<PrimaryDisplayVariant>,
+ HdrNotSupportedVariant<PrimaryDisplayVariant>,
+ Cta861_3_PerFrameMetadataSupportVariant<PrimaryDisplayVariant>>;
using InvalidDisplayCase = Case<InvalidDisplayVariant, WideColorSupportNotConfiguredVariant,
- NonHwcDisplayHdrSupportVariant>;
-
+ NonHwcDisplayHdrSupportVariant,
+ NoPerFrameMetadataSupportVariant<InvalidDisplayVariant>>;
/* ------------------------------------------------------------------------
*
* SurfaceFlinger::onHotplugReceived
@@ -960,12 +1027,10 @@
// Various native window calls will be made.
Case::Display::setupNativeWindowSurfaceCreationCallExpectations(this);
-
- // TODO(b/69807179): SurfaceFlinger does not correctly get the active config.
- // Case::Display::setupHwcGetActiveConfigCallExpectations(this)
-
+ Case::Display::setupHwcGetActiveConfigCallExpectations(this);
Case::WideColorSupport::setupComposerCallExpectations(this);
Case::HdrSupport::setupComposerCallExpectations(this);
+ Case::PerFrameMetadataSupport::setupComposerCallExpectations(this);
// --------------------------------------------------------------------
// Invocation
@@ -986,7 +1051,12 @@
EXPECT_EQ(Case::HdrSupport::HDR10_SUPPORTED, device->hasHDR10Support());
EXPECT_EQ(Case::HdrSupport::HDR_HLG_SUPPORTED, device->hasHLGSupport());
EXPECT_EQ(Case::HdrSupport::HDR_DOLBY_VISION_SUPPORTED, device->hasDolbyVisionSupport());
- EXPECT_EQ(Case::Display::HWC_ACTIVE_CONFIG_ID, device->getActiveConfig());
+ // Note: This is not Case::Display::HWC_ACTIVE_CONFIG_ID as the ids are
+ // remapped, and the test only ever sets up one config. If there were an error
+ // looking up the remapped index, device->getActiveConfig() would be -1 instead.
+ EXPECT_EQ(0, device->getActiveConfig());
+ EXPECT_EQ(Case::PerFrameMetadataSupport::PER_FRAME_METADATA_KEYS,
+ device->getSupportedPerFrameMetadata());
}
TEST_F(SetupNewDisplayDeviceInternalTest, createSimplePrimaryDisplay) {
@@ -1025,6 +1095,14 @@
setupNewDisplayDeviceInternalTest<HdrDolbyVisionDisplayCase>();
}
+TEST_F(SetupNewDisplayDeviceInternalTest, createHdrSmpte2086DisplayCase) {
+ setupNewDisplayDeviceInternalTest<HdrSmpte2086DisplayCase>();
+}
+
+TEST_F(SetupNewDisplayDeviceInternalTest, createHdrCta816_3_DisplayCase) {
+ setupNewDisplayDeviceInternalTest<HdrCta861_3_DisplayCase>();
+}
+
/* ------------------------------------------------------------------------
* SurfaceFlinger::handleTransactionLocked(eDisplayTransactionNeeded)
*/
@@ -1082,6 +1160,7 @@
Case::WideColorSupport::setupComposerCallExpectations(this);
Case::HdrSupport::setupComposerCallExpectations(this);
+ Case::PerFrameMetadataSupport::setupComposerCallExpectations(this);
EXPECT_CALL(*mSurfaceInterceptor, saveDisplayCreation(_)).Times(1);
EXPECT_CALL(*mEventThread, onHotplugReceived(Case::Display::TYPE, true)).Times(1);
@@ -1421,6 +1500,7 @@
Case::Display::setupHwcVirtualDisplayCreationCallExpectations(this);
Case::WideColorSupport::setupComposerCallExpectations(this);
Case::HdrSupport::setupComposerCallExpectations(this);
+ Case::PerFrameMetadataSupport::setupComposerCallExpectations(this);
// --------------------------------------------------------------------
// Invocation
@@ -2276,5 +2356,446 @@
EXPECT_EQ(DEFAULT_REFRESH_RATE, compositorTiming.presentLatency);
}
+/* ------------------------------------------------------------------------
+ * SurfaceFlinger::setPowerModeInternal
+ */
+
+// Used when we simulate a display that supports doze.
+struct DozeIsSupportedVariant {
+ static constexpr bool DOZE_SUPPORTED = true;
+ static constexpr IComposerClient::PowerMode ACTUAL_POWER_MODE_FOR_DOZE =
+ IComposerClient::PowerMode::DOZE;
+ static constexpr IComposerClient::PowerMode ACTUAL_POWER_MODE_FOR_DOZE_SUSPEND =
+ IComposerClient::PowerMode::DOZE_SUSPEND;
+};
+
+// Used when we simulate a display that does not support doze.
+struct DozeNotSupportedVariant {
+ static constexpr bool DOZE_SUPPORTED = false;
+ static constexpr IComposerClient::PowerMode ACTUAL_POWER_MODE_FOR_DOZE =
+ IComposerClient::PowerMode::ON;
+ static constexpr IComposerClient::PowerMode ACTUAL_POWER_MODE_FOR_DOZE_SUSPEND =
+ IComposerClient::PowerMode::ON;
+};
+
+struct EventThreadBaseSupportedVariant {
+ static void setupEventAndEventControlThreadNoCallExpectations(DisplayTransactionTest* test) {
+ // The event control thread should not be notified.
+ EXPECT_CALL(*test->mEventControlThread, setVsyncEnabled(_)).Times(0);
+
+ // The event thread should not be notified.
+ EXPECT_CALL(*test->mEventThread, onScreenReleased()).Times(0);
+ EXPECT_CALL(*test->mEventThread, onScreenAcquired()).Times(0);
+ }
+};
+
+struct EventThreadNotSupportedVariant : public EventThreadBaseSupportedVariant {
+ static void setupAcquireAndEnableVsyncCallExpectations(DisplayTransactionTest* test) {
+ // These calls are only expected for the primary display.
+
+ // Instead expect no calls.
+ setupEventAndEventControlThreadNoCallExpectations(test);
+ }
+
+ static void setupReleaseAndDisableVsyncCallExpectations(DisplayTransactionTest* test) {
+ // These calls are only expected for the primary display.
+
+ // Instead expect no calls.
+ setupEventAndEventControlThreadNoCallExpectations(test);
+ }
+};
+
+struct EventThreadIsSupportedVariant : public EventThreadBaseSupportedVariant {
+ static void setupAcquireAndEnableVsyncCallExpectations(DisplayTransactionTest* test) {
+ // The event control thread should be notified to enable vsyncs
+ EXPECT_CALL(*test->mEventControlThread, setVsyncEnabled(true)).Times(1);
+
+ // The event thread should be notified that the screen was acquired.
+ EXPECT_CALL(*test->mEventThread, onScreenAcquired()).Times(1);
+ }
+
+ static void setupReleaseAndDisableVsyncCallExpectations(DisplayTransactionTest* test) {
+ // There should be a call to setVsyncEnabled(false)
+ EXPECT_CALL(*test->mEventControlThread, setVsyncEnabled(false)).Times(1);
+
+ // The event thread should not be notified that the screen was released.
+ EXPECT_CALL(*test->mEventThread, onScreenReleased()).Times(1);
+ }
+};
+
+// --------------------------------------------------------------------
+// Note:
+//
+// There are a large number of transitions we could test, however we only test a
+// selected subset which provides complete test coverage of the implementation.
+// --------------------------------------------------------------------
+
+template <int initialPowerMode, int targetPowerMode>
+struct TransitionVariantCommon {
+ static constexpr auto INITIAL_POWER_MODE = initialPowerMode;
+ static constexpr auto TARGET_POWER_MODE = targetPowerMode;
+
+ static void verifyPostconditions(DisplayTransactionTest*) {}
+};
+
+struct TransitionOffToOnVariant
+ : public TransitionVariantCommon<HWC_POWER_MODE_OFF, HWC_POWER_MODE_NORMAL> {
+ template <typename Case>
+ static void setupCallExpectations(DisplayTransactionTest* test) {
+ Case::setupComposerCallExpectations(test, IComposerClient::PowerMode::ON);
+ Case::EventThread::setupAcquireAndEnableVsyncCallExpectations(test);
+ Case::setupRepaintEverythingCallExpectations(test);
+ }
+
+ static void verifyPostconditions(DisplayTransactionTest* test) {
+ EXPECT_TRUE(test->mFlinger.getVisibleRegionsDirty());
+ EXPECT_TRUE(test->mFlinger.getHasPoweredOff());
+ }
+};
+
+struct TransitionOffToDozeSuspendVariant
+ : public TransitionVariantCommon<HWC_POWER_MODE_OFF, HWC_POWER_MODE_DOZE_SUSPEND> {
+ template <typename Case>
+ static void setupCallExpectations(DisplayTransactionTest* test) {
+ Case::setupComposerCallExpectations(test, Case::Doze::ACTUAL_POWER_MODE_FOR_DOZE_SUSPEND);
+ Case::EventThread::setupEventAndEventControlThreadNoCallExpectations(test);
+ Case::setupRepaintEverythingCallExpectations(test);
+ }
+
+ static void verifyPostconditions(DisplayTransactionTest* test) {
+ EXPECT_TRUE(test->mFlinger.getVisibleRegionsDirty());
+ EXPECT_TRUE(test->mFlinger.getHasPoweredOff());
+ }
+};
+
+struct TransitionOnToOffVariant
+ : public TransitionVariantCommon<HWC_POWER_MODE_NORMAL, HWC_POWER_MODE_OFF> {
+ template <typename Case>
+ static void setupCallExpectations(DisplayTransactionTest* test) {
+ Case::EventThread::setupReleaseAndDisableVsyncCallExpectations(test);
+ Case::setupComposerCallExpectations(test, IComposerClient::PowerMode::OFF);
+ }
+
+ static void verifyPostconditions(DisplayTransactionTest* test) {
+ EXPECT_TRUE(test->mFlinger.getVisibleRegionsDirty());
+ }
+};
+
+struct TransitionDozeSuspendToOffVariant
+ : public TransitionVariantCommon<HWC_POWER_MODE_DOZE_SUSPEND, HWC_POWER_MODE_OFF> {
+ template <typename Case>
+ static void setupCallExpectations(DisplayTransactionTest* test) {
+ Case::EventThread::setupEventAndEventControlThreadNoCallExpectations(test);
+ Case::setupComposerCallExpectations(test, IComposerClient::PowerMode::OFF);
+ }
+
+ static void verifyPostconditions(DisplayTransactionTest* test) {
+ EXPECT_TRUE(test->mFlinger.getVisibleRegionsDirty());
+ }
+};
+
+struct TransitionOnToDozeVariant
+ : public TransitionVariantCommon<HWC_POWER_MODE_NORMAL, HWC_POWER_MODE_DOZE> {
+ template <typename Case>
+ static void setupCallExpectations(DisplayTransactionTest* test) {
+ Case::EventThread::setupEventAndEventControlThreadNoCallExpectations(test);
+ Case::setupComposerCallExpectations(test, Case::Doze::ACTUAL_POWER_MODE_FOR_DOZE);
+ }
+};
+
+struct TransitionDozeSuspendToDozeVariant
+ : public TransitionVariantCommon<HWC_POWER_MODE_DOZE_SUSPEND, HWC_POWER_MODE_DOZE> {
+ template <typename Case>
+ static void setupCallExpectations(DisplayTransactionTest* test) {
+ Case::EventThread::setupAcquireAndEnableVsyncCallExpectations(test);
+ Case::setupComposerCallExpectations(test, Case::Doze::ACTUAL_POWER_MODE_FOR_DOZE);
+ }
+};
+
+struct TransitionDozeToOnVariant
+ : public TransitionVariantCommon<HWC_POWER_MODE_DOZE, HWC_POWER_MODE_NORMAL> {
+ template <typename Case>
+ static void setupCallExpectations(DisplayTransactionTest* test) {
+ Case::EventThread::setupEventAndEventControlThreadNoCallExpectations(test);
+ Case::setupComposerCallExpectations(test, IComposerClient::PowerMode::ON);
+ }
+};
+
+struct TransitionDozeSuspendToOnVariant
+ : public TransitionVariantCommon<HWC_POWER_MODE_DOZE_SUSPEND, HWC_POWER_MODE_NORMAL> {
+ template <typename Case>
+ static void setupCallExpectations(DisplayTransactionTest* test) {
+ Case::EventThread::setupAcquireAndEnableVsyncCallExpectations(test);
+ Case::setupComposerCallExpectations(test, IComposerClient::PowerMode::ON);
+ }
+};
+
+struct TransitionOnToDozeSuspendVariant
+ : public TransitionVariantCommon<HWC_POWER_MODE_NORMAL, HWC_POWER_MODE_DOZE_SUSPEND> {
+ template <typename Case>
+ static void setupCallExpectations(DisplayTransactionTest* test) {
+ Case::EventThread::setupReleaseAndDisableVsyncCallExpectations(test);
+ Case::setupComposerCallExpectations(test, Case::Doze::ACTUAL_POWER_MODE_FOR_DOZE_SUSPEND);
+ }
+};
+
+struct TransitionOnToUnknownVariant
+ : public TransitionVariantCommon<HWC_POWER_MODE_NORMAL, HWC_POWER_MODE_LEET> {
+ template <typename Case>
+ static void setupCallExpectations(DisplayTransactionTest* test) {
+ Case::EventThread::setupEventAndEventControlThreadNoCallExpectations(test);
+ Case::setupNoComposerPowerModeCallExpectations(test);
+ }
+};
+
+// --------------------------------------------------------------------
+// Note:
+//
+// Rather than testing the cartesian product of of
+// DozeIsSupported/DozeNotSupported with all other options, we use one for one
+// display type, and the other for another display type.
+// --------------------------------------------------------------------
+
+template <typename DisplayVariant, typename DozeVariant, typename EventThreadVariant,
+ typename TransitionVariant>
+struct DisplayPowerCase {
+ using Display = DisplayVariant;
+ using Doze = DozeVariant;
+ using EventThread = EventThreadVariant;
+ using Transition = TransitionVariant;
+
+ static auto injectDisplayWithInitialPowerMode(DisplayTransactionTest* test, int mode) {
+ Display::injectHwcDisplay(test);
+ auto display = Display::makeFakeExistingDisplayInjector(test);
+ display.inject();
+ display.mutableDisplayDevice()->setPowerMode(mode);
+ return display;
+ }
+
+ static void setInitialPrimaryHWVsyncEnabled(DisplayTransactionTest* test, bool enabled) {
+ test->mFlinger.mutablePrimaryHWVsyncEnabled() = enabled;
+ }
+
+ static void setupRepaintEverythingCallExpectations(DisplayTransactionTest* test) {
+ EXPECT_CALL(*test->mMessageQueue, invalidate()).Times(1);
+ }
+
+ static void setupSurfaceInterceptorCallExpectations(DisplayTransactionTest* test, int mode) {
+ EXPECT_CALL(*test->mSurfaceInterceptor, isEnabled()).WillOnce(Return(true));
+ EXPECT_CALL(*test->mSurfaceInterceptor, savePowerModeUpdate(_, mode)).Times(1);
+ }
+
+ static void setupComposerCallExpectations(DisplayTransactionTest* test,
+ IComposerClient::PowerMode mode) {
+ // Any calls to get the active config will return a default value.
+ EXPECT_CALL(*test->mComposer, getActiveConfig(Display::HWC_DISPLAY_ID, _))
+ .WillRepeatedly(DoAll(SetArgPointee<1>(Display::HWC_ACTIVE_CONFIG_ID),
+ Return(Error::NONE)));
+
+ // Any calls to get whether the display supports dozing will return the value set by the
+ // policy variant.
+ EXPECT_CALL(*test->mComposer, getDozeSupport(Display::HWC_DISPLAY_ID, _))
+ .WillRepeatedly(DoAll(SetArgPointee<1>(Doze::DOZE_SUPPORTED), Return(Error::NONE)));
+
+ EXPECT_CALL(*test->mComposer, setPowerMode(Display::HWC_DISPLAY_ID, mode)).Times(1);
+ }
+
+ static void setupNoComposerPowerModeCallExpectations(DisplayTransactionTest* test) {
+ EXPECT_CALL(*test->mComposer, setPowerMode(Display::HWC_DISPLAY_ID, _)).Times(0);
+ }
+};
+
+// A sample configuration for the primary display.
+// In addition to having event thread support, we emulate doze support.
+template <typename TransitionVariant>
+using PrimaryDisplayPowerCase = DisplayPowerCase<PrimaryDisplayVariant, DozeIsSupportedVariant,
+ EventThreadIsSupportedVariant, TransitionVariant>;
+
+// A sample configuration for the external display.
+// In addition to not having event thread support, we emulate not having doze
+// support.
+template <typename TransitionVariant>
+using ExternalDisplayPowerCase =
+ DisplayPowerCase<ExternalDisplayVariant, DozeNotSupportedVariant,
+ EventThreadNotSupportedVariant, TransitionVariant>;
+
+class SetPowerModeInternalTest : public DisplayTransactionTest {
+public:
+ template <typename Case>
+ void transitionDisplayCommon();
+};
+
+template <int PowerMode>
+struct PowerModeInitialVSyncEnabled : public std::false_type {};
+
+template <>
+struct PowerModeInitialVSyncEnabled<HWC_POWER_MODE_NORMAL> : public std::true_type {};
+
+template <>
+struct PowerModeInitialVSyncEnabled<HWC_POWER_MODE_DOZE> : public std::true_type {};
+
+template <typename Case>
+void SetPowerModeInternalTest::transitionDisplayCommon() {
+ // --------------------------------------------------------------------
+ // Preconditions
+
+ auto display =
+ Case::injectDisplayWithInitialPowerMode(this, Case::Transition::INITIAL_POWER_MODE);
+ Case::setInitialPrimaryHWVsyncEnabled(this,
+ PowerModeInitialVSyncEnabled<
+ Case::Transition::INITIAL_POWER_MODE>::value);
+
+ // --------------------------------------------------------------------
+ // Call Expectations
+
+ Case::setupSurfaceInterceptorCallExpectations(this, Case::Transition::TARGET_POWER_MODE);
+ Case::Transition::template setupCallExpectations<Case>(this);
+
+ // --------------------------------------------------------------------
+ // Invocation
+
+ mFlinger.setPowerModeInternal(display.mutableDisplayDevice(),
+ Case::Transition::TARGET_POWER_MODE);
+
+ // --------------------------------------------------------------------
+ // Postconditions
+
+ Case::Transition::verifyPostconditions(this);
+}
+
+TEST_F(SetPowerModeInternalTest, setPowerModeInternalDoesNothingIfNoChange) {
+ using Case = SimplePrimaryDisplayCase;
+
+ // --------------------------------------------------------------------
+ // Preconditions
+
+ // A primary display device is set up
+ Case::Display::injectHwcDisplay(this);
+ auto display = Case::Display::makeFakeExistingDisplayInjector(this);
+ display.inject();
+
+ // The diplay is already set to HWC_POWER_MODE_NORMAL
+ display.mutableDisplayDevice()->setPowerMode(HWC_POWER_MODE_NORMAL);
+
+ // --------------------------------------------------------------------
+ // Invocation
+
+ mFlinger.setPowerModeInternal(display.mutableDisplayDevice(), HWC_POWER_MODE_NORMAL);
+
+ // --------------------------------------------------------------------
+ // Postconditions
+
+ EXPECT_EQ(HWC_POWER_MODE_NORMAL, display.mutableDisplayDevice()->getPowerMode());
+}
+
+TEST_F(SetPowerModeInternalTest, setPowerModeInternalJustSetsInternalStateIfVirtualDisplay) {
+ using Case = HwcVirtualDisplayCase;
+
+ // --------------------------------------------------------------------
+ // Preconditions
+
+ // We need to resize this so that the HWC thinks the virtual display
+ // is something it created.
+ mFlinger.mutableHwcDisplayData().resize(3);
+
+ // A virtual display device is set up
+ Case::Display::injectHwcDisplay(this);
+ auto display = Case::Display::makeFakeExistingDisplayInjector(this);
+ display.inject();
+
+ // The display is set to HWC_POWER_MODE_OFF
+ getDisplayDevice(display.token())->setPowerMode(HWC_POWER_MODE_OFF);
+
+ // --------------------------------------------------------------------
+ // Invocation
+
+ mFlinger.setPowerModeInternal(display.mutableDisplayDevice(), HWC_POWER_MODE_NORMAL);
+
+ // --------------------------------------------------------------------
+ // Postconditions
+
+ EXPECT_EQ(HWC_POWER_MODE_NORMAL, display.mutableDisplayDevice()->getPowerMode());
+}
+
+TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOffToOnPrimaryDisplay) {
+ transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionOffToOnVariant>>();
+}
+
+TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOffToDozeSuspendPrimaryDisplay) {
+ transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionOffToDozeSuspendVariant>>();
+}
+
+TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToOffPrimaryDisplay) {
+ transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionOnToOffVariant>>();
+}
+
+TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeSuspendToOffPrimaryDisplay) {
+ transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionDozeSuspendToOffVariant>>();
+}
+
+TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToDozePrimaryDisplay) {
+ transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionOnToDozeVariant>>();
+}
+
+TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeSuspendToDozePrimaryDisplay) {
+ transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionDozeSuspendToDozeVariant>>();
+}
+
+TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeToOnPrimaryDisplay) {
+ transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionDozeToOnVariant>>();
+}
+
+TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeSuspendToOnPrimaryDisplay) {
+ transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionDozeSuspendToOnVariant>>();
+}
+
+TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToDozeSuspendPrimaryDisplay) {
+ transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionOnToDozeSuspendVariant>>();
+}
+
+TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToUnknownPrimaryDisplay) {
+ transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionOnToUnknownVariant>>();
+}
+
+TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOffToOnExternalDisplay) {
+ transitionDisplayCommon<ExternalDisplayPowerCase<TransitionOffToOnVariant>>();
+}
+
+TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOffToDozeSuspendExternalDisplay) {
+ transitionDisplayCommon<ExternalDisplayPowerCase<TransitionOffToDozeSuspendVariant>>();
+}
+
+TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToOffExternalDisplay) {
+ transitionDisplayCommon<ExternalDisplayPowerCase<TransitionOnToOffVariant>>();
+}
+
+TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeSuspendToOffExternalDisplay) {
+ transitionDisplayCommon<ExternalDisplayPowerCase<TransitionDozeSuspendToOffVariant>>();
+}
+
+TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToDozeExternalDisplay) {
+ transitionDisplayCommon<ExternalDisplayPowerCase<TransitionOnToDozeVariant>>();
+}
+
+TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeSuspendToDozeExternalDisplay) {
+ transitionDisplayCommon<ExternalDisplayPowerCase<TransitionDozeSuspendToDozeVariant>>();
+}
+
+TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeToOnExternalDisplay) {
+ transitionDisplayCommon<ExternalDisplayPowerCase<TransitionDozeToOnVariant>>();
+}
+
+TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeSuspendToOnExternalDisplay) {
+ transitionDisplayCommon<ExternalDisplayPowerCase<TransitionDozeSuspendToOnVariant>>();
+}
+
+TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToDozeSuspendExternalDisplay) {
+ transitionDisplayCommon<ExternalDisplayPowerCase<TransitionOnToDozeSuspendVariant>>();
+}
+
+TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToUnknownExternalDisplay) {
+ transitionDisplayCommon<ExternalDisplayPowerCase<TransitionOnToUnknownVariant>>();
+}
+
} // namespace
} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/EventControlThreadTest.cpp b/services/surfaceflinger/tests/unittests/EventControlThreadTest.cpp
new file mode 100644
index 0000000..b346454
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/EventControlThreadTest.cpp
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "LibSurfaceFlingerUnittests"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <log/log.h>
+
+#include "AsyncCallRecorder.h"
+#include "EventControlThread.h"
+
+namespace android {
+namespace {
+
+using namespace std::chrono_literals;
+using testing::_;
+
+class EventControlThreadTest : public testing::Test {
+protected:
+ EventControlThreadTest();
+ ~EventControlThreadTest() override;
+
+ void createThread();
+
+ void expectVSyncEnableCallbackCalled(bool enable);
+
+ AsyncCallRecorder<void (*)(bool)> mVSyncSetEnabledCallRecorder;
+
+ std::unique_ptr<EventControlThread> mThread;
+};
+
+EventControlThreadTest::EventControlThreadTest() {
+ const ::testing::TestInfo* const test_info =
+ ::testing::UnitTest::GetInstance()->current_test_info();
+ ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
+}
+
+EventControlThreadTest::~EventControlThreadTest() {
+ const ::testing::TestInfo* const test_info =
+ ::testing::UnitTest::GetInstance()->current_test_info();
+ ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
+}
+
+void EventControlThreadTest::createThread() {
+ mThread = std::make_unique<android::impl::EventControlThread>(
+ mVSyncSetEnabledCallRecorder.getInvocable());
+}
+
+void EventControlThreadTest::expectVSyncEnableCallbackCalled(bool expectedEnabled) {
+ auto args = mVSyncSetEnabledCallRecorder.waitForCall();
+ ASSERT_TRUE(args.has_value());
+ EXPECT_EQ(std::get<0>(args.value()), expectedEnabled);
+}
+
+/* ------------------------------------------------------------------------
+ * Test cases
+ */
+
+TEST_F(EventControlThreadTest, signalsVSyncDisabledOnStartup) {
+ createThread();
+
+ // On thread start, there should be an automatic explicit call to disable
+ // vsyncs
+ expectVSyncEnableCallbackCalled(false);
+}
+
+TEST_F(EventControlThreadTest, signalsVSyncDisabledOnce) {
+ createThread();
+ expectVSyncEnableCallbackCalled(false);
+
+ mThread->setVsyncEnabled(false);
+
+ EXPECT_FALSE(mVSyncSetEnabledCallRecorder.waitForUnexpectedCall().has_value());
+}
+
+TEST_F(EventControlThreadTest, signalsVSyncEnabledThenDisabled) {
+ createThread();
+ expectVSyncEnableCallbackCalled(false);
+
+ mThread->setVsyncEnabled(true);
+
+ expectVSyncEnableCallbackCalled(true);
+
+ mThread->setVsyncEnabled(false);
+
+ expectVSyncEnableCallbackCalled(false);
+}
+
+TEST_F(EventControlThreadTest, signalsVSyncEnabledOnce) {
+ createThread();
+ expectVSyncEnableCallbackCalled(false);
+
+ mThread->setVsyncEnabled(true);
+
+ expectVSyncEnableCallbackCalled(true);
+
+ mThread->setVsyncEnabled(true);
+
+ EXPECT_FALSE(mVSyncSetEnabledCallRecorder.waitForUnexpectedCall().has_value());
+}
+
+} // namespace
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
new file mode 100644
index 0000000..80fdb80
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
@@ -0,0 +1,422 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "LibSurfaceFlingerUnittests"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <log/log.h>
+
+#include <utils/Errors.h>
+
+#include "AsyncCallRecorder.h"
+#include "EventThread.h"
+
+using namespace std::chrono_literals;
+using namespace std::placeholders;
+
+using testing::_;
+using testing::Invoke;
+
+namespace android {
+namespace {
+
+class MockVSyncSource : public VSyncSource {
+public:
+ MOCK_METHOD1(setVSyncEnabled, void(bool));
+ MOCK_METHOD1(setCallback, void(VSyncSource::Callback*));
+ MOCK_METHOD1(setPhaseOffset, void(nsecs_t));
+};
+
+} // namespace
+
+class EventThreadTest : public testing::Test {
+protected:
+ class MockEventThreadConnection : public android::impl::EventThread::Connection {
+ public:
+ explicit MockEventThreadConnection(android::impl::EventThread* eventThread)
+ : android::impl::EventThread::Connection(eventThread) {}
+ MOCK_METHOD1(postEvent, status_t(const DisplayEventReceiver::Event& event));
+ };
+
+ using ConnectionEventRecorder =
+ AsyncCallRecorderWithCannedReturn<status_t (*)(const DisplayEventReceiver::Event&)>;
+
+ EventThreadTest();
+ ~EventThreadTest() override;
+
+ void createThread();
+ sp<MockEventThreadConnection> createConnection(ConnectionEventRecorder& recorder);
+
+ void expectVSyncSetEnabledCallReceived(bool expectedState);
+ void expectVSyncSetPhaseOffsetCallReceived(nsecs_t expectedPhaseOffset);
+ VSyncSource::Callback* expectVSyncSetCallbackCallReceived();
+ void expectInterceptCallReceived(nsecs_t expectedTimestamp);
+ void expectVsyncEventReceivedByConnection(const char* name,
+ ConnectionEventRecorder& connectionEventRecorder,
+ nsecs_t expectedTimestamp, unsigned expectedCount);
+ void expectVsyncEventReceivedByConnection(nsecs_t expectedTimestamp, unsigned expectedCount);
+ void expectHotplugEventReceivedByConnection(int expectedDisplayType, bool expectedConnected);
+
+ AsyncCallRecorder<void (*)(bool)> mVSyncSetEnabledCallRecorder;
+ AsyncCallRecorder<void (*)(VSyncSource::Callback*)> mVSyncSetCallbackCallRecorder;
+ AsyncCallRecorder<void (*)(nsecs_t)> mVSyncSetPhaseOffsetCallRecorder;
+ AsyncCallRecorder<void (*)()> mResyncCallRecorder;
+ AsyncCallRecorder<void (*)(nsecs_t)> mInterceptVSyncCallRecorder;
+ ConnectionEventRecorder mConnectionEventCallRecorder{0};
+
+ MockVSyncSource mVSyncSource;
+ std::unique_ptr<android::impl::EventThread> mThread;
+ sp<MockEventThreadConnection> mConnection;
+};
+
+EventThreadTest::EventThreadTest() {
+ const ::testing::TestInfo* const test_info =
+ ::testing::UnitTest::GetInstance()->current_test_info();
+ ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
+
+ EXPECT_CALL(mVSyncSource, setVSyncEnabled(_))
+ .WillRepeatedly(Invoke(mVSyncSetEnabledCallRecorder.getInvocable()));
+
+ EXPECT_CALL(mVSyncSource, setCallback(_))
+ .WillRepeatedly(Invoke(mVSyncSetCallbackCallRecorder.getInvocable()));
+
+ EXPECT_CALL(mVSyncSource, setPhaseOffset(_))
+ .WillRepeatedly(Invoke(mVSyncSetPhaseOffsetCallRecorder.getInvocable()));
+
+ createThread();
+ mConnection = createConnection(mConnectionEventCallRecorder);
+}
+
+EventThreadTest::~EventThreadTest() {
+ const ::testing::TestInfo* const test_info =
+ ::testing::UnitTest::GetInstance()->current_test_info();
+ ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
+}
+
+void EventThreadTest::createThread() {
+ mThread =
+ std::make_unique<android::impl::EventThread>(&mVSyncSource,
+ mResyncCallRecorder.getInvocable(),
+ mInterceptVSyncCallRecorder.getInvocable(),
+ "unit-test-event-thread");
+}
+
+sp<EventThreadTest::MockEventThreadConnection> EventThreadTest::createConnection(
+ ConnectionEventRecorder& recorder) {
+ sp<MockEventThreadConnection> connection = new MockEventThreadConnection(mThread.get());
+ EXPECT_CALL(*connection, postEvent(_)).WillRepeatedly(Invoke(recorder.getInvocable()));
+ return connection;
+}
+
+void EventThreadTest::expectVSyncSetEnabledCallReceived(bool expectedState) {
+ auto args = mVSyncSetEnabledCallRecorder.waitForCall();
+ ASSERT_TRUE(args.has_value());
+ EXPECT_EQ(expectedState, std::get<0>(args.value()));
+}
+
+void EventThreadTest::expectVSyncSetPhaseOffsetCallReceived(nsecs_t expectedPhaseOffset) {
+ auto args = mVSyncSetPhaseOffsetCallRecorder.waitForCall();
+ ASSERT_TRUE(args.has_value());
+ EXPECT_EQ(expectedPhaseOffset, std::get<0>(args.value()));
+}
+
+VSyncSource::Callback* EventThreadTest::expectVSyncSetCallbackCallReceived() {
+ auto callbackSet = mVSyncSetCallbackCallRecorder.waitForCall();
+ return callbackSet.has_value() ? std::get<0>(callbackSet.value()) : nullptr;
+}
+
+void EventThreadTest::expectInterceptCallReceived(nsecs_t expectedTimestamp) {
+ auto args = mInterceptVSyncCallRecorder.waitForCall();
+ ASSERT_TRUE(args.has_value());
+ EXPECT_EQ(expectedTimestamp, std::get<0>(args.value()));
+}
+
+void EventThreadTest::expectVsyncEventReceivedByConnection(
+ const char* name, ConnectionEventRecorder& connectionEventRecorder,
+ nsecs_t expectedTimestamp, unsigned expectedCount) {
+ auto args = connectionEventRecorder.waitForCall();
+ ASSERT_TRUE(args.has_value()) << name << " did not receive an event for timestamp "
+ << expectedTimestamp;
+ const auto& event = std::get<0>(args.value());
+ EXPECT_EQ(DisplayEventReceiver::DISPLAY_EVENT_VSYNC, event.header.type)
+ << name << " did not get the correct event for timestamp " << expectedTimestamp;
+ EXPECT_EQ(expectedTimestamp, event.header.timestamp)
+ << name << " did not get the expected timestamp for timestamp " << expectedTimestamp;
+ EXPECT_EQ(expectedCount, event.vsync.count)
+ << name << " did not get the expected count for timestamp " << expectedTimestamp;
+}
+
+void EventThreadTest::expectVsyncEventReceivedByConnection(nsecs_t expectedTimestamp,
+ unsigned expectedCount) {
+ expectVsyncEventReceivedByConnection("mConnectionEventCallRecorder",
+ mConnectionEventCallRecorder, expectedTimestamp,
+ expectedCount);
+}
+
+void EventThreadTest::expectHotplugEventReceivedByConnection(int expectedDisplayType,
+ bool expectedConnected) {
+ auto args = mConnectionEventCallRecorder.waitForCall();
+ ASSERT_TRUE(args.has_value());
+ const auto& event = std::get<0>(args.value());
+ EXPECT_EQ(DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG, event.header.type);
+ EXPECT_EQ(static_cast<unsigned>(expectedDisplayType), event.header.id);
+ EXPECT_EQ(expectedConnected, event.hotplug.connected);
+}
+
+namespace {
+
+/* ------------------------------------------------------------------------
+ * Test cases
+ */
+
+TEST_F(EventThreadTest, canCreateAndDestroyThreadWithNoEventsSent) {
+ EXPECT_FALSE(mVSyncSetEnabledCallRecorder.waitForUnexpectedCall().has_value());
+ EXPECT_FALSE(mVSyncSetCallbackCallRecorder.waitForCall(0us).has_value());
+ EXPECT_FALSE(mVSyncSetPhaseOffsetCallRecorder.waitForCall(0us).has_value());
+ EXPECT_FALSE(mResyncCallRecorder.waitForCall(0us).has_value());
+ EXPECT_FALSE(mInterceptVSyncCallRecorder.waitForCall(0us).has_value());
+ EXPECT_FALSE(mConnectionEventCallRecorder.waitForCall(0us).has_value());
+}
+
+TEST_F(EventThreadTest, requestNextVsyncPostsASingleVSyncEventToTheConnection) {
+ // Signal that we want the next vsync event to be posted to the connection
+ mThread->requestNextVsync(mConnection);
+
+ // EventThread should immediately request a resync.
+ EXPECT_TRUE(mResyncCallRecorder.waitForCall().has_value());
+
+ // EventThread should enable vsync callbacks, and set a callback interface
+ // pointer to use them with the VSync source.
+ expectVSyncSetEnabledCallReceived(true);
+ auto callback = expectVSyncSetCallbackCallReceived();
+ ASSERT_TRUE(callback);
+
+ // Use the received callback to signal a first vsync event.
+ // The interceptor should receive the event, as well as the connection.
+ callback->onVSyncEvent(123);
+ expectInterceptCallReceived(123);
+ expectVsyncEventReceivedByConnection(123, 1u);
+
+ // Use the received callback to signal a second vsync event.
+ // The interceptor should receive the event, but the the connection should
+ // not as it was only interested in the first.
+ callback->onVSyncEvent(456);
+ expectInterceptCallReceived(456);
+ EXPECT_FALSE(mConnectionEventCallRecorder.waitForUnexpectedCall().has_value());
+
+ // EventThread should also detect that at this point that it does not need
+ // any more vsync events, and should disable their generation.
+ expectVSyncSetEnabledCallReceived(false);
+}
+
+TEST_F(EventThreadTest, setVsyncRateZeroPostsNoVSyncEventsToThatConnection) {
+ // Create a first connection, register it, and request a vsync rate of zero.
+ ConnectionEventRecorder firstConnectionEventRecorder{0};
+ sp<MockEventThreadConnection> firstConnection = createConnection(firstConnectionEventRecorder);
+ mThread->setVsyncRate(0, firstConnection);
+
+ // By itself, this should not enable vsync events
+ EXPECT_FALSE(mVSyncSetEnabledCallRecorder.waitForUnexpectedCall().has_value());
+ EXPECT_FALSE(mVSyncSetCallbackCallRecorder.waitForCall(0us).has_value());
+
+ // However if there is another connection which wants events at a nonzero rate.....
+ ConnectionEventRecorder secondConnectionEventRecorder{0};
+ sp<MockEventThreadConnection> secondConnection =
+ createConnection(secondConnectionEventRecorder);
+ mThread->setVsyncRate(1, secondConnection);
+
+ // EventThread should enable vsync callbacks, and set a callback interface
+ // pointer to use them with the VSync source.
+ expectVSyncSetEnabledCallReceived(true);
+ auto callback = expectVSyncSetCallbackCallReceived();
+ ASSERT_TRUE(callback);
+
+ // Send a vsync event. EventThread should then make a call to the
+ // interceptor, and the second connection. The first connection should not
+ // get the event.
+ callback->onVSyncEvent(123);
+ expectInterceptCallReceived(123);
+ EXPECT_FALSE(firstConnectionEventRecorder.waitForUnexpectedCall().has_value());
+ expectVsyncEventReceivedByConnection("secondConnection", secondConnectionEventRecorder, 123,
+ 1u);
+}
+
+TEST_F(EventThreadTest, setVsyncRateOnePostsAllEventsToThatConnection) {
+ mThread->setVsyncRate(1, mConnection);
+
+ // EventThread should enable vsync callbacks, and set a callback interface
+ // pointer to use them with the VSync source.
+ expectVSyncSetEnabledCallReceived(true);
+ auto callback = expectVSyncSetCallbackCallReceived();
+ ASSERT_TRUE(callback);
+
+ // Send a vsync event. EventThread should then make a call to the
+ // interceptor, and the connection.
+ callback->onVSyncEvent(123);
+ expectInterceptCallReceived(123);
+ expectVsyncEventReceivedByConnection(123, 1u);
+
+ // A second event should go to the same places.
+ callback->onVSyncEvent(456);
+ expectInterceptCallReceived(456);
+ expectVsyncEventReceivedByConnection(456, 2u);
+
+ // A third event should go to the same places.
+ callback->onVSyncEvent(789);
+ expectInterceptCallReceived(789);
+ expectVsyncEventReceivedByConnection(789, 3u);
+}
+
+TEST_F(EventThreadTest, setVsyncRateTwoPostsEveryOtherEventToThatConnection) {
+ mThread->setVsyncRate(2, mConnection);
+
+ // EventThread should enable vsync callbacks, and set a callback interface
+ // pointer to use them with the VSync source.
+ expectVSyncSetEnabledCallReceived(true);
+ auto callback = expectVSyncSetCallbackCallReceived();
+ ASSERT_TRUE(callback);
+
+ // The first event will be seen by the interceptor, and not the connection.
+ callback->onVSyncEvent(123);
+ expectInterceptCallReceived(123);
+ EXPECT_FALSE(mConnectionEventCallRecorder.waitForUnexpectedCall().has_value());
+
+ // The second event will be seen by the interceptor and the connection.
+ callback->onVSyncEvent(456);
+ expectInterceptCallReceived(456);
+ expectVsyncEventReceivedByConnection(456, 2u);
+
+ // The third event will be seen by the interceptor, and not the connection.
+ callback->onVSyncEvent(789);
+ expectInterceptCallReceived(789);
+ EXPECT_FALSE(mConnectionEventCallRecorder.waitForUnexpectedCall().has_value());
+
+ // The fourth event will be seen by the interceptor and the connection.
+ callback->onVSyncEvent(101112);
+ expectInterceptCallReceived(101112);
+ expectVsyncEventReceivedByConnection(101112, 4u);
+}
+
+TEST_F(EventThreadTest, connectionsRemovedIfInstanceDestroyed) {
+ mThread->setVsyncRate(1, mConnection);
+
+ // EventThread should enable vsync callbacks, and set a callback interface
+ // pointer to use them with the VSync source.
+ expectVSyncSetEnabledCallReceived(true);
+ auto callback = expectVSyncSetCallbackCallReceived();
+ ASSERT_TRUE(callback);
+
+ // Destroy the only (strong) reference to the connection.
+ mConnection = nullptr;
+
+ // The first event will be seen by the interceptor, and not the connection.
+ callback->onVSyncEvent(123);
+ expectInterceptCallReceived(123);
+ EXPECT_FALSE(mConnectionEventCallRecorder.waitForUnexpectedCall().has_value());
+
+ // EventThread should disable vsync callbacks
+ expectVSyncSetEnabledCallReceived(false);
+}
+
+TEST_F(EventThreadTest, connectionsRemovedIfEventDeliveryError) {
+ ConnectionEventRecorder errorConnectionEventRecorder{NO_MEMORY};
+ sp<MockEventThreadConnection> errorConnection = createConnection(errorConnectionEventRecorder);
+ mThread->setVsyncRate(1, errorConnection);
+
+ // EventThread should enable vsync callbacks, and set a callback interface
+ // pointer to use them with the VSync source.
+ expectVSyncSetEnabledCallReceived(true);
+ auto callback = expectVSyncSetCallbackCallReceived();
+ ASSERT_TRUE(callback);
+
+ // The first event will be seen by the interceptor, and by the connection,
+ // which then returns an error.
+ callback->onVSyncEvent(123);
+ expectInterceptCallReceived(123);
+ expectVsyncEventReceivedByConnection("errorConnection", errorConnectionEventRecorder, 123, 1u);
+
+ // A subsequent event will be seen by the interceptor and not by the
+ // connection.
+ callback->onVSyncEvent(456);
+ expectInterceptCallReceived(456);
+ EXPECT_FALSE(errorConnectionEventRecorder.waitForUnexpectedCall().has_value());
+
+ // EventThread should disable vsync callbacks with the second event
+ expectVSyncSetEnabledCallReceived(false);
+}
+
+TEST_F(EventThreadTest, eventsDroppedIfNonfatalEventDeliveryError) {
+ ConnectionEventRecorder errorConnectionEventRecorder{WOULD_BLOCK};
+ sp<MockEventThreadConnection> errorConnection = createConnection(errorConnectionEventRecorder);
+ mThread->setVsyncRate(1, errorConnection);
+
+ // EventThread should enable vsync callbacks, and set a callback interface
+ // pointer to use them with the VSync source.
+ expectVSyncSetEnabledCallReceived(true);
+ auto callback = expectVSyncSetCallbackCallReceived();
+ ASSERT_TRUE(callback);
+
+ // The first event will be seen by the interceptor, and by the connection,
+ // which then returns an non-fatal error.
+ callback->onVSyncEvent(123);
+ expectInterceptCallReceived(123);
+ expectVsyncEventReceivedByConnection("errorConnection", errorConnectionEventRecorder, 123, 1u);
+
+ // A subsequent event will be seen by the interceptor, and by the connection,
+ // which still then returns an non-fatal error.
+ callback->onVSyncEvent(456);
+ expectInterceptCallReceived(456);
+ expectVsyncEventReceivedByConnection("errorConnection", errorConnectionEventRecorder, 456, 2u);
+
+ // EventThread will not disable vsync callbacks as the errors are non-fatal.
+ EXPECT_FALSE(mVSyncSetEnabledCallRecorder.waitForUnexpectedCall().has_value());
+}
+
+TEST_F(EventThreadTest, setPhaseOffsetForwardsToVSyncSource) {
+ mThread->setPhaseOffset(321);
+ expectVSyncSetPhaseOffsetCallReceived(321);
+}
+
+TEST_F(EventThreadTest, postHotplugPrimaryDisconnect) {
+ mThread->onHotplugReceived(DisplayDevice::DISPLAY_PRIMARY, false);
+ expectHotplugEventReceivedByConnection(DisplayDevice::DISPLAY_PRIMARY, false);
+}
+
+TEST_F(EventThreadTest, postHotplugPrimaryConnect) {
+ mThread->onHotplugReceived(DisplayDevice::DISPLAY_PRIMARY, true);
+ expectHotplugEventReceivedByConnection(DisplayDevice::DISPLAY_PRIMARY, true);
+}
+
+TEST_F(EventThreadTest, postHotplugExternalDisconnect) {
+ mThread->onHotplugReceived(DisplayDevice::DISPLAY_EXTERNAL, false);
+ expectHotplugEventReceivedByConnection(DisplayDevice::DISPLAY_EXTERNAL, false);
+}
+
+TEST_F(EventThreadTest, postHotplugExternalConnect) {
+ mThread->onHotplugReceived(DisplayDevice::DISPLAY_EXTERNAL, true);
+ expectHotplugEventReceivedByConnection(DisplayDevice::DISPLAY_EXTERNAL, true);
+}
+
+TEST_F(EventThreadTest, postHotplugVirtualDisconnectIsFilteredOut) {
+ mThread->onHotplugReceived(DisplayDevice::DISPLAY_VIRTUAL, false);
+ EXPECT_FALSE(mConnectionEventCallRecorder.waitForUnexpectedCall().has_value());
+}
+
+} // namespace
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index a4e7361..f1556d8 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -89,11 +89,19 @@
auto onInitializeDisplays() { return mFlinger->onInitializeDisplays(); }
+ auto setPowerModeInternal(const sp<DisplayDevice>& hw, int mode, bool stateLockHeld = false) {
+ return mFlinger->setPowerModeInternal(hw, mode, stateLockHeld);
+ }
+
/* ------------------------------------------------------------------------
* Read-only access to private data to assert post-conditions.
*/
const auto& getAnimFrameTracker() const { return mFlinger->mAnimFrameTracker; }
+ const auto& getHasPoweredOff() const { return mFlinger->mHasPoweredOff; }
+ const auto& getHWVsyncAvailable() const { return mFlinger->mHWVsyncAvailable; }
+ const auto& getVisibleRegionsDirty() const { return mFlinger->mVisibleRegionsDirty; }
+
const auto& getCompositorTiming() const { return mFlinger->getBE().mCompositorTiming; }
/* ------------------------------------------------------------------------