Add unit test for AidlPowerHalWrapper

Bug: b/195990840
Test: atest libsurfaceflinger_unittest:libsurfaceflinger_unittest.AidlPowerHalWrapperTest
Change-Id: I5b2197904445a83e5d586aa6331ccc3d46db22a3
diff --git a/services/surfaceflinger/tests/fakehwc/Android.bp b/services/surfaceflinger/tests/fakehwc/Android.bp
index 168b576..704815d 100644
--- a/services/surfaceflinger/tests/fakehwc/Android.bp
+++ b/services/surfaceflinger/tests/fakehwc/Android.bp
@@ -28,6 +28,7 @@
         "android.hardware.graphics.mapper@3.0",
         "android.hardware.graphics.mapper@4.0",
         "android.hardware.power@1.3",
+        "android.hardware.power-V2-cpp",
         "libbase",
         "libbinder",
         "libbinder_ndk",
diff --git a/services/surfaceflinger/tests/unittests/AidlPowerHalWrapperTest.cpp b/services/surfaceflinger/tests/unittests/AidlPowerHalWrapperTest.cpp
new file mode 100644
index 0000000..e25a0ae
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/AidlPowerHalWrapperTest.cpp
@@ -0,0 +1,266 @@
+/*
+ * 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 "AidlPowerHalWrapperTest"
+
+#include <android/hardware/power/IPower.h>
+#include <android/hardware/power/IPowerHintSession.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <algorithm>
+#include <chrono>
+#include <memory>
+#include "DisplayHardware/PowerAdvisor.h"
+#include "android/hardware/power/WorkDuration.h"
+#include "binder/Status.h"
+#include "log/log_main.h"
+#include "mock/DisplayHardware/MockIPower.h"
+#include "mock/DisplayHardware/MockIPowerHintSession.h"
+#include "utils/Timers.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 AidlPowerHalWrapperTest : public testing::Test {
+public:
+    void SetUp() override;
+
+protected:
+    std::unique_ptr<AidlPowerHalWrapper> mWrapper = nullptr;
+    sp<NiceMock<MockIPower>> mMockHal = nullptr;
+    sp<NiceMock<MockIPowerHintSession>> mMockSession = nullptr;
+    void verifyAndClearExpectations();
+    void sendActualWorkDurationGroup(std::vector<WorkDuration> durations,
+                                     std::chrono::nanoseconds sleepBeforeLastSend);
+};
+
+void AidlPowerHalWrapperTest::SetUp() {
+    mMockHal = new NiceMock<MockIPower>();
+    mMockSession = new NiceMock<MockIPowerHintSession>();
+    ON_CALL(*mMockHal.get(), getHintSessionPreferredRate(_)).WillByDefault(Return(Status::ok()));
+    mWrapper = std::make_unique<AidlPowerHalWrapper>(mMockHal);
+}
+
+void AidlPowerHalWrapperTest::verifyAndClearExpectations() {
+    Mock::VerifyAndClearExpectations(mMockHal.get());
+    Mock::VerifyAndClearExpectations(mMockSession.get());
+}
+
+void AidlPowerHalWrapperTest::sendActualWorkDurationGroup(
+        std::vector<WorkDuration> durations, std::chrono::nanoseconds sleepBeforeLastSend) {
+    for (size_t i = 0; i < durations.size(); i++) {
+        if (i == durations.size() - 1) {
+            std::this_thread::sleep_for(sleepBeforeLastSend);
+        }
+        auto duration = durations[i];
+        mWrapper->sendActualWorkDuration(duration.durationNanos, duration.timeStampNanos);
+    }
+}
+WorkDuration toWorkDuration(std::chrono::nanoseconds durationNanos, int64_t timeStampNanos) {
+    WorkDuration duration;
+    duration.durationNanos = durationNanos.count();
+    duration.timeStampNanos = timeStampNanos;
+    return duration;
+}
+
+namespace {
+TEST_F(AidlPowerHalWrapperTest, supportsPowerHintSession) {
+    ASSERT_TRUE(mWrapper->supportsPowerHintSession());
+    Mock::VerifyAndClearExpectations(mMockHal.get());
+    ON_CALL(*mMockHal.get(), getHintSessionPreferredRate(_))
+            .WillByDefault(Return(Status::fromExceptionCode(Status::Exception::EX_ILLEGAL_STATE)));
+    auto newWrapper = AidlPowerHalWrapper(mMockHal);
+    EXPECT_FALSE(newWrapper.supportsPowerHintSession());
+}
+
+TEST_F(AidlPowerHalWrapperTest, startPowerHintSession) {
+    ASSERT_TRUE(mWrapper->supportsPowerHintSession());
+    std::vector<int32_t> threadIds = {1, 2};
+    mWrapper->setPowerHintSessionThreadIds(threadIds);
+    EXPECT_CALL(*mMockHal.get(), createHintSession(_, _, threadIds, _, _))
+            .WillOnce(DoAll(SetArgPointee<4>(mMockSession), Return(Status::ok())));
+    EXPECT_TRUE(mWrapper->startPowerHintSession());
+    EXPECT_FALSE(mWrapper->startPowerHintSession());
+}
+
+TEST_F(AidlPowerHalWrapperTest, restartNewPoserHintSessionWithNewThreadIds) {
+    ASSERT_TRUE(mWrapper->supportsPowerHintSession());
+
+    std::vector<int32_t> threadIds = {1, 2};
+    EXPECT_CALL(*mMockHal.get(), createHintSession(_, _, threadIds, _, _))
+            .WillOnce(DoAll(SetArgPointee<4>(mMockSession), Return(Status::ok())));
+    mWrapper->setPowerHintSessionThreadIds(threadIds);
+    EXPECT_EQ(mWrapper->getPowerHintSessionThreadIds(), threadIds);
+    ASSERT_TRUE(mWrapper->startPowerHintSession());
+    verifyAndClearExpectations();
+
+    threadIds = {2, 3};
+    EXPECT_CALL(*mMockHal.get(), createHintSession(_, _, threadIds, _, _))
+            .WillOnce(DoAll(SetArgPointee<4>(mMockSession), Return(Status::ok())));
+    EXPECT_CALL(*mMockSession.get(), close()).Times(1);
+    mWrapper->setPowerHintSessionThreadIds(threadIds);
+    EXPECT_EQ(mWrapper->getPowerHintSessionThreadIds(), threadIds);
+    verifyAndClearExpectations();
+
+    EXPECT_CALL(*mMockHal.get(), createHintSession(_, _, threadIds, _, _)).Times(0);
+    EXPECT_CALL(*mMockSession.get(), close()).Times(0);
+    mWrapper->setPowerHintSessionThreadIds(threadIds);
+    verifyAndClearExpectations();
+}
+
+TEST_F(AidlPowerHalWrapperTest, setTargetWorkDuration) {
+    ASSERT_TRUE(mWrapper->supportsPowerHintSession());
+
+    std::vector<int32_t> threadIds = {1, 2};
+    mWrapper->setPowerHintSessionThreadIds(threadIds);
+    EXPECT_CALL(*mMockHal.get(), createHintSession(_, _, threadIds, _, _))
+            .WillOnce(DoAll(SetArgPointee<4>(mMockSession), Return(Status::ok())));
+    ASSERT_TRUE(mWrapper->startPowerHintSession());
+    verifyAndClearExpectations();
+
+    std::chrono::nanoseconds base = 100ms;
+    // test cases with target work duration and whether it should update hint against baseline 100ms
+    const std::vector<std::pair<std::chrono::nanoseconds, bool>> testCases = {{0ms, false},
+                                                                              {-1ms, false},
+                                                                              {200ms, true},
+                                                                              {2ms, true},
+                                                                              {96ms, false},
+                                                                              {104ms, false}};
+
+    for (const auto& test : testCases) {
+        // reset to 100ms baseline
+        mWrapper->setTargetWorkDuration(1);
+        mWrapper->setTargetWorkDuration(base.count());
+
+        auto target = test.first;
+        EXPECT_CALL(*mMockSession.get(), updateTargetWorkDuration(target.count()))
+                .Times(test.second ? 1 : 0);
+        mWrapper->setTargetWorkDuration(target.count());
+        verifyAndClearExpectations();
+    }
+}
+
+TEST_F(AidlPowerHalWrapperTest, setTargetWorkDuration_shouldReconnectOnError) {
+    ASSERT_TRUE(mWrapper->supportsPowerHintSession());
+
+    std::vector<int32_t> threadIds = {1, 2};
+    mWrapper->setPowerHintSessionThreadIds(threadIds);
+    EXPECT_CALL(*mMockHal.get(), createHintSession(_, _, threadIds, _, _))
+            .WillOnce(DoAll(SetArgPointee<4>(mMockSession), Return(Status::ok())));
+    ASSERT_TRUE(mWrapper->startPowerHintSession());
+    verifyAndClearExpectations();
+
+    EXPECT_CALL(*mMockSession.get(), updateTargetWorkDuration(1))
+            .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_ILLEGAL_STATE)));
+    mWrapper->setTargetWorkDuration(1);
+    EXPECT_TRUE(mWrapper->shouldReconnectHAL());
+}
+
+TEST_F(AidlPowerHalWrapperTest, sendActualWorkDuration) {
+    ASSERT_TRUE(mWrapper->supportsPowerHintSession());
+
+    std::vector<int32_t> threadIds = {1, 2};
+    mWrapper->setPowerHintSessionThreadIds(threadIds);
+    EXPECT_CALL(*mMockHal.get(), createHintSession(_, _, threadIds, _, _))
+            .WillOnce(DoAll(SetArgPointee<4>(mMockSession), Return(Status::ok())));
+    ASSERT_TRUE(mWrapper->startPowerHintSession());
+    verifyAndClearExpectations();
+
+    auto base = toWorkDuration(100ms, 0);
+    // test cases with actual work durations and whether it should update hint against baseline
+    // 100ms
+    const std::vector<std::pair<std::vector<std::pair<std::chrono::nanoseconds, nsecs_t>>, bool>>
+            testCases = {{{{-1ms, 100}}, false},
+                         {{{91ms, 100}}, false},
+                         {{{109ms, 100}}, false},
+                         {{{100ms, 100}, {200ms, 200}}, true},
+                         {{{100ms, 500}, {100ms, 600}, {3ms, 600}}, true}};
+
+    for (const auto& test : testCases) {
+        // reset actual duration
+        sendActualWorkDurationGroup({base}, 80ms);
+
+        auto raw = test.first;
+        std::vector<WorkDuration> durations(raw.size());
+        std::transform(raw.begin(), raw.end(), durations.begin(),
+                       [](std::pair<std::chrono::nanoseconds, nsecs_t> d) {
+                           return toWorkDuration(d.first, d.second);
+                       });
+        EXPECT_CALL(*mMockSession.get(), reportActualWorkDuration(durations))
+                .Times(test.second ? 1 : 0);
+        sendActualWorkDurationGroup(durations, 0ms);
+        verifyAndClearExpectations();
+    }
+}
+
+TEST_F(AidlPowerHalWrapperTest, sendActualWorkDuration_exceedsStaleTime) {
+    ASSERT_TRUE(mWrapper->supportsPowerHintSession());
+
+    std::vector<int32_t> threadIds = {1, 2};
+    mWrapper->setPowerHintSessionThreadIds(threadIds);
+    EXPECT_CALL(*mMockHal.get(), createHintSession(_, _, threadIds, _, _))
+            .WillOnce(DoAll(SetArgPointee<4>(mMockSession), Return(Status::ok())));
+    ASSERT_TRUE(mWrapper->startPowerHintSession());
+    verifyAndClearExpectations();
+
+    auto base = toWorkDuration(100ms, 0);
+    // test cases with actual work durations and whether it should update hint against baseline
+    // 100ms
+    const std::vector<std::pair<std::vector<std::pair<std::chrono::nanoseconds, nsecs_t>>, bool>>
+            testCases = {{{{91ms, 100}}, true}, {{{109ms, 100}}, true}};
+
+    for (const auto& test : testCases) {
+        // reset actual duration
+        sendActualWorkDurationGroup({base}, 80ms);
+
+        auto raw = test.first;
+        std::vector<WorkDuration> durations(raw.size());
+        std::transform(raw.begin(), raw.end(), durations.begin(),
+                       [](std::pair<std::chrono::nanoseconds, nsecs_t> d) {
+                           return toWorkDuration(d.first, d.second);
+                       });
+        EXPECT_CALL(*mMockSession.get(), reportActualWorkDuration(durations))
+                .Times(test.second ? 1 : 0);
+        sendActualWorkDurationGroup(durations, 80ms);
+        verifyAndClearExpectations();
+    }
+}
+
+TEST_F(AidlPowerHalWrapperTest, sendActualWorkDuration_shouldReconnectOnError) {
+    ASSERT_TRUE(mWrapper->supportsPowerHintSession());
+
+    std::vector<int32_t> threadIds = {1, 2};
+    mWrapper->setPowerHintSessionThreadIds(threadIds);
+    EXPECT_CALL(*mMockHal.get(), createHintSession(_, _, threadIds, _, _))
+            .WillOnce(DoAll(SetArgPointee<4>(mMockSession), Return(Status::ok())));
+    ASSERT_TRUE(mWrapper->startPowerHintSession());
+    verifyAndClearExpectations();
+    WorkDuration duration;
+    duration.durationNanos = 1;
+    EXPECT_CALL(*mMockSession.get(), reportActualWorkDuration(_))
+            .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_ILLEGAL_STATE)));
+    sendActualWorkDurationGroup({duration}, 0ms);
+    EXPECT_TRUE(mWrapper->shouldReconnectHAL());
+}
+
+} // namespace
+} // namespace android::Hwc2::impl
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index 1eea023..cc9d48c 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -26,6 +26,8 @@
     srcs: [
         "mock/DisplayHardware/MockComposer.cpp",
         "mock/DisplayHardware/MockHWC2.cpp",
+        "mock/DisplayHardware/MockIPower.cpp",
+        "mock/DisplayHardware/MockIPowerHintSession.cpp",
         "mock/DisplayHardware/MockPowerAdvisor.cpp",
         "mock/MockEventThread.cpp",
         "mock/MockFrameTimeline.cpp",
@@ -67,6 +69,7 @@
         ":libsurfaceflinger_mock_sources",
         ":libsurfaceflinger_sources",
         "libsurfaceflinger_unittest_main.cpp",
+        "AidlPowerHalWrapperTest.cpp",
         "CachingTest.cpp",
         "CompositionTest.cpp",
         "DispSyncSourceTest.cpp",
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPower.cpp b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPower.cpp
new file mode 100644
index 0000000..2323ebb
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPower.cpp
@@ -0,0 +1,24 @@
+/*
+ * 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.
+ */
+
+#include "mock/DisplayHardware/MockIPower.h"
+
+namespace android::Hwc2::mock {
+
+// Explicit default instantiation is recommended.
+MockIPower::MockIPower() = default;
+
+} // namespace android::Hwc2::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPower.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPower.h
new file mode 100644
index 0000000..0ddc90d
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPower.h
@@ -0,0 +1,50 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include "binder/Status.h"
+
+#include <android/hardware/power/IPower.h>
+#include <gmock/gmock.h>
+
+using android::binder::Status;
+using android::hardware::power::Boost;
+using android::hardware::power::IPower;
+using android::hardware::power::IPowerHintSession;
+using android::hardware::power::Mode;
+
+namespace android::Hwc2::mock {
+
+class MockIPower : public IPower {
+public:
+    MockIPower();
+
+    MOCK_METHOD(Status, isBoostSupported, (Boost boost, bool* ret), (override));
+    MOCK_METHOD(Status, setBoost, (Boost boost, int32_t durationMs), (override));
+    MOCK_METHOD(Status, isModeSupported, (Mode mode, bool* ret), (override));
+    MOCK_METHOD(Status, setMode, (Mode mode, bool enabled), (override));
+    MOCK_METHOD(Status, createHintSession,
+                (int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds,
+                 int64_t durationNanos, sp<IPowerHintSession>* session),
+                (override));
+    MOCK_METHOD(Status, getHintSessionPreferredRate, (int64_t * rate), (override));
+    MOCK_METHOD(int32_t, getInterfaceVersion, (), (override));
+    MOCK_METHOD(std::string, getInterfaceHash, (), (override));
+    MOCK_METHOD(IBinder*, onAsBinder, (), (override));
+};
+
+} // namespace android::Hwc2::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPowerHintSession.cpp b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPowerHintSession.cpp
new file mode 100644
index 0000000..770bc15
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPowerHintSession.cpp
@@ -0,0 +1,24 @@
+/*
+ * 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.
+ */
+
+#include "mock/DisplayHardware/MockIPowerHintSession.h"
+
+namespace android::Hwc2::mock {
+
+// Explicit default instantiation is recommended.
+MockIPowerHintSession::MockIPowerHintSession() = default;
+
+} // namespace android::Hwc2::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPowerHintSession.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPowerHintSession.h
new file mode 100644
index 0000000..439f6f4
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPowerHintSession.h
@@ -0,0 +1,45 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include "binder/Status.h"
+
+#include <android/hardware/power/IPower.h>
+#include <gmock/gmock.h>
+
+using android::binder::Status;
+using android::hardware::power::IPowerHintSession;
+
+using namespace android::hardware::power;
+
+namespace android::Hwc2::mock {
+
+class MockIPowerHintSession : public IPowerHintSession {
+public:
+    MockIPowerHintSession();
+
+    MOCK_METHOD(IBinder*, onAsBinder, (), (override));
+    MOCK_METHOD(Status, pause, (), (override));
+    MOCK_METHOD(Status, resume, (), (override));
+    MOCK_METHOD(Status, close, (), (override));
+    MOCK_METHOD(int32_t, getInterfaceVersion, (), (override));
+    MOCK_METHOD(std::string, getInterfaceHash, (), (override));
+    MOCK_METHOD(Status, updateTargetWorkDuration, (int64_t), (override));
+    MOCK_METHOD(Status, reportActualWorkDuration, (const ::std::vector<WorkDuration>&), (override));
+};
+
+} // namespace android::Hwc2::mock