Add additional tests for PowerAdvisor

Adds additional tests for PowerAdvisor functionality regarding
timing and multidisplay

Bug: b/241465879
Test: atest
libsurfaceflinger_unittest:libsurfaceflinger_unittest.PowerAdvisorTest

Change-Id: I833e91efb5608d5972220c679a061c9bb51372fa
diff --git a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
index 4018c6b..a0350b7 100644
--- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
+++ b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
@@ -838,10 +838,7 @@
         base::GetBoolProperty(std::string("debug.sf.trace_hint_sessions"), false);
 
 PowerAdvisor::HalWrapper* PowerAdvisor::getPowerHal() {
-    static std::unique_ptr<HalWrapper> sHalWrapper = nullptr;
-    static bool sHasHal = true;
-
-    if (!sHasHal) {
+    if (!mHasHal) {
         return nullptr;
     }
 
@@ -849,57 +846,57 @@
     std::vector<int32_t> oldPowerHintSessionThreadIds;
     std::optional<int64_t> oldTargetWorkDuration;
 
-    if (sHalWrapper != nullptr) {
-        oldPowerHintSessionThreadIds = sHalWrapper->getPowerHintSessionThreadIds();
-        oldTargetWorkDuration = sHalWrapper->getTargetWorkDuration();
+    if (mHalWrapper != nullptr) {
+        oldPowerHintSessionThreadIds = mHalWrapper->getPowerHintSessionThreadIds();
+        oldTargetWorkDuration = mHalWrapper->getTargetWorkDuration();
     }
 
     // If we used to have a HAL, but it stopped responding, attempt to reconnect
     if (mReconnectPowerHal) {
-        sHalWrapper = nullptr;
+        mHalWrapper = nullptr;
         mReconnectPowerHal = false;
     }
 
-    if (sHalWrapper != nullptr) {
-        auto wrapper = sHalWrapper.get();
+    if (mHalWrapper != nullptr) {
+        auto wrapper = mHalWrapper.get();
         // If the wrapper is fine, return it, but if it indicates a reconnect, remake it
         if (!wrapper->shouldReconnectHAL()) {
             return wrapper;
         }
         ALOGD("Reconnecting Power HAL");
-        sHalWrapper = nullptr;
+        mHalWrapper = nullptr;
     }
 
     // At this point, we know for sure there is no running session
     mPowerHintSessionRunning = false;
 
     // First attempt to connect to the AIDL Power HAL
-    sHalWrapper = AidlPowerHalWrapper::connect();
+    mHalWrapper = AidlPowerHalWrapper::connect();
 
     // If that didn't succeed, attempt to connect to the HIDL Power HAL
-    if (sHalWrapper == nullptr) {
-        sHalWrapper = HidlPowerHalWrapper::connect();
+    if (mHalWrapper == nullptr) {
+        mHalWrapper = HidlPowerHalWrapper::connect();
     } else {
         ALOGD("Successfully connecting AIDL Power HAL");
         // If AIDL, pass on any existing hint session values
-        sHalWrapper->setPowerHintSessionThreadIds(oldPowerHintSessionThreadIds);
+        mHalWrapper->setPowerHintSessionThreadIds(oldPowerHintSessionThreadIds);
         // Only set duration and start if duration is defined
         if (oldTargetWorkDuration.has_value()) {
-            sHalWrapper->setTargetWorkDuration(*oldTargetWorkDuration);
+            mHalWrapper->setTargetWorkDuration(*oldTargetWorkDuration);
             // Only start if possible to run and both threadids and duration are defined
             if (usePowerHintSession() && !oldPowerHintSessionThreadIds.empty()) {
-                mPowerHintSessionRunning = sHalWrapper->startPowerHintSession();
+                mPowerHintSessionRunning = mHalWrapper->startPowerHintSession();
             }
         }
     }
 
     // If we make it to this point and still don't have a HAL, it's unlikely we
     // will, so stop trying
-    if (sHalWrapper == nullptr) {
-        sHasHal = false;
+    if (mHalWrapper == nullptr) {
+        mHasHal = false;
     }
 
-    return sHalWrapper.get();
+    return mHalWrapper.get();
 }
 
 } // namespace impl
diff --git a/services/surfaceflinger/DisplayHardware/PowerAdvisor.h b/services/surfaceflinger/DisplayHardware/PowerAdvisor.h
index 71a1f8c..6e25f78 100644
--- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.h
+++ b/services/surfaceflinger/DisplayHardware/PowerAdvisor.h
@@ -154,6 +154,13 @@
     void setTotalFrameTargetWorkDuration(nsecs_t targetDuration) override;
 
 private:
+    friend class PowerAdvisorTest;
+
+    // Tracks if powerhal exists
+    bool mHasHal = true;
+    // Holds the hal wrapper for getPowerHal
+    std::unique_ptr<HalWrapper> mHalWrapper GUARDED_BY(mPowerHalMutex) = nullptr;
+
     HalWrapper* getPowerHal() REQUIRES(mPowerHalMutex);
     bool mReconnectPowerHal GUARDED_BY(mPowerHalMutex) = false;
     std::mutex mPowerHalMutex;
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index 7823363..004f31c 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -24,6 +24,7 @@
 filegroup {
     name: "libsurfaceflinger_mock_sources",
     srcs: [
+        "mock/DisplayHardware/MockAidlPowerHalWrapper.cpp",
         "mock/DisplayHardware/MockComposer.cpp",
         "mock/DisplayHardware/MockHWC2.cpp",
         "mock/DisplayHardware/MockIPower.cpp",
@@ -95,6 +96,7 @@
         "LayerTest.cpp",
         "LayerTestUtils.cpp",
         "MessageQueueTest.cpp",
+        "PowerAdvisorTest.cpp",
         "SurfaceFlinger_CreateDisplayTest.cpp",
         "SurfaceFlinger_DestroyDisplayTest.cpp",
         "SurfaceFlinger_DisplayModeSwitching.cpp",
diff --git a/services/surfaceflinger/tests/unittests/PowerAdvisorTest.cpp b/services/surfaceflinger/tests/unittests/PowerAdvisorTest.cpp
new file mode 100644
index 0000000..8711a42
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/PowerAdvisorTest.cpp
@@ -0,0 +1,203 @@
+/*
+ * Copyright 2022 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 "PowerAdvisorTest"
+
+#include <DisplayHardware/PowerAdvisor.h>
+#include <compositionengine/Display.h>
+#include <ftl/fake_guard.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <ui/DisplayId.h>
+#include <chrono>
+#include "TestableSurfaceFlinger.h"
+#include "mock/DisplayHardware/MockAidlPowerHalWrapper.h"
+
+using namespace android;
+using namespace android::Hwc2::mock;
+using namespace android::hardware::power;
+using namespace std::chrono_literals;
+using namespace testing;
+
+namespace android::Hwc2::impl {
+
+class PowerAdvisorTest : public testing::Test {
+public:
+    void SetUp() override;
+    void startPowerHintSession();
+    void fakeBasicFrameTiming(nsecs_t startTime, nsecs_t vsyncPeriod);
+    void setExpectedTiming(nsecs_t startTime, nsecs_t vsyncPeriod);
+    nsecs_t getFenceWaitDelayDuration(bool skipValidate);
+
+protected:
+    TestableSurfaceFlinger mFlinger;
+    std::unique_ptr<PowerAdvisor> mPowerAdvisor;
+    NiceMock<MockAidlPowerHalWrapper>* mMockAidlWrapper;
+    nsecs_t kErrorMargin = std::chrono::nanoseconds(1ms).count();
+};
+
+void PowerAdvisorTest::SetUp() FTL_FAKE_GUARD(mPowerAdvisor->mPowerHalMutex) {
+    std::unique_ptr<MockAidlPowerHalWrapper> mockAidlWrapper =
+            std::make_unique<NiceMock<MockAidlPowerHalWrapper>>();
+    mPowerAdvisor = std::make_unique<PowerAdvisor>(*mFlinger.flinger());
+    ON_CALL(*mockAidlWrapper.get(), supportsPowerHintSession()).WillByDefault(Return(true));
+    ON_CALL(*mockAidlWrapper.get(), startPowerHintSession()).WillByDefault(Return(true));
+    mPowerAdvisor->mHalWrapper = std::move(mockAidlWrapper);
+    mMockAidlWrapper =
+            reinterpret_cast<NiceMock<MockAidlPowerHalWrapper>*>(mPowerAdvisor->mHalWrapper.get());
+}
+
+void PowerAdvisorTest::startPowerHintSession() {
+    const std::vector<int32_t> threadIds = {1, 2, 3};
+    mPowerAdvisor->enablePowerHint(true);
+    mPowerAdvisor->startPowerHintSession(threadIds);
+}
+
+void PowerAdvisorTest::setExpectedTiming(nsecs_t totalFrameTarget, nsecs_t expectedPresentTime) {
+    mPowerAdvisor->setTotalFrameTargetWorkDuration(totalFrameTarget);
+    mPowerAdvisor->setExpectedPresentTime(expectedPresentTime);
+}
+
+void PowerAdvisorTest::fakeBasicFrameTiming(nsecs_t startTime, nsecs_t vsyncPeriod) {
+    mPowerAdvisor->setCommitStart(startTime);
+    mPowerAdvisor->setFrameDelay(0);
+    mPowerAdvisor->setTargetWorkDuration(vsyncPeriod);
+}
+
+nsecs_t PowerAdvisorTest::getFenceWaitDelayDuration(bool skipValidate) {
+    return (skipValidate ? PowerAdvisor::kFenceWaitStartDelaySkippedValidate
+                         : PowerAdvisor::kFenceWaitStartDelayValidated)
+            .count();
+}
+
+namespace {
+
+TEST_F(PowerAdvisorTest, hintSessionUseHwcDisplay) {
+    mPowerAdvisor->onBootFinished();
+    startPowerHintSession();
+
+    std::vector<DisplayId> displayIds{PhysicalDisplayId::fromPort(42u)};
+
+    // 60hz
+    const nsecs_t vsyncPeriod = std::chrono::nanoseconds(1s).count() / 60;
+    const nsecs_t presentDuration = std::chrono::nanoseconds(5ms).count();
+    const nsecs_t postCompDuration = std::chrono::nanoseconds(1ms).count();
+
+    nsecs_t startTime = 100;
+
+    // advisor only starts on frame 2 so do an initial no-op frame
+    fakeBasicFrameTiming(startTime, vsyncPeriod);
+    setExpectedTiming(vsyncPeriod, startTime + vsyncPeriod);
+    mPowerAdvisor->setDisplays(displayIds);
+    mPowerAdvisor->setSfPresentTiming(startTime, startTime + presentDuration);
+    mPowerAdvisor->setCompositeEnd(startTime + presentDuration + postCompDuration);
+
+    // increment the frame
+    startTime += vsyncPeriod;
+
+    const nsecs_t expectedDuration = kErrorMargin + presentDuration + postCompDuration;
+    EXPECT_CALL(*mMockAidlWrapper, sendActualWorkDuration(Eq(expectedDuration), _)).Times(1);
+
+    fakeBasicFrameTiming(startTime, vsyncPeriod);
+    setExpectedTiming(vsyncPeriod, startTime + vsyncPeriod);
+    mPowerAdvisor->setDisplays(displayIds);
+    mPowerAdvisor->setHwcValidateTiming(displayIds[0], startTime + 1000000, startTime + 1500000);
+    mPowerAdvisor->setHwcPresentTiming(displayIds[0], startTime + 2000000, startTime + 2500000);
+    mPowerAdvisor->setSfPresentTiming(startTime, startTime + presentDuration);
+    mPowerAdvisor->sendActualWorkDuration();
+}
+
+TEST_F(PowerAdvisorTest, hintSessionSubtractsHwcFenceTime) {
+    mPowerAdvisor->onBootFinished();
+    startPowerHintSession();
+
+    std::vector<DisplayId> displayIds{PhysicalDisplayId::fromPort(42u)};
+
+    // 60hz
+    const nsecs_t vsyncPeriod = std::chrono::nanoseconds(1s).count() / 60;
+    const nsecs_t presentDuration = std::chrono::nanoseconds(5ms).count();
+    const nsecs_t postCompDuration = std::chrono::nanoseconds(1ms).count();
+    const nsecs_t hwcBlockedDuration = std::chrono::nanoseconds(500us).count();
+
+    nsecs_t startTime = 100;
+
+    // advisor only starts on frame 2 so do an initial no-op frame
+    fakeBasicFrameTiming(startTime, vsyncPeriod);
+    setExpectedTiming(vsyncPeriod, startTime + vsyncPeriod);
+    mPowerAdvisor->setDisplays(displayIds);
+    mPowerAdvisor->setSfPresentTiming(startTime, startTime + presentDuration);
+    mPowerAdvisor->setCompositeEnd(startTime + presentDuration + postCompDuration);
+
+    // increment the frame
+    startTime += vsyncPeriod;
+
+    const nsecs_t expectedDuration = kErrorMargin + presentDuration +
+            getFenceWaitDelayDuration(false) - hwcBlockedDuration + postCompDuration;
+    EXPECT_CALL(*mMockAidlWrapper, sendActualWorkDuration(Eq(expectedDuration), _)).Times(1);
+
+    fakeBasicFrameTiming(startTime, vsyncPeriod);
+    setExpectedTiming(vsyncPeriod, startTime + vsyncPeriod);
+    mPowerAdvisor->setDisplays(displayIds);
+    mPowerAdvisor->setHwcValidateTiming(displayIds[0], startTime + 1000000, startTime + 1500000);
+    mPowerAdvisor->setHwcPresentTiming(displayIds[0], startTime + 2000000, startTime + 3000000);
+    // now report the fence as having fired during the display HWC time
+    mPowerAdvisor->setSfPresentTiming(startTime + 2000000 + hwcBlockedDuration,
+                                      startTime + presentDuration);
+    mPowerAdvisor->sendActualWorkDuration();
+}
+
+TEST_F(PowerAdvisorTest, hintSessionUsingSecondaryVirtualDisplays) {
+    mPowerAdvisor->onBootFinished();
+    startPowerHintSession();
+
+    std::vector<DisplayId> displayIds{PhysicalDisplayId::fromPort(42u), GpuVirtualDisplayId(0),
+                                      GpuVirtualDisplayId(1)};
+
+    // 60hz
+    const nsecs_t vsyncPeriod = std::chrono::nanoseconds(1s).count() / 60;
+    // make present duration much later than the hwc display by itself will account for
+    const nsecs_t presentDuration = std::chrono::nanoseconds(10ms).count();
+    const nsecs_t postCompDuration = std::chrono::nanoseconds(1ms).count();
+
+    nsecs_t startTime = 100;
+
+    // advisor only starts on frame 2 so do an initial no-op frame
+    fakeBasicFrameTiming(startTime, vsyncPeriod);
+    setExpectedTiming(vsyncPeriod, startTime + vsyncPeriod);
+    mPowerAdvisor->setDisplays(displayIds);
+    mPowerAdvisor->setSfPresentTiming(startTime, startTime + presentDuration);
+    mPowerAdvisor->setCompositeEnd(startTime + presentDuration + postCompDuration);
+
+    // increment the frame
+    startTime += vsyncPeriod;
+
+    const nsecs_t expectedDuration = kErrorMargin + presentDuration + postCompDuration;
+    EXPECT_CALL(*mMockAidlWrapper, sendActualWorkDuration(Eq(expectedDuration), _)).Times(1);
+
+    fakeBasicFrameTiming(startTime, vsyncPeriod);
+    setExpectedTiming(vsyncPeriod, startTime + vsyncPeriod);
+    mPowerAdvisor->setDisplays(displayIds);
+
+    // don't report timing for the gpu displays since they don't use hwc
+    mPowerAdvisor->setHwcValidateTiming(displayIds[0], startTime + 1000000, startTime + 1500000);
+    mPowerAdvisor->setHwcPresentTiming(displayIds[0], startTime + 2000000, startTime + 2500000);
+    mPowerAdvisor->setSfPresentTiming(startTime, startTime + presentDuration);
+    mPowerAdvisor->sendActualWorkDuration();
+}
+
+} // namespace
+} // namespace android::Hwc2::impl
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockAidlPowerHalWrapper.cpp b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockAidlPowerHalWrapper.cpp
new file mode 100644
index 0000000..5049b1d
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockAidlPowerHalWrapper.cpp
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+#include "MockAidlPowerHalWrapper.h"
+#include "MockIPower.h"
+
+namespace android::Hwc2::mock {
+
+MockAidlPowerHalWrapper::MockAidlPowerHalWrapper()
+      : AidlPowerHalWrapper(sp<testing::NiceMock<MockIPower>>::make()){};
+MockAidlPowerHalWrapper::~MockAidlPowerHalWrapper() = default;
+
+} // namespace android::Hwc2::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockAidlPowerHalWrapper.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockAidlPowerHalWrapper.h
new file mode 100644
index 0000000..657ced3
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockAidlPowerHalWrapper.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2022 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 <gmock/gmock.h>
+
+#include "DisplayHardware/PowerAdvisor.h"
+
+namespace android {
+namespace hardware {
+namespace power {
+class IPower;
+}
+} // namespace hardware
+} // namespace android
+
+namespace android::Hwc2::mock {
+
+class MockAidlPowerHalWrapper : public Hwc2::impl::AidlPowerHalWrapper {
+public:
+    MockAidlPowerHalWrapper();
+    ~MockAidlPowerHalWrapper() override;
+    MOCK_METHOD(bool, setExpensiveRendering, (bool enabled), (override));
+    MOCK_METHOD(bool, notifyDisplayUpdateImminent, (), (override));
+    MOCK_METHOD(bool, supportsPowerHintSession, (), (override));
+    MOCK_METHOD(bool, isPowerHintSessionRunning, (), (override));
+    MOCK_METHOD(void, restartPowerHintSession, (), (override));
+    MOCK_METHOD(void, setPowerHintSessionThreadIds, (const std::vector<int32_t>& threadIds),
+                (override));
+    MOCK_METHOD(bool, startPowerHintSession, (), (override));
+    MOCK_METHOD(void, setTargetWorkDuration, (nsecs_t targetDuration), (override));
+    MOCK_METHOD(void, sendActualWorkDuration, (nsecs_t actualDuration, nsecs_t timestamp),
+                (override));
+    MOCK_METHOD(bool, shouldReconnectHAL, (), (override));
+};
+
+} // namespace android::Hwc2::mock
\ No newline at end of file