diff options
| author | 2022-03-30 23:36:57 +0000 | |
|---|---|---|
| committer | 2022-03-30 23:36:57 +0000 | |
| commit | a633a86a665bf27da4be651fcacfcf2a1c57bdcc (patch) | |
| tree | 55d4a58687e68b70ceb1fa55660bbe41f119001e | |
| parent | bc9e42db9a4b74185390fd9a0aa6882fb55899df (diff) | |
| parent | e12b4fa9f648372b5e68a6641edc7e8e41c26c87 (diff) | |
Merge "Add unit test for AidlPowerHalWrapper" into tm-dev
11 files changed, 662 insertions, 212 deletions
diff --git a/services/surfaceflinger/CompositionEngine/Android.bp b/services/surfaceflinger/CompositionEngine/Android.bp index aefc014062..516c3ef35b 100644 --- a/services/surfaceflinger/CompositionEngine/Android.bp +++ b/services/surfaceflinger/CompositionEngine/Android.bp @@ -23,6 +23,7 @@ cc_defaults { "android.hardware.graphics.composer3-V1-ndk", "android.hardware.power@1.0", "android.hardware.power@1.3", + "android.hardware.power-V2-cpp", "libbase", "libcutils", "libgui", diff --git a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp index 44c086d7d3..05f488b64b 100644 --- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp +++ b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp @@ -32,7 +32,6 @@ #include <utils/Trace.h> #include <android/hardware/power/1.3/IPower.h> -#include <android/hardware/power/IPower.h> #include <android/hardware/power/IPowerHintSession.h> #include <android/hardware/power/WorkDuration.h> @@ -62,8 +61,6 @@ using android::hardware::power::WorkDuration; using scheduler::OneShotTimer; -class AidlPowerHalWrapper; - PowerAdvisor::~PowerAdvisor() = default; namespace { @@ -294,258 +291,231 @@ private: const sp<V1_3::IPower> mPowerHal = nullptr; }; -class AidlPowerHalWrapper : public PowerAdvisor::HalWrapper { -public: - AidlPowerHalWrapper(sp<IPower> powerHal) : mPowerHal(std::move(powerHal)) { - auto ret = mPowerHal->isModeSupported(Mode::EXPENSIVE_RENDERING, &mHasExpensiveRendering); - if (!ret.isOk()) { - mHasExpensiveRendering = false; - } - - ret = mPowerHal->isBoostSupported(Boost::DISPLAY_UPDATE_IMMINENT, - &mHasDisplayUpdateImminent); - if (!ret.isOk()) { - mHasDisplayUpdateImminent = false; - } +AidlPowerHalWrapper::AidlPowerHalWrapper(sp<IPower> powerHal) : mPowerHal(std::move(powerHal)) { + auto ret = mPowerHal->isModeSupported(Mode::EXPENSIVE_RENDERING, &mHasExpensiveRendering); + if (!ret.isOk()) { + mHasExpensiveRendering = false; + } - mSupportsPowerHint = checkPowerHintSessionSupported(); + ret = mPowerHal->isBoostSupported(Boost::DISPLAY_UPDATE_IMMINENT, &mHasDisplayUpdateImminent); + if (!ret.isOk()) { + mHasDisplayUpdateImminent = false; } - ~AidlPowerHalWrapper() override { - if (mPowerHintSession != nullptr) { - mPowerHintSession->close(); - mPowerHintSession = nullptr; - } - }; + mSupportsPowerHint = checkPowerHintSessionSupported(); +} - static std::unique_ptr<HalWrapper> connect() { - // This only waits if the service is actually declared - sp<IPower> powerHal = waitForVintfService<IPower>(); - if (powerHal == nullptr) { - return nullptr; - } - ALOGI("Loaded AIDL Power HAL service"); +AidlPowerHalWrapper::~AidlPowerHalWrapper() { + if (mPowerHintSession != nullptr) { + mPowerHintSession->close(); + mPowerHintSession = nullptr; + } +}; - return std::make_unique<AidlPowerHalWrapper>(std::move(powerHal)); +std::unique_ptr<PowerAdvisor::HalWrapper> AidlPowerHalWrapper::connect() { + // This only waits if the service is actually declared + sp<IPower> powerHal = waitForVintfService<IPower>(); + if (powerHal == nullptr) { + return nullptr; } + ALOGI("Loaded AIDL Power HAL service"); - bool setExpensiveRendering(bool enabled) override { - ALOGV("AIDL setExpensiveRendering %s", enabled ? "T" : "F"); - if (!mHasExpensiveRendering) { - ALOGV("Skipped sending EXPENSIVE_RENDERING because HAL doesn't support it"); - return true; - } + return std::make_unique<AidlPowerHalWrapper>(std::move(powerHal)); +} - auto ret = mPowerHal->setMode(Mode::EXPENSIVE_RENDERING, enabled); - if (ret.isOk()) { - traceExpensiveRendering(enabled); - } - return ret.isOk(); +bool AidlPowerHalWrapper::setExpensiveRendering(bool enabled) { + ALOGV("AIDL setExpensiveRendering %s", enabled ? "T" : "F"); + if (!mHasExpensiveRendering) { + ALOGV("Skipped sending EXPENSIVE_RENDERING because HAL doesn't support it"); + return true; } - bool notifyDisplayUpdateImminent() override { - ALOGV("AIDL notifyDisplayUpdateImminent"); - if (!mHasDisplayUpdateImminent) { - ALOGV("Skipped sending DISPLAY_UPDATE_IMMINENT because HAL doesn't support it"); - return true; - } + auto ret = mPowerHal->setMode(Mode::EXPENSIVE_RENDERING, enabled); + if (ret.isOk()) { + traceExpensiveRendering(enabled); + } + return ret.isOk(); +} - auto ret = mPowerHal->setBoost(Boost::DISPLAY_UPDATE_IMMINENT, 0); - return ret.isOk(); +bool AidlPowerHalWrapper::notifyDisplayUpdateImminent() { + ALOGV("AIDL notifyDisplayUpdateImminent"); + if (!mHasDisplayUpdateImminent) { + ALOGV("Skipped sending DISPLAY_UPDATE_IMMINENT because HAL doesn't support it"); + return true; } - // only version 2+ of the aidl supports power hint sessions, hidl has no support - bool supportsPowerHintSession() override { return mSupportsPowerHint; } + auto ret = mPowerHal->setBoost(Boost::DISPLAY_UPDATE_IMMINENT, 0); + return ret.isOk(); +} + +// only version 2+ of the aidl supports power hint sessions, hidl has no support +bool AidlPowerHalWrapper::supportsPowerHintSession() { + return mSupportsPowerHint; +} + +bool AidlPowerHalWrapper::checkPowerHintSessionSupported() { + int64_t unused; + // Try to get preferred rate to determine if hint sessions are supported + // We check for isOk not EX_UNSUPPORTED_OPERATION to lump together errors + return mPowerHal->getHintSessionPreferredRate(&unused).isOk(); +} + +bool AidlPowerHalWrapper::isPowerHintSessionRunning() { + return mPowerHintSession != nullptr; +} - bool checkPowerHintSessionSupported() { - int64_t unused; - // Try to get preferred rate to determine if hint sessions are supported - // We check for isOk not EX_UNSUPPORTED_OPERATION to lump other errors - return mPowerHal->getHintSessionPreferredRate(&unused).isOk(); +void AidlPowerHalWrapper::closePowerHintSession() { + if (mPowerHintSession != nullptr) { + mPowerHintSession->close(); + mPowerHintSession = nullptr; } +} - bool isPowerHintSessionRunning() override { return mPowerHintSession != nullptr; } +void AidlPowerHalWrapper::restartPowerHintSession() { + closePowerHintSession(); + startPowerHintSession(); +} - void closePowerHintSession() { - if (mPowerHintSession != nullptr) { - mPowerHintSession->close(); - mPowerHintSession = nullptr; +void AidlPowerHalWrapper::setPowerHintSessionThreadIds(const std::vector<int32_t>& threadIds) { + if (threadIds != mPowerHintThreadIds) { + mPowerHintThreadIds = threadIds; + if (isPowerHintSessionRunning()) { + restartPowerHintSession(); } } +} - void restartPowerHintSession() { - closePowerHintSession(); - startPowerHintSession(); +bool AidlPowerHalWrapper::startPowerHintSession() { + if (mPowerHintSession != nullptr || mPowerHintThreadIds.empty()) { + ALOGV("Cannot start power hint session, skipping"); + return false; + } + auto ret = + mPowerHal->createHintSession(getpid(), static_cast<int32_t>(getuid()), + mPowerHintThreadIds, mTargetDuration, &mPowerHintSession); + if (!ret.isOk()) { + ALOGW("Failed to start power hint session with error: %s", + ret.exceptionToString(ret.exceptionCode()).c_str()); + } else { + mLastTargetDurationSent = mTargetDuration; } + return isPowerHintSessionRunning(); +} - void setPowerHintSessionThreadIds(const std::vector<int32_t>& threadIds) override { - if (threadIds != mPowerHintThreadIds) { - mPowerHintThreadIds = threadIds; - if (isPowerHintSessionRunning()) { - restartPowerHintSession(); - } - } +bool AidlPowerHalWrapper::shouldSetTargetDuration(int64_t targetDurationNanos) { + if (targetDurationNanos <= 0) { + return false; } + // report if the change in target from our last submission to now exceeds the threshold + return abs(1.0 - + static_cast<double>(mLastTargetDurationSent) / + static_cast<double>(targetDurationNanos)) >= kAllowedTargetDeviationPercent; +} - bool startPowerHintSession() override { - if (mPowerHintSession != nullptr || mPowerHintThreadIds.empty()) { - ALOGV("Cannot start power hint session, skipping"); - return false; +void AidlPowerHalWrapper::setTargetWorkDuration(int64_t targetDurationNanos) { + ATRACE_CALL(); + mTargetDuration = targetDurationNanos; + if (sTraceHintSessionData) ATRACE_INT64("Time target", targetDurationNanos); + if (!sNormalizeTarget && isPowerHintSessionRunning() && + shouldSetTargetDuration(targetDurationNanos)) { + if (mLastActualDurationSent.has_value()) { + // update the error term here since we are actually sending an update to powerhal + if (sTraceHintSessionData) + ATRACE_INT64("Target error term", targetDurationNanos - *mLastActualDurationSent); } - auto ret = mPowerHal->createHintSession(getpid(), static_cast<int32_t>(getuid()), - mPowerHintThreadIds, mTargetDuration, - &mPowerHintSession); + ALOGV("Sending target time: %" PRId64 "ns", targetDurationNanos); + mLastTargetDurationSent = targetDurationNanos; + auto ret = mPowerHintSession->updateTargetWorkDuration(targetDurationNanos); if (!ret.isOk()) { - ALOGW("Failed to start power hint session with error: %s", - ret.exceptionToString(ret.exceptionCode()).c_str()); - } else { - mLastTargetDurationSent = mTargetDuration; - } - return isPowerHintSessionRunning(); - } - - bool shouldSetTargetDuration(int64_t targetDurationNanos) { - // report if the change in target from our last submission to now exceeds the threshold - return abs(1.0 - - static_cast<double>(mLastTargetDurationSent) / - static_cast<double>(targetDurationNanos)) >= - kAllowedTargetDeviationPercent; - } - - void setTargetWorkDuration(int64_t targetDurationNanos) override { - ATRACE_CALL(); - mTargetDuration = targetDurationNanos; - if (sTraceHintSessionData) ATRACE_INT64("Time target", targetDurationNanos); - if (!sNormalizeTarget && shouldSetTargetDuration(targetDurationNanos) && - isPowerHintSessionRunning()) { - if (mLastActualDurationSent.has_value()) { - // update the error term here since we are actually sending an update to powerhal - if (sTraceHintSessionData) - ATRACE_INT64("Target error term", - targetDurationNanos - *mLastActualDurationSent); - } - ALOGV("Sending target time: %lld ns", static_cast<long long>(targetDurationNanos)); - mLastTargetDurationSent = targetDurationNanos; - auto ret = mPowerHintSession->updateTargetWorkDuration(targetDurationNanos); - if (!ret.isOk()) { - ALOGW("Failed to set power hint target work duration with error: %s", - ret.exceptionMessage().c_str()); - mShouldReconnectHal = true; - } + ALOGW("Failed to set power hint target work duration with error: %s", + ret.exceptionMessage().c_str()); + mShouldReconnectHal = true; } } +} - bool shouldReportActualDurationsNow() { - // report if we have never reported before or are approaching a stale session - if (!mLastActualDurationSent.has_value() || - (systemTime() - mLastActualReportTimestamp) > kStaleTimeout.count()) { - return true; - } +bool AidlPowerHalWrapper::shouldReportActualDurationsNow() { + // report if we have never reported before or are approaching a stale session + if (!mLastActualDurationSent.has_value() || + (systemTime() - mLastActualReportTimestamp) > kStaleTimeout.count()) { + return true; + } - if (!mActualDuration.has_value()) { - return false; - } + if (!mActualDuration.has_value()) { + return false; + } - // duration of most recent timing - const double mostRecentActualDuration = static_cast<double>(*mActualDuration); - // duration of the last timing actually reported to the powerhal - const double lastReportedActualDuration = static_cast<double>(*mLastActualDurationSent); + // duration of most recent timing + const double mostRecentActualDuration = static_cast<double>(*mActualDuration); + // duration of the last timing actually reported to the powerhal + const double lastReportedActualDuration = static_cast<double>(*mLastActualDurationSent); - // report if the change in duration from then to now exceeds the threshold - return abs(1.0 - mostRecentActualDuration / lastReportedActualDuration) >= - kAllowedActualDeviationPercent; - } + // report if the change in duration from then to now exceeds the threshold + return abs(1.0 - mostRecentActualDuration / lastReportedActualDuration) >= + kAllowedActualDeviationPercent; +} - void sendActualWorkDuration(int64_t actualDurationNanos, nsecs_t timeStampNanos) override { - ATRACE_CALL(); +void AidlPowerHalWrapper::sendActualWorkDuration(int64_t actualDurationNanos, + nsecs_t timeStampNanos) { + ATRACE_CALL(); - if (actualDurationNanos < 0 || !isPowerHintSessionRunning()) { - ALOGV("Failed to send actual work duration, skipping"); - return; - } + if (actualDurationNanos < 0 || !isPowerHintSessionRunning()) { + ALOGV("Failed to send actual work duration, skipping"); + return; + } - WorkDuration duration; - duration.durationNanos = actualDurationNanos; - mActualDuration = actualDurationNanos; + WorkDuration duration; + duration.durationNanos = actualDurationNanos; + mActualDuration = actualDurationNanos; - // normalize the sent values to a pre-set target - if (sNormalizeTarget) { - duration.durationNanos += mLastTargetDurationSent - mTargetDuration; - } - duration.timeStampNanos = timeStampNanos; - mPowerHintQueue.push_back(duration); + // normalize the sent values to a pre-set target + if (sNormalizeTarget) { + duration.durationNanos += mLastTargetDurationSent - mTargetDuration; + } + duration.timeStampNanos = timeStampNanos; + mPowerHintQueue.push_back(duration); - long long targetNsec = mTargetDuration; - long long durationNsec = actualDurationNanos; + nsecs_t targetNsec = mTargetDuration; + nsecs_t durationNsec = actualDurationNanos; - if (sTraceHintSessionData) { - ATRACE_INT64("Measured duration", durationNsec); - ATRACE_INT64("Target error term", targetNsec - durationNsec); - } + if (sTraceHintSessionData) { + ATRACE_INT64("Measured duration", durationNsec); + ATRACE_INT64("Target error term", targetNsec - durationNsec); + } - ALOGV("Sending actual work duration of: %lld on target: %lld with error: %lld", - durationNsec, targetNsec, targetNsec - durationNsec); - - // This rate limiter queues similar duration reports to the powerhal into - // batches to avoid excessive binder calls. The criteria to send a given batch - // are outlined in shouldReportActualDurationsNow() - if (shouldReportActualDurationsNow()) { - ALOGV("Sending hint update batch"); - mLastActualReportTimestamp = systemTime(); - auto ret = mPowerHintSession->reportActualWorkDuration(mPowerHintQueue); - if (!ret.isOk()) { - ALOGW("Failed to report actual work durations with error: %s", - ret.exceptionMessage().c_str()); - mShouldReconnectHal = true; - } - mPowerHintQueue.clear(); - // we save the non-normalized value here to detect % changes - mLastActualDurationSent = actualDurationNanos; + ALOGV("Sending actual work duration of: %" PRId64 " on target: %" PRId64 + " with error: %" PRId64, + durationNsec, targetNsec, targetNsec - durationNsec); + + // This rate limiter queues similar duration reports to the powerhal into + // batches to avoid excessive binder calls. The criteria to send a given batch + // are outlined in shouldReportActualDurationsNow() + if (shouldReportActualDurationsNow()) { + ALOGV("Sending hint update batch"); + mLastActualReportTimestamp = systemTime(); + auto ret = mPowerHintSession->reportActualWorkDuration(mPowerHintQueue); + if (!ret.isOk()) { + ALOGW("Failed to report actual work durations with error: %s", + ret.exceptionMessage().c_str()); + mShouldReconnectHal = true; } + mPowerHintQueue.clear(); + // we save the non-normalized value here to detect % changes + mLastActualDurationSent = actualDurationNanos; } +} - bool shouldReconnectHAL() override { return mShouldReconnectHal; } - - std::vector<int32_t> getPowerHintSessionThreadIds() override { return mPowerHintThreadIds; } +bool AidlPowerHalWrapper::shouldReconnectHAL() { + return mShouldReconnectHal; +} - std::optional<int64_t> getTargetWorkDuration() override { return mTargetDuration; } +std::vector<int32_t> AidlPowerHalWrapper::getPowerHintSessionThreadIds() { + return mPowerHintThreadIds; +} -private: - const sp<IPower> mPowerHal = nullptr; - bool mHasExpensiveRendering = false; - bool mHasDisplayUpdateImminent = false; - // Used to indicate an error state and need for reconstruction - bool mShouldReconnectHal = false; - // This is not thread safe, but is currently protected by mPowerHalMutex so it needs no lock - sp<IPowerHintSession> mPowerHintSession = nullptr; - // Queue of actual durations saved to report - std::vector<WorkDuration> mPowerHintQueue; - // The latest un-normalized values we have received for target and actual - int64_t mTargetDuration = kDefaultTarget.count(); - std::optional<int64_t> mActualDuration; - // The list of thread ids, stored so we can restart the session from this class if needed - std::vector<int32_t> mPowerHintThreadIds; - bool mSupportsPowerHint; - // Keep track of the last messages sent for rate limiter change detection - std::optional<int64_t> mLastActualDurationSent; - // timestamp of the last report we sent, used to avoid stale sessions - int64_t mLastActualReportTimestamp = 0; - int64_t mLastTargetDurationSent = kDefaultTarget.count(); - // Whether to normalize all the actual values as error terms relative to a constant target - // This saves a binder call by not setting the target, and should not affect the pid values - static const bool sNormalizeTarget; - // Whether we should emit ATRACE_INT data for hint sessions - static const bool sTraceHintSessionData; - // Max percent the actual duration can vary without causing a report (eg: 0.1 = 10%) - static constexpr double kAllowedActualDeviationPercent = 0.1; - // Max percent the target duration can vary without causing a report (eg: 0.05 = 5%) - static constexpr double kAllowedTargetDeviationPercent = 0.05; - // Target used for init and normalization, the actual value does not really matter - static constexpr const std::chrono::nanoseconds kDefaultTarget = 50ms; - // Amount of time after the last message was sent before the session goes stale - // actually 100ms but we use 80 here to ideally avoid going stale - static constexpr const std::chrono::nanoseconds kStaleTimeout = 80ms; -}; +std::optional<int64_t> AidlPowerHalWrapper::getTargetWorkDuration() { + return mTargetDuration; +} const bool AidlPowerHalWrapper::sTraceHintSessionData = base::GetBoolProperty(std::string("debug.sf.trace_hint_sessions"), false); diff --git a/services/surfaceflinger/DisplayHardware/PowerAdvisor.h b/services/surfaceflinger/DisplayHardware/PowerAdvisor.h index 0db56aad42..3f47ffdd53 100644 --- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.h +++ b/services/surfaceflinger/DisplayHardware/PowerAdvisor.h @@ -22,6 +22,7 @@ #include <utils/Mutex.h> +#include <android/hardware/power/IPower.h> #include <ui/DisplayIdentification.h> #include "../Scheduler/OneShotTimer.h" @@ -118,6 +119,69 @@ private: scheduler::OneShotTimer mScreenUpdateTimer; }; +class AidlPowerHalWrapper : public PowerAdvisor::HalWrapper { +public: + explicit AidlPowerHalWrapper(sp<hardware::power::IPower> powerHal); + ~AidlPowerHalWrapper() override; + + static std::unique_ptr<HalWrapper> connect(); + + bool setExpensiveRendering(bool enabled) override; + bool notifyDisplayUpdateImminent() override; + bool supportsPowerHintSession() override; + bool isPowerHintSessionRunning() override; + void restartPowerHintSession() override; + void setPowerHintSessionThreadIds(const std::vector<int32_t>& threadIds) override; + bool startPowerHintSession() override; + void setTargetWorkDuration(int64_t targetDurationNanos) override; + void sendActualWorkDuration(int64_t actualDurationNanos, nsecs_t timeStampNanos) override; + bool shouldReconnectHAL() override; + std::vector<int32_t> getPowerHintSessionThreadIds() override; + std::optional<int64_t> getTargetWorkDuration() override; + +private: + bool checkPowerHintSessionSupported(); + void closePowerHintSession(); + bool shouldReportActualDurationsNow(); + bool shouldSetTargetDuration(int64_t targetDurationNanos); + + const sp<hardware::power::IPower> mPowerHal = nullptr; + bool mHasExpensiveRendering = false; + bool mHasDisplayUpdateImminent = false; + // Used to indicate an error state and need for reconstruction + bool mShouldReconnectHal = false; + // This is not thread safe, but is currently protected by mPowerHalMutex so it needs no lock + sp<hardware::power::IPowerHintSession> mPowerHintSession = nullptr; + // Queue of actual durations saved to report + std::vector<hardware::power::WorkDuration> mPowerHintQueue; + // The latest un-normalized values we have received for target and actual + int64_t mTargetDuration = kDefaultTarget.count(); + std::optional<int64_t> mActualDuration; + // The list of thread ids, stored so we can restart the session from this class if needed + std::vector<int32_t> mPowerHintThreadIds; + bool mSupportsPowerHint; + // Keep track of the last messages sent for rate limiter change detection + std::optional<int64_t> mLastActualDurationSent; + // timestamp of the last report we sent, used to avoid stale sessions + int64_t mLastActualReportTimestamp = 0; + int64_t mLastTargetDurationSent = kDefaultTarget.count(); + // Whether to normalize all the actual values as error terms relative to a constant target + // This saves a binder call by not setting the target, and should not affect the pid values + static const bool sNormalizeTarget; + // Whether we should emit ATRACE_INT data for hint sessions + static const bool sTraceHintSessionData; + + // Max percent the actual duration can vary without causing a report (eg: 0.1 = 10%) + static constexpr double kAllowedActualDeviationPercent = 0.1; + // Max percent the target duration can vary without causing a report (eg: 0.05 = 5%) + static constexpr double kAllowedTargetDeviationPercent = 0.05; + // Target used for init and normalization, the actual value does not really matter + static constexpr const std::chrono::nanoseconds kDefaultTarget = 50ms; + // Amount of time after the last message was sent before the session goes stale + // actually 100ms but we use 80 here to ideally avoid going stale + static constexpr const std::chrono::nanoseconds kStaleTimeout = 80ms; +}; + } // namespace impl } // namespace Hwc2 } // namespace android diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index 4fc9d273fc..dc62cdaefc 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -681,7 +681,9 @@ void SurfaceFlinger::bootFinished() { if (renderEngineTid.has_value()) { tidList.emplace_back(*renderEngineTid); } - mPowerAdvisor.startPowerHintSession(tidList); + if (!mPowerAdvisor.startPowerHintSession(tidList)) { + ALOGW("Cannot start power hint session"); + } } mBootStage = BootStage::FINISHED; diff --git a/services/surfaceflinger/tests/fakehwc/Android.bp b/services/surfaceflinger/tests/fakehwc/Android.bp index 168b57612e..704815de3a 100644 --- a/services/surfaceflinger/tests/fakehwc/Android.bp +++ b/services/surfaceflinger/tests/fakehwc/Android.bp @@ -28,6 +28,7 @@ cc_test { "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 0000000000..e25a0aea24 --- /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 1eea023f17..cc9d48cfcc 100644 --- a/services/surfaceflinger/tests/unittests/Android.bp +++ b/services/surfaceflinger/tests/unittests/Android.bp @@ -26,6 +26,8 @@ filegroup { 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 @@ cc_test { ":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 0000000000..2323ebb4aa --- /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 0000000000..0ddc90d585 --- /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 0000000000..770bc15869 --- /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 0000000000..439f6f4e75 --- /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 |