/* * Copyright 2020 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 #include #include "DisplayTransactionTestHelpers.h" #include #include using namespace com::android::graphics::surfaceflinger; namespace android { namespace { MATCHER_P(DisplayModeFps, value, "equals") { using fps_approx_ops::operator==; return arg->getVsyncRate() == value; } // Used when we simulate a display that supports doze. template 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; static void setupComposerCallExpectations(DisplayTransactionTest* test) { EXPECT_CALL(*test->mComposer, getDisplayCapabilities(Display::HWC_DISPLAY_ID, _)) .WillOnce(DoAll(SetArgPointee<1>( std::vector({DisplayCapability::DOZE})), Return(Error::NONE))); } }; template // 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; static void setupComposerCallExpectations(DisplayTransactionTest* test) { EXPECT_CALL(*test->mComposer, getDisplayCapabilities(Display::HWC_DISPLAY_ID, _)) .WillOnce(DoAll(SetArgPointee<1>(std::vector({})), Return(Error::NONE))); } }; struct EventThreadBaseSupportedVariant { static void setupVsyncNoCallExpectations(DisplayTransactionTest* test) { // Expect no change to hardware nor synthetic VSYNC. EXPECT_CALL(test->mFlinger.scheduler()->mockRequestHardwareVsync, Call(_, _)).Times(0); EXPECT_CALL(*test->mEventThread, enableSyntheticVsync(_)).Times(0); } }; struct EventThreadNotSupportedVariant : public EventThreadBaseSupportedVariant { static void setupEnableVsyncCallExpectations(DisplayTransactionTest* test) { EXPECT_CALL(test->mFlinger.scheduler()->mockRequestHardwareVsync, Call(_, true)).Times(1); EXPECT_CALL(*test->mEventThread, enableSyntheticVsync(_)).Times(0); } static void setupDisableVsyncCallExpectations(DisplayTransactionTest* test) { EXPECT_CALL(test->mFlinger.scheduler()->mockRequestHardwareVsync, Call(_, false)).Times(1); EXPECT_CALL(*test->mEventThread, enableSyntheticVsync(_)).Times(0); } }; struct EventThreadIsSupportedVariant : public EventThreadBaseSupportedVariant { static void setupEnableVsyncCallExpectations(DisplayTransactionTest* test) { // Expect to enable hardware VSYNC and disable synthetic VSYNC. EXPECT_CALL(test->mFlinger.scheduler()->mockRequestHardwareVsync, Call(_, true)).Times(1); EXPECT_CALL(*test->mEventThread, enableSyntheticVsync(false)).Times(1); } static void setupDisableVsyncCallExpectations(DisplayTransactionTest* test) { // Expect to disable hardware VSYNC and enable synthetic VSYNC. EXPECT_CALL(test->mFlinger.scheduler()->mockRequestHardwareVsync, Call(_, false)).Times(1); EXPECT_CALL(*test->mEventThread, enableSyntheticVsync(true)).Times(1); } }; struct DispSyncIsSupportedVariant { static void setupResetModelCallExpectations(DisplayTransactionTest* test) { auto vsyncSchedule = test->mFlinger.scheduler()->getVsyncSchedule(); EXPECT_CALL(static_cast(vsyncSchedule->getController()), onDisplayModeChanged(DisplayModeFps(Fps::fromPeriodNsecs(DEFAULT_VSYNC_PERIOD)), false)) .Times(1); EXPECT_CALL(static_cast(vsyncSchedule->getTracker()), resetModel()) .Times(1); } }; struct DispSyncNotSupportedVariant { static void setupResetModelCallExpectations(DisplayTransactionTest* /* test */) {} }; // -------------------------------------------------------------------- // 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 struct TransitionVariantCommon { static constexpr auto INITIAL_POWER_MODE = initialPowerMode; static constexpr auto TARGET_POWER_MODE = targetPowerMode; static void verifyPostconditions(DisplayTransactionTest*) {} }; struct TransitionOffToOnVariant : public TransitionVariantCommon { template static void setupCallExpectations(DisplayTransactionTest* test) { Case::setupComposerCallExpectations(test, IComposerClient::PowerMode::ON); Case::EventThread::setupEnableVsyncCallExpectations(test); Case::DispSync::setupResetModelCallExpectations(test); Case::setupRepaintEverythingCallExpectations(test); } static void verifyPostconditions(DisplayTransactionTest* test) { EXPECT_TRUE(test->mFlinger.getVisibleRegionsDirty()); } }; struct TransitionOffToDozeSuspendVariant : public TransitionVariantCommon { template static void setupCallExpectations(DisplayTransactionTest* test) { Case::setupComposerCallExpectations(test, Case::Doze::ACTUAL_POWER_MODE_FOR_DOZE_SUSPEND); Case::EventThread::setupVsyncNoCallExpectations(test); Case::setupRepaintEverythingCallExpectations(test); } static void verifyPostconditions(DisplayTransactionTest* test) { EXPECT_TRUE(test->mFlinger.getVisibleRegionsDirty()); } }; struct TransitionOnToOffVariant : public TransitionVariantCommon { template static void setupCallExpectations(DisplayTransactionTest* test) { Case::EventThread::setupDisableVsyncCallExpectations(test); Case::setupComposerCallExpectations(test, IComposerClient::PowerMode::OFF); } static void verifyPostconditions(DisplayTransactionTest* test) { EXPECT_TRUE(test->mFlinger.getVisibleRegionsDirty()); } }; struct TransitionDozeSuspendToOffVariant : public TransitionVariantCommon { template static void setupCallExpectations(DisplayTransactionTest* test) { Case::EventThread::setupVsyncNoCallExpectations(test); Case::setupComposerCallExpectations(test, IComposerClient::PowerMode::OFF); } static void verifyPostconditions(DisplayTransactionTest* test) { EXPECT_TRUE(test->mFlinger.getVisibleRegionsDirty()); } }; struct TransitionOnToDozeVariant : public TransitionVariantCommon { template static void setupCallExpectations(DisplayTransactionTest* test) { Case::EventThread::setupVsyncNoCallExpectations(test); Case::setupComposerCallExpectations(test, Case::Doze::ACTUAL_POWER_MODE_FOR_DOZE); } }; struct TransitionDozeSuspendToDozeVariant : public TransitionVariantCommon { template static void setupCallExpectations(DisplayTransactionTest* test) { Case::EventThread::setupEnableVsyncCallExpectations(test); Case::DispSync::setupResetModelCallExpectations(test); Case::setupComposerCallExpectations(test, Case::Doze::ACTUAL_POWER_MODE_FOR_DOZE); } }; struct TransitionDozeToOnVariant : public TransitionVariantCommon { template static void setupCallExpectations(DisplayTransactionTest* test) { Case::EventThread::setupVsyncNoCallExpectations(test); Case::setupComposerCallExpectations(test, IComposerClient::PowerMode::ON); } }; struct TransitionDozeSuspendToOnVariant : public TransitionVariantCommon { template static void setupCallExpectations(DisplayTransactionTest* test) { Case::EventThread::setupEnableVsyncCallExpectations(test); Case::DispSync::setupResetModelCallExpectations(test); Case::setupComposerCallExpectations(test, IComposerClient::PowerMode::ON); } }; struct TransitionOnToDozeSuspendVariant : public TransitionVariantCommon { template static void setupCallExpectations(DisplayTransactionTest* test) { Case::EventThread::setupDisableVsyncCallExpectations(test); Case::setupComposerCallExpectations(test, Case::Doze::ACTUAL_POWER_MODE_FOR_DOZE_SUSPEND); } }; struct TransitionOnToUnknownVariant : public TransitionVariantCommon(POWER_MODE_LEET)> { template static void setupCallExpectations(DisplayTransactionTest* test) { Case::EventThread::setupVsyncNoCallExpectations(test); Case::setupNoComposerPowerModeCallExpectations(test); } }; // -------------------------------------------------------------------- // Note: // // Rather than testing the cartesian product of // DozeIsSupported/DozeNotSupported with all other options, we use one for one // display type, and the other for another display type. // -------------------------------------------------------------------- template struct DisplayPowerCase { using Display = DisplayVariant; using Doze = DozeVariant; using EventThread = EventThreadVariant; using DispSync = DispSyncVariant; using Transition = TransitionVariant; static sp injectDisplayWithInitialPowerMode(DisplayTransactionTest* test, PowerMode mode) { Display::injectHwcDisplayWithNoDefaultCapabilities(test); auto injector = Display::makeFakeExistingDisplayInjector(test); const auto display = injector.inject(); display->setPowerMode(mode); return display; } static void setInitialHwVsyncEnabled(DisplayTransactionTest* test, PhysicalDisplayId id, bool enabled) { test->mFlinger.scheduler()->setInitialHwVsyncEnabled(id, enabled); } static void setupRepaintEverythingCallExpectations(DisplayTransactionTest* test) { EXPECT_CALL(*test->mFlinger.scheduler(), scheduleFrame(_)).Times(1); } static void setupComposerCallExpectations(DisplayTransactionTest* test, 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 using PrimaryDisplayPowerCase = DisplayPowerCase, EventThreadIsSupportedVariant, DispSyncIsSupportedVariant, TransitionVariant>; // A sample configuration for the external display. // In addition to not having event thread support, we emulate not having doze // support. // TODO (b/267483230): ExternalDisplay supports the features tracked in // DispSyncIsSupportedVariant, but is the follower, so the // expectations set by DispSyncIsSupportedVariant don't match (wrong schedule). // We need a way to retrieve the proper DisplayId from // setupResetModelCallExpectations (or pass it in). template using ExternalDisplayPowerCase = DisplayPowerCase, EventThreadNotSupportedVariant, DispSyncNotSupportedVariant, TransitionVariant>; class SetPhysicalDisplayPowerModeTest : public DisplayTransactionTest { public: template void transitionDisplayCommon(); }; template struct PowerModeInitialVSyncEnabled : public std::false_type {}; template <> struct PowerModeInitialVSyncEnabled : public std::true_type {}; template <> struct PowerModeInitialVSyncEnabled : public std::true_type {}; template void SetPhysicalDisplayPowerModeTest::transitionDisplayCommon() { // -------------------------------------------------------------------- // Preconditions Case::Doze::setupComposerCallExpectations(this); auto display = Case::injectDisplayWithInitialPowerMode(this, Case::Transition::INITIAL_POWER_MODE); if (auto physicalDisplayId = asPhysicalDisplayId(display->getDisplayIdVariant())) { Case::setInitialHwVsyncEnabled(this, *physicalDisplayId, PowerModeInitialVSyncEnabled< Case::Transition::INITIAL_POWER_MODE>::value); } // -------------------------------------------------------------------- // Call Expectations Case::Transition::template setupCallExpectations(this); // -------------------------------------------------------------------- // Invocation mFlinger.setPhysicalDisplayPowerMode(display, Case::Transition::TARGET_POWER_MODE); // -------------------------------------------------------------------- // Postconditions Case::Transition::verifyPostconditions(this); } TEST_F(SetPhysicalDisplayPowerModeTest, setPhysicalDisplayPowerModeDoesNothingIfNoChange) { using Case = SimplePrimaryDisplayCase; // -------------------------------------------------------------------- // Preconditions // A primary display device is set up Case::Display::injectHwcDisplay(this); auto display = Case::Display::makeFakeExistingDisplayInjector(this); display.inject(); // The display is already set to PowerMode::ON display.mutableDisplayDevice()->setPowerMode(PowerMode::ON); // -------------------------------------------------------------------- // Invocation mFlinger.setPhysicalDisplayPowerMode(display.mutableDisplayDevice(), PowerMode::ON); // -------------------------------------------------------------------- // Postconditions EXPECT_EQ(PowerMode::ON, display.mutableDisplayDevice()->getPowerMode()); } TEST_F(SetPhysicalDisplayPowerModeTest, setPhysicalDisplayPowerModeDoesNothingIfVirtualDisplay) { using Case = HwcVirtualDisplayCase; // -------------------------------------------------------------------- // Preconditions // Insert display data so that the HWC thinks it created the virtual display. const auto displayId = asHalDisplayId(Case::Display::DISPLAY_ID::get()); ASSERT_TRUE(displayId); ASSERT_TRUE(mFlinger.mutableHwcDisplayData().try_emplace(*displayId).second); // A virtual display device is set up Case::Display::injectHwcDisplay(this); auto display = Case::Display::makeFakeExistingDisplayInjector(this); display.inject(); // The display is set to PowerMode::ON display.mutableDisplayDevice()->setPowerMode(PowerMode::ON); // -------------------------------------------------------------------- // Invocation mFlinger.setPhysicalDisplayPowerMode(display.mutableDisplayDevice(), PowerMode::OFF); // -------------------------------------------------------------------- // Postconditions EXPECT_EQ(PowerMode::ON, display.mutableDisplayDevice()->getPowerMode()); } TEST_F(SetPhysicalDisplayPowerModeTest, transitionsDisplayFromOffToOnPrimaryDisplay) { transitionDisplayCommon>(); } TEST_F(SetPhysicalDisplayPowerModeTest, transitionsDisplayFromOffToDozeSuspendPrimaryDisplay) { transitionDisplayCommon>(); } TEST_F(SetPhysicalDisplayPowerModeTest, transitionsDisplayFromOnToOffPrimaryDisplay) { transitionDisplayCommon>(); } TEST_F(SetPhysicalDisplayPowerModeTest, transitionsDisplayFromDozeSuspendToOffPrimaryDisplay) { transitionDisplayCommon>(); } TEST_F(SetPhysicalDisplayPowerModeTest, transitionsDisplayFromOnToDozePrimaryDisplay) { transitionDisplayCommon>(); } TEST_F(SetPhysicalDisplayPowerModeTest, transitionsDisplayFromDozeSuspendToDozePrimaryDisplay) { transitionDisplayCommon>(); } TEST_F(SetPhysicalDisplayPowerModeTest, transitionsDisplayFromDozeToOnPrimaryDisplay) { transitionDisplayCommon>(); } TEST_F(SetPhysicalDisplayPowerModeTest, transitionsDisplayFromDozeSuspendToOnPrimaryDisplay) { transitionDisplayCommon>(); } TEST_F(SetPhysicalDisplayPowerModeTest, transitionsDisplayFromOnToDozeSuspendPrimaryDisplay) { transitionDisplayCommon>(); } TEST_F(SetPhysicalDisplayPowerModeTest, transitionsDisplayFromOnToUnknownPrimaryDisplay) { transitionDisplayCommon>(); } TEST_F(SetPhysicalDisplayPowerModeTest, transitionsDisplayFromOffToOnExternalDisplay) { transitionDisplayCommon>(); } TEST_F(SetPhysicalDisplayPowerModeTest, transitionsDisplayFromOffToDozeSuspendExternalDisplay) { transitionDisplayCommon>(); } TEST_F(SetPhysicalDisplayPowerModeTest, transitionsDisplayFromOnToOffExternalDisplay) { transitionDisplayCommon>(); } TEST_F(SetPhysicalDisplayPowerModeTest, transitionsDisplayFromDozeSuspendToOffExternalDisplay) { transitionDisplayCommon>(); } TEST_F(SetPhysicalDisplayPowerModeTest, transitionsDisplayFromOnToDozeExternalDisplay) { transitionDisplayCommon>(); } TEST_F(SetPhysicalDisplayPowerModeTest, transitionsDisplayFromDozeSuspendToDozeExternalDisplay) { transitionDisplayCommon>(); } TEST_F(SetPhysicalDisplayPowerModeTest, transitionsDisplayFromDozeToOnExternalDisplay) { transitionDisplayCommon>(); } TEST_F(SetPhysicalDisplayPowerModeTest, transitionsDisplayFromDozeSuspendToOnExternalDisplay) { transitionDisplayCommon>(); } TEST_F(SetPhysicalDisplayPowerModeTest, transitionsDisplayFromOnToDozeSuspendExternalDisplay) { transitionDisplayCommon>(); } TEST_F(SetPhysicalDisplayPowerModeTest, transitionsDisplayFromOnToUnknownExternalDisplay) { transitionDisplayCommon>(); } } // namespace } // namespace android