Loading libs/hwui/Android.bp +1 −0 Original line number Diff line number Diff line Loading @@ -737,6 +737,7 @@ cc_test { "tests/unit/EglManagerTests.cpp", "tests/unit/FatVectorTests.cpp", "tests/unit/GraphicsStatsServiceTests.cpp", "tests/unit/HintSessionWrapperTests.cpp", "tests/unit/JankTrackerTests.cpp", "tests/unit/FrameMetricsReporterTests.cpp", "tests/unit/LayerUpdateQueueTests.cpp", Loading libs/hwui/renderthread/HintSessionWrapper.cpp +26 −60 Original line number Diff line number Diff line Loading @@ -32,65 +32,30 @@ namespace android { namespace uirenderer { namespace renderthread { namespace { typedef APerformanceHintManager* (*APH_getManager)(); typedef APerformanceHintSession* (*APH_createSession)(APerformanceHintManager*, const int32_t*, size_t, int64_t); typedef void (*APH_closeSession)(APerformanceHintSession* session); typedef void (*APH_updateTargetWorkDuration)(APerformanceHintSession*, int64_t); typedef void (*APH_reportActualWorkDuration)(APerformanceHintSession*, int64_t); typedef void (*APH_sendHint)(APerformanceHintSession* session, int32_t); bool gAPerformanceHintBindingInitialized = false; APH_getManager gAPH_getManagerFn = nullptr; APH_createSession gAPH_createSessionFn = nullptr; APH_closeSession gAPH_closeSessionFn = nullptr; APH_updateTargetWorkDuration gAPH_updateTargetWorkDurationFn = nullptr; APH_reportActualWorkDuration gAPH_reportActualWorkDurationFn = nullptr; APH_sendHint gAPH_sendHintFn = nullptr; void ensureAPerformanceHintBindingInitialized() { if (gAPerformanceHintBindingInitialized) return; #define BIND_APH_METHOD(name) \ name = (decltype(name))dlsym(handle_, "APerformanceHint_" #name); \ LOG_ALWAYS_FATAL_IF(name == nullptr, "Failed to find required symbol APerformanceHint_" #name) void HintSessionWrapper::HintSessionBinding::init() { if (mInitialized) return; void* handle_ = dlopen("libandroid.so", RTLD_NOW | RTLD_NODELETE); LOG_ALWAYS_FATAL_IF(handle_ == nullptr, "Failed to dlopen libandroid.so!"); gAPH_getManagerFn = (APH_getManager)dlsym(handle_, "APerformanceHint_getManager"); LOG_ALWAYS_FATAL_IF(gAPH_getManagerFn == nullptr, "Failed to find required symbol APerformanceHint_getManager!"); gAPH_createSessionFn = (APH_createSession)dlsym(handle_, "APerformanceHint_createSession"); LOG_ALWAYS_FATAL_IF(gAPH_createSessionFn == nullptr, "Failed to find required symbol APerformanceHint_createSession!"); gAPH_closeSessionFn = (APH_closeSession)dlsym(handle_, "APerformanceHint_closeSession"); LOG_ALWAYS_FATAL_IF(gAPH_closeSessionFn == nullptr, "Failed to find required symbol APerformanceHint_closeSession!"); gAPH_updateTargetWorkDurationFn = (APH_updateTargetWorkDuration)dlsym( handle_, "APerformanceHint_updateTargetWorkDuration"); LOG_ALWAYS_FATAL_IF( gAPH_updateTargetWorkDurationFn == nullptr, "Failed to find required symbol APerformanceHint_updateTargetWorkDuration!"); BIND_APH_METHOD(getManager); BIND_APH_METHOD(createSession); BIND_APH_METHOD(closeSession); BIND_APH_METHOD(updateTargetWorkDuration); BIND_APH_METHOD(reportActualWorkDuration); BIND_APH_METHOD(sendHint); gAPH_reportActualWorkDurationFn = (APH_reportActualWorkDuration)dlsym( handle_, "APerformanceHint_reportActualWorkDuration"); LOG_ALWAYS_FATAL_IF( gAPH_reportActualWorkDurationFn == nullptr, "Failed to find required symbol APerformanceHint_reportActualWorkDuration!"); gAPH_sendHintFn = (APH_sendHint)dlsym(handle_, "APerformanceHint_sendHint"); LOG_ALWAYS_FATAL_IF(gAPH_sendHintFn == nullptr, "Failed to find required symbol APerformanceHint_sendHint!"); gAPerformanceHintBindingInitialized = true; mInitialized = true; } } // namespace HintSessionWrapper::HintSessionWrapper(pid_t uiThreadId, pid_t renderThreadId) : mUiThreadId(uiThreadId), mRenderThreadId(renderThreadId) {} : mUiThreadId(uiThreadId) , mRenderThreadId(renderThreadId) , mBinding(std::make_shared<HintSessionBinding>()) {} HintSessionWrapper::~HintSessionWrapper() { destroy(); Loading @@ -101,7 +66,7 @@ void HintSessionWrapper::destroy() { mHintSession = mHintSessionFuture.get(); } if (mHintSession) { gAPH_closeSessionFn(mHintSession); mBinding->closeSession(mHintSession); mSessionValid = true; mHintSession = nullptr; } Loading Loading @@ -133,9 +98,9 @@ bool HintSessionWrapper::init() { // Assume that if we return before the end, it broke mSessionValid = false; ensureAPerformanceHintBindingInitialized(); mBinding->init(); APerformanceHintManager* manager = gAPH_getManagerFn(); APerformanceHintManager* manager = mBinding->getManager(); if (!manager) return false; std::vector<pid_t> tids = CommonPool::getThreadIds(); Loading @@ -145,8 +110,9 @@ bool HintSessionWrapper::init() { // Use a placeholder target value to initialize, // this will always be replaced elsewhere before it gets used int64_t defaultTargetDurationNanos = 16666667; mHintSessionFuture = CommonPool::async([=, tids = std::move(tids)] { return gAPH_createSessionFn(manager, tids.data(), tids.size(), defaultTargetDurationNanos); mHintSessionFuture = CommonPool::async([=, this, tids = std::move(tids)] { return mBinding->createSession(manager, tids.data(), tids.size(), defaultTargetDurationNanos); }); return false; } Loading @@ -158,7 +124,7 @@ void HintSessionWrapper::updateTargetWorkDuration(long targetWorkDurationNanos) targetWorkDurationNanos > kSanityCheckLowerBound && targetWorkDurationNanos < kSanityCheckUpperBound) { mLastTargetWorkDuration = targetWorkDurationNanos; gAPH_updateTargetWorkDurationFn(mHintSession, targetWorkDurationNanos); mBinding->updateTargetWorkDuration(mHintSession, targetWorkDurationNanos); } mLastFrameNotification = systemTime(); } Loading @@ -168,7 +134,7 @@ void HintSessionWrapper::reportActualWorkDuration(long actualDurationNanos) { mResetsSinceLastReport = 0; if (actualDurationNanos > kSanityCheckLowerBound && actualDurationNanos < kSanityCheckUpperBound) { gAPH_reportActualWorkDurationFn(mHintSession, actualDurationNanos); mBinding->reportActualWorkDuration(mHintSession, actualDurationNanos); } } Loading @@ -179,14 +145,14 @@ void HintSessionWrapper::sendLoadResetHint() { if (now - mLastFrameNotification > kResetHintTimeout && mResetsSinceLastReport <= kMaxResetsSinceLastReport) { ++mResetsSinceLastReport; gAPH_sendHintFn(mHintSession, static_cast<int>(SessionHint::CPU_LOAD_RESET)); mBinding->sendHint(mHintSession, static_cast<int32_t>(SessionHint::CPU_LOAD_RESET)); } mLastFrameNotification = now; } void HintSessionWrapper::sendLoadIncreaseHint() { if (!init()) return; gAPH_sendHintFn(mHintSession, static_cast<int>(SessionHint::CPU_LOAD_UP)); mBinding->sendHint(mHintSession, static_cast<int32_t>(SessionHint::CPU_LOAD_UP)); } } /* namespace renderthread */ Loading libs/hwui/renderthread/HintSessionWrapper.h +24 −0 Original line number Diff line number Diff line Loading @@ -29,6 +29,8 @@ namespace renderthread { class HintSessionWrapper { public: friend class HintSessionWrapperTests; HintSessionWrapper(pid_t uiThreadId, pid_t renderThreadId); ~HintSessionWrapper(); Loading @@ -55,6 +57,28 @@ private: static constexpr nsecs_t kResetHintTimeout = 100_ms; static constexpr int64_t kSanityCheckLowerBound = 100_us; static constexpr int64_t kSanityCheckUpperBound = 10_s; // Allows easier stub when testing class HintSessionBinding { public: virtual ~HintSessionBinding() = default; virtual void init(); APerformanceHintManager* (*getManager)(); APerformanceHintSession* (*createSession)(APerformanceHintManager* manager, const int32_t* tids, size_t tidCount, int64_t defaultTarget) = nullptr; void (*closeSession)(APerformanceHintSession* session) = nullptr; void (*updateTargetWorkDuration)(APerformanceHintSession* session, int64_t targetDuration) = nullptr; void (*reportActualWorkDuration)(APerformanceHintSession* session, int64_t actualDuration) = nullptr; void (*sendHint)(APerformanceHintSession* session, int32_t hintId) = nullptr; private: bool mInitialized = false; }; std::shared_ptr<HintSessionBinding> mBinding; }; } /* namespace renderthread */ Loading libs/hwui/tests/unit/HintSessionWrapperTests.cpp 0 → 100644 +151 −0 Original line number Diff line number Diff line /* * Copyright (C) 2023 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 <gmock/gmock.h> #include <gtest/gtest.h> #include <private/performance_hint_private.h> #include <renderthread/HintSessionWrapper.h> #include <utils/Log.h> #include <chrono> #include "Properties.h" using namespace testing; using namespace std::chrono_literals; APerformanceHintManager* managerPtr = reinterpret_cast<APerformanceHintManager*>(123); APerformanceHintSession* sessionPtr = reinterpret_cast<APerformanceHintSession*>(456); int uiThreadId = 1; int renderThreadId = 2; namespace android::uirenderer::renderthread { class HintSessionWrapperTests : public testing::Test { public: void SetUp() override; void TearDown() override; protected: std::shared_ptr<HintSessionWrapper> mWrapper; class MockHintSessionBinding : public HintSessionWrapper::HintSessionBinding { public: void init() override; MOCK_METHOD(APerformanceHintManager*, fakeGetManager, ()); MOCK_METHOD(APerformanceHintSession*, fakeCreateSession, (APerformanceHintManager*, const int32_t*, size_t, int64_t)); MOCK_METHOD(void, fakeCloseSession, (APerformanceHintSession*)); MOCK_METHOD(void, fakeUpdateTargetWorkDuration, (APerformanceHintSession*, int64_t)); MOCK_METHOD(void, fakeReportActualWorkDuration, (APerformanceHintSession*, int64_t)); MOCK_METHOD(void, fakeSendHint, (APerformanceHintSession*, int32_t)); }; // Must be static so it can have function pointers we can point to with static methods static std::shared_ptr<MockHintSessionBinding> sMockBinding; // Must be static so we can point to them as normal fn pointers with HintSessionBinding static APerformanceHintManager* stubGetManager() { return sMockBinding->fakeGetManager(); }; static APerformanceHintSession* stubCreateSession(APerformanceHintManager* manager, const int32_t* ids, size_t idsSize, int64_t initialTarget) { return sMockBinding->fakeCreateSession(manager, ids, idsSize, initialTarget); } static APerformanceHintSession* stubSlowCreateSession(APerformanceHintManager* manager, const int32_t* ids, size_t idsSize, int64_t initialTarget) { std::this_thread::sleep_for(50ms); return sMockBinding->fakeCreateSession(manager, ids, idsSize, initialTarget); } static void stubCloseSession(APerformanceHintSession* session) { sMockBinding->fakeCloseSession(session); }; static void stubUpdateTargetWorkDuration(APerformanceHintSession* session, int64_t workDuration) { sMockBinding->fakeUpdateTargetWorkDuration(session, workDuration); } static void stubReportActualWorkDuration(APerformanceHintSession* session, int64_t workDuration) { sMockBinding->fakeReportActualWorkDuration(session, workDuration); } static void stubSendHint(APerformanceHintSession* session, int32_t hintId) { sMockBinding->fakeSendHint(session, hintId); }; void waitForWrapperReady() { mWrapper->mHintSessionFuture.wait(); } }; std::shared_ptr<HintSessionWrapperTests::MockHintSessionBinding> HintSessionWrapperTests::sMockBinding; void HintSessionWrapperTests::SetUp() { // Pretend it's supported even if we're in an emulator Properties::useHintManager = true; sMockBinding = std::make_shared<NiceMock<MockHintSessionBinding>>(); mWrapper = std::make_shared<HintSessionWrapper>(uiThreadId, renderThreadId); mWrapper->mBinding = sMockBinding; EXPECT_CALL(*sMockBinding, fakeGetManager).WillOnce(Return(managerPtr)); ON_CALL(*sMockBinding, fakeCreateSession).WillByDefault(Return(sessionPtr)); } void HintSessionWrapperTests::MockHintSessionBinding::init() { sMockBinding->getManager = &stubGetManager; if (sMockBinding->createSession == nullptr) { sMockBinding->createSession = &stubCreateSession; } sMockBinding->closeSession = &stubCloseSession; sMockBinding->updateTargetWorkDuration = &stubUpdateTargetWorkDuration; sMockBinding->reportActualWorkDuration = &stubReportActualWorkDuration; sMockBinding->sendHint = &stubSendHint; } void HintSessionWrapperTests::TearDown() { mWrapper = nullptr; sMockBinding = nullptr; } TEST_F(HintSessionWrapperTests, destructorClosesBackgroundSession) { EXPECT_CALL(*sMockBinding, fakeCloseSession(sessionPtr)).Times(1); sMockBinding->createSession = stubSlowCreateSession; mWrapper->init(); mWrapper = nullptr; } TEST_F(HintSessionWrapperTests, sessionInitializesCorrectly) { EXPECT_CALL(*sMockBinding, fakeCreateSession(managerPtr, _, Gt(1), _)).Times(1); mWrapper->init(); waitForWrapperReady(); } TEST_F(HintSessionWrapperTests, loadUpHintsSendCorrectly) { EXPECT_CALL(*sMockBinding, fakeSendHint(sessionPtr, static_cast<int32_t>(SessionHint::CPU_LOAD_UP))) .Times(1); mWrapper->init(); waitForWrapperReady(); mWrapper->sendLoadIncreaseHint(); } TEST_F(HintSessionWrapperTests, loadResetHintsSendCorrectly) { EXPECT_CALL(*sMockBinding, fakeSendHint(sessionPtr, static_cast<int32_t>(SessionHint::CPU_LOAD_RESET))) .Times(1); mWrapper->init(); waitForWrapperReady(); mWrapper->sendLoadResetHint(); } } // namespace android::uirenderer::renderthread No newline at end of file Loading
libs/hwui/Android.bp +1 −0 Original line number Diff line number Diff line Loading @@ -737,6 +737,7 @@ cc_test { "tests/unit/EglManagerTests.cpp", "tests/unit/FatVectorTests.cpp", "tests/unit/GraphicsStatsServiceTests.cpp", "tests/unit/HintSessionWrapperTests.cpp", "tests/unit/JankTrackerTests.cpp", "tests/unit/FrameMetricsReporterTests.cpp", "tests/unit/LayerUpdateQueueTests.cpp", Loading
libs/hwui/renderthread/HintSessionWrapper.cpp +26 −60 Original line number Diff line number Diff line Loading @@ -32,65 +32,30 @@ namespace android { namespace uirenderer { namespace renderthread { namespace { typedef APerformanceHintManager* (*APH_getManager)(); typedef APerformanceHintSession* (*APH_createSession)(APerformanceHintManager*, const int32_t*, size_t, int64_t); typedef void (*APH_closeSession)(APerformanceHintSession* session); typedef void (*APH_updateTargetWorkDuration)(APerformanceHintSession*, int64_t); typedef void (*APH_reportActualWorkDuration)(APerformanceHintSession*, int64_t); typedef void (*APH_sendHint)(APerformanceHintSession* session, int32_t); bool gAPerformanceHintBindingInitialized = false; APH_getManager gAPH_getManagerFn = nullptr; APH_createSession gAPH_createSessionFn = nullptr; APH_closeSession gAPH_closeSessionFn = nullptr; APH_updateTargetWorkDuration gAPH_updateTargetWorkDurationFn = nullptr; APH_reportActualWorkDuration gAPH_reportActualWorkDurationFn = nullptr; APH_sendHint gAPH_sendHintFn = nullptr; void ensureAPerformanceHintBindingInitialized() { if (gAPerformanceHintBindingInitialized) return; #define BIND_APH_METHOD(name) \ name = (decltype(name))dlsym(handle_, "APerformanceHint_" #name); \ LOG_ALWAYS_FATAL_IF(name == nullptr, "Failed to find required symbol APerformanceHint_" #name) void HintSessionWrapper::HintSessionBinding::init() { if (mInitialized) return; void* handle_ = dlopen("libandroid.so", RTLD_NOW | RTLD_NODELETE); LOG_ALWAYS_FATAL_IF(handle_ == nullptr, "Failed to dlopen libandroid.so!"); gAPH_getManagerFn = (APH_getManager)dlsym(handle_, "APerformanceHint_getManager"); LOG_ALWAYS_FATAL_IF(gAPH_getManagerFn == nullptr, "Failed to find required symbol APerformanceHint_getManager!"); gAPH_createSessionFn = (APH_createSession)dlsym(handle_, "APerformanceHint_createSession"); LOG_ALWAYS_FATAL_IF(gAPH_createSessionFn == nullptr, "Failed to find required symbol APerformanceHint_createSession!"); gAPH_closeSessionFn = (APH_closeSession)dlsym(handle_, "APerformanceHint_closeSession"); LOG_ALWAYS_FATAL_IF(gAPH_closeSessionFn == nullptr, "Failed to find required symbol APerformanceHint_closeSession!"); gAPH_updateTargetWorkDurationFn = (APH_updateTargetWorkDuration)dlsym( handle_, "APerformanceHint_updateTargetWorkDuration"); LOG_ALWAYS_FATAL_IF( gAPH_updateTargetWorkDurationFn == nullptr, "Failed to find required symbol APerformanceHint_updateTargetWorkDuration!"); BIND_APH_METHOD(getManager); BIND_APH_METHOD(createSession); BIND_APH_METHOD(closeSession); BIND_APH_METHOD(updateTargetWorkDuration); BIND_APH_METHOD(reportActualWorkDuration); BIND_APH_METHOD(sendHint); gAPH_reportActualWorkDurationFn = (APH_reportActualWorkDuration)dlsym( handle_, "APerformanceHint_reportActualWorkDuration"); LOG_ALWAYS_FATAL_IF( gAPH_reportActualWorkDurationFn == nullptr, "Failed to find required symbol APerformanceHint_reportActualWorkDuration!"); gAPH_sendHintFn = (APH_sendHint)dlsym(handle_, "APerformanceHint_sendHint"); LOG_ALWAYS_FATAL_IF(gAPH_sendHintFn == nullptr, "Failed to find required symbol APerformanceHint_sendHint!"); gAPerformanceHintBindingInitialized = true; mInitialized = true; } } // namespace HintSessionWrapper::HintSessionWrapper(pid_t uiThreadId, pid_t renderThreadId) : mUiThreadId(uiThreadId), mRenderThreadId(renderThreadId) {} : mUiThreadId(uiThreadId) , mRenderThreadId(renderThreadId) , mBinding(std::make_shared<HintSessionBinding>()) {} HintSessionWrapper::~HintSessionWrapper() { destroy(); Loading @@ -101,7 +66,7 @@ void HintSessionWrapper::destroy() { mHintSession = mHintSessionFuture.get(); } if (mHintSession) { gAPH_closeSessionFn(mHintSession); mBinding->closeSession(mHintSession); mSessionValid = true; mHintSession = nullptr; } Loading Loading @@ -133,9 +98,9 @@ bool HintSessionWrapper::init() { // Assume that if we return before the end, it broke mSessionValid = false; ensureAPerformanceHintBindingInitialized(); mBinding->init(); APerformanceHintManager* manager = gAPH_getManagerFn(); APerformanceHintManager* manager = mBinding->getManager(); if (!manager) return false; std::vector<pid_t> tids = CommonPool::getThreadIds(); Loading @@ -145,8 +110,9 @@ bool HintSessionWrapper::init() { // Use a placeholder target value to initialize, // this will always be replaced elsewhere before it gets used int64_t defaultTargetDurationNanos = 16666667; mHintSessionFuture = CommonPool::async([=, tids = std::move(tids)] { return gAPH_createSessionFn(manager, tids.data(), tids.size(), defaultTargetDurationNanos); mHintSessionFuture = CommonPool::async([=, this, tids = std::move(tids)] { return mBinding->createSession(manager, tids.data(), tids.size(), defaultTargetDurationNanos); }); return false; } Loading @@ -158,7 +124,7 @@ void HintSessionWrapper::updateTargetWorkDuration(long targetWorkDurationNanos) targetWorkDurationNanos > kSanityCheckLowerBound && targetWorkDurationNanos < kSanityCheckUpperBound) { mLastTargetWorkDuration = targetWorkDurationNanos; gAPH_updateTargetWorkDurationFn(mHintSession, targetWorkDurationNanos); mBinding->updateTargetWorkDuration(mHintSession, targetWorkDurationNanos); } mLastFrameNotification = systemTime(); } Loading @@ -168,7 +134,7 @@ void HintSessionWrapper::reportActualWorkDuration(long actualDurationNanos) { mResetsSinceLastReport = 0; if (actualDurationNanos > kSanityCheckLowerBound && actualDurationNanos < kSanityCheckUpperBound) { gAPH_reportActualWorkDurationFn(mHintSession, actualDurationNanos); mBinding->reportActualWorkDuration(mHintSession, actualDurationNanos); } } Loading @@ -179,14 +145,14 @@ void HintSessionWrapper::sendLoadResetHint() { if (now - mLastFrameNotification > kResetHintTimeout && mResetsSinceLastReport <= kMaxResetsSinceLastReport) { ++mResetsSinceLastReport; gAPH_sendHintFn(mHintSession, static_cast<int>(SessionHint::CPU_LOAD_RESET)); mBinding->sendHint(mHintSession, static_cast<int32_t>(SessionHint::CPU_LOAD_RESET)); } mLastFrameNotification = now; } void HintSessionWrapper::sendLoadIncreaseHint() { if (!init()) return; gAPH_sendHintFn(mHintSession, static_cast<int>(SessionHint::CPU_LOAD_UP)); mBinding->sendHint(mHintSession, static_cast<int32_t>(SessionHint::CPU_LOAD_UP)); } } /* namespace renderthread */ Loading
libs/hwui/renderthread/HintSessionWrapper.h +24 −0 Original line number Diff line number Diff line Loading @@ -29,6 +29,8 @@ namespace renderthread { class HintSessionWrapper { public: friend class HintSessionWrapperTests; HintSessionWrapper(pid_t uiThreadId, pid_t renderThreadId); ~HintSessionWrapper(); Loading @@ -55,6 +57,28 @@ private: static constexpr nsecs_t kResetHintTimeout = 100_ms; static constexpr int64_t kSanityCheckLowerBound = 100_us; static constexpr int64_t kSanityCheckUpperBound = 10_s; // Allows easier stub when testing class HintSessionBinding { public: virtual ~HintSessionBinding() = default; virtual void init(); APerformanceHintManager* (*getManager)(); APerformanceHintSession* (*createSession)(APerformanceHintManager* manager, const int32_t* tids, size_t tidCount, int64_t defaultTarget) = nullptr; void (*closeSession)(APerformanceHintSession* session) = nullptr; void (*updateTargetWorkDuration)(APerformanceHintSession* session, int64_t targetDuration) = nullptr; void (*reportActualWorkDuration)(APerformanceHintSession* session, int64_t actualDuration) = nullptr; void (*sendHint)(APerformanceHintSession* session, int32_t hintId) = nullptr; private: bool mInitialized = false; }; std::shared_ptr<HintSessionBinding> mBinding; }; } /* namespace renderthread */ Loading
libs/hwui/tests/unit/HintSessionWrapperTests.cpp 0 → 100644 +151 −0 Original line number Diff line number Diff line /* * Copyright (C) 2023 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 <gmock/gmock.h> #include <gtest/gtest.h> #include <private/performance_hint_private.h> #include <renderthread/HintSessionWrapper.h> #include <utils/Log.h> #include <chrono> #include "Properties.h" using namespace testing; using namespace std::chrono_literals; APerformanceHintManager* managerPtr = reinterpret_cast<APerformanceHintManager*>(123); APerformanceHintSession* sessionPtr = reinterpret_cast<APerformanceHintSession*>(456); int uiThreadId = 1; int renderThreadId = 2; namespace android::uirenderer::renderthread { class HintSessionWrapperTests : public testing::Test { public: void SetUp() override; void TearDown() override; protected: std::shared_ptr<HintSessionWrapper> mWrapper; class MockHintSessionBinding : public HintSessionWrapper::HintSessionBinding { public: void init() override; MOCK_METHOD(APerformanceHintManager*, fakeGetManager, ()); MOCK_METHOD(APerformanceHintSession*, fakeCreateSession, (APerformanceHintManager*, const int32_t*, size_t, int64_t)); MOCK_METHOD(void, fakeCloseSession, (APerformanceHintSession*)); MOCK_METHOD(void, fakeUpdateTargetWorkDuration, (APerformanceHintSession*, int64_t)); MOCK_METHOD(void, fakeReportActualWorkDuration, (APerformanceHintSession*, int64_t)); MOCK_METHOD(void, fakeSendHint, (APerformanceHintSession*, int32_t)); }; // Must be static so it can have function pointers we can point to with static methods static std::shared_ptr<MockHintSessionBinding> sMockBinding; // Must be static so we can point to them as normal fn pointers with HintSessionBinding static APerformanceHintManager* stubGetManager() { return sMockBinding->fakeGetManager(); }; static APerformanceHintSession* stubCreateSession(APerformanceHintManager* manager, const int32_t* ids, size_t idsSize, int64_t initialTarget) { return sMockBinding->fakeCreateSession(manager, ids, idsSize, initialTarget); } static APerformanceHintSession* stubSlowCreateSession(APerformanceHintManager* manager, const int32_t* ids, size_t idsSize, int64_t initialTarget) { std::this_thread::sleep_for(50ms); return sMockBinding->fakeCreateSession(manager, ids, idsSize, initialTarget); } static void stubCloseSession(APerformanceHintSession* session) { sMockBinding->fakeCloseSession(session); }; static void stubUpdateTargetWorkDuration(APerformanceHintSession* session, int64_t workDuration) { sMockBinding->fakeUpdateTargetWorkDuration(session, workDuration); } static void stubReportActualWorkDuration(APerformanceHintSession* session, int64_t workDuration) { sMockBinding->fakeReportActualWorkDuration(session, workDuration); } static void stubSendHint(APerformanceHintSession* session, int32_t hintId) { sMockBinding->fakeSendHint(session, hintId); }; void waitForWrapperReady() { mWrapper->mHintSessionFuture.wait(); } }; std::shared_ptr<HintSessionWrapperTests::MockHintSessionBinding> HintSessionWrapperTests::sMockBinding; void HintSessionWrapperTests::SetUp() { // Pretend it's supported even if we're in an emulator Properties::useHintManager = true; sMockBinding = std::make_shared<NiceMock<MockHintSessionBinding>>(); mWrapper = std::make_shared<HintSessionWrapper>(uiThreadId, renderThreadId); mWrapper->mBinding = sMockBinding; EXPECT_CALL(*sMockBinding, fakeGetManager).WillOnce(Return(managerPtr)); ON_CALL(*sMockBinding, fakeCreateSession).WillByDefault(Return(sessionPtr)); } void HintSessionWrapperTests::MockHintSessionBinding::init() { sMockBinding->getManager = &stubGetManager; if (sMockBinding->createSession == nullptr) { sMockBinding->createSession = &stubCreateSession; } sMockBinding->closeSession = &stubCloseSession; sMockBinding->updateTargetWorkDuration = &stubUpdateTargetWorkDuration; sMockBinding->reportActualWorkDuration = &stubReportActualWorkDuration; sMockBinding->sendHint = &stubSendHint; } void HintSessionWrapperTests::TearDown() { mWrapper = nullptr; sMockBinding = nullptr; } TEST_F(HintSessionWrapperTests, destructorClosesBackgroundSession) { EXPECT_CALL(*sMockBinding, fakeCloseSession(sessionPtr)).Times(1); sMockBinding->createSession = stubSlowCreateSession; mWrapper->init(); mWrapper = nullptr; } TEST_F(HintSessionWrapperTests, sessionInitializesCorrectly) { EXPECT_CALL(*sMockBinding, fakeCreateSession(managerPtr, _, Gt(1), _)).Times(1); mWrapper->init(); waitForWrapperReady(); } TEST_F(HintSessionWrapperTests, loadUpHintsSendCorrectly) { EXPECT_CALL(*sMockBinding, fakeSendHint(sessionPtr, static_cast<int32_t>(SessionHint::CPU_LOAD_UP))) .Times(1); mWrapper->init(); waitForWrapperReady(); mWrapper->sendLoadIncreaseHint(); } TEST_F(HintSessionWrapperTests, loadResetHintsSendCorrectly) { EXPECT_CALL(*sMockBinding, fakeSendHint(sessionPtr, static_cast<int32_t>(SessionHint::CPU_LOAD_RESET))) .Times(1); mWrapper->init(); waitForWrapperReady(); mWrapper->sendLoadResetHint(); } } // namespace android::uirenderer::renderthread No newline at end of file