Add unit test for AidlPowerHalWrapper
Bug: b/195990840
Test: atest libsurfaceflinger_unittest:libsurfaceflinger_unittest.AidlPowerHalWrapperTest
Change-Id: I5b2197904445a83e5d586aa6331ccc3d46db22a3
diff --git a/services/surfaceflinger/CompositionEngine/Android.bp b/services/surfaceflinger/CompositionEngine/Android.bp
index aefc014..516c3ef 100644
--- a/services/surfaceflinger/CompositionEngine/Android.bp
+++ b/services/surfaceflinger/CompositionEngine/Android.bp
@@ -23,6 +23,7 @@
"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 44c086d..05f488b 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 scheduler::OneShotTimer;
-class AidlPowerHalWrapper;
-
PowerAdvisor::~PowerAdvisor() = default;
namespace {
@@ -294,259 +291,232 @@
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;
- }
-
- mSupportsPowerHint = checkPowerHintSessionSupported();
+AidlPowerHalWrapper::AidlPowerHalWrapper(sp<IPower> powerHal) : mPowerHal(std::move(powerHal)) {
+ auto ret = mPowerHal->isModeSupported(Mode::EXPENSIVE_RENDERING, &mHasExpensiveRendering);
+ if (!ret.isOk()) {
+ mHasExpensiveRendering = false;
}
- ~AidlPowerHalWrapper() override {
- if (mPowerHintSession != nullptr) {
- mPowerHintSession->close();
- mPowerHintSession = nullptr;
- }
- };
-
- 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");
-
- return std::make_unique<AidlPowerHalWrapper>(std::move(powerHal));
+ ret = mPowerHal->isBoostSupported(Boost::DISPLAY_UPDATE_IMMINENT, &mHasDisplayUpdateImminent);
+ if (!ret.isOk()) {
+ mHasDisplayUpdateImminent = false;
}
- 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;
- }
+ mSupportsPowerHint = checkPowerHintSessionSupported();
+}
- auto ret = mPowerHal->setMode(Mode::EXPENSIVE_RENDERING, enabled);
- if (ret.isOk()) {
- traceExpensiveRendering(enabled);
- }
- return ret.isOk();
+AidlPowerHalWrapper::~AidlPowerHalWrapper() {
+ if (mPowerHintSession != nullptr) {
+ mPowerHintSession->close();
+ mPowerHintSession = nullptr;
}
-
- 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->setBoost(Boost::DISPLAY_UPDATE_IMMINENT, 0);
- return ret.isOk();
- }
-
- // only version 2+ of the aidl supports power hint sessions, hidl has no support
- bool supportsPowerHintSession() override { return mSupportsPowerHint; }
-
- 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();
- }
-
- bool isPowerHintSessionRunning() override { return mPowerHintSession != nullptr; }
-
- void closePowerHintSession() {
- if (mPowerHintSession != nullptr) {
- mPowerHintSession->close();
- mPowerHintSession = nullptr;
- }
- }
-
- void restartPowerHintSession() {
- closePowerHintSession();
- startPowerHintSession();
- }
-
- void setPowerHintSessionThreadIds(const std::vector<int32_t>& threadIds) override {
- if (threadIds != mPowerHintThreadIds) {
- mPowerHintThreadIds = threadIds;
- if (isPowerHintSessionRunning()) {
- restartPowerHintSession();
- }
- }
- }
-
- bool startPowerHintSession() override {
- 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();
- }
-
- 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;
- }
- }
- }
-
- 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;
- }
-
- 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);
-
- // 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();
-
- if (actualDurationNanos < 0 || !isPowerHintSessionRunning()) {
- ALOGV("Failed to send actual work duration, skipping");
- return;
- }
-
- 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);
-
- long long targetNsec = mTargetDuration;
- long long durationNsec = actualDurationNanos;
-
- 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;
- }
- }
-
- bool shouldReconnectHAL() override { return mShouldReconnectHal; }
-
- std::vector<int32_t> getPowerHintSessionThreadIds() override { return mPowerHintThreadIds; }
-
- std::optional<int64_t> getTargetWorkDuration() override { return mTargetDuration; }
-
-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::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");
+
+ return std::make_unique<AidlPowerHalWrapper>(std::move(powerHal));
+}
+
+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;
+ }
+
+ auto ret = mPowerHal->setMode(Mode::EXPENSIVE_RENDERING, enabled);
+ if (ret.isOk()) {
+ traceExpensiveRendering(enabled);
+ }
+ 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;
+ }
+
+ 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;
+}
+
+void AidlPowerHalWrapper::closePowerHintSession() {
+ if (mPowerHintSession != nullptr) {
+ mPowerHintSession->close();
+ mPowerHintSession = nullptr;
+ }
+}
+
+void AidlPowerHalWrapper::restartPowerHintSession() {
+ closePowerHintSession();
+ startPowerHintSession();
+}
+
+void AidlPowerHalWrapper::setPowerHintSessionThreadIds(const std::vector<int32_t>& threadIds) {
+ if (threadIds != mPowerHintThreadIds) {
+ mPowerHintThreadIds = threadIds;
+ if (isPowerHintSessionRunning()) {
+ restartPowerHintSession();
+ }
+ }
+}
+
+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();
+}
+
+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;
+}
+
+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);
+ }
+ ALOGV("Sending target time: %" PRId64 "ns", 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;
+ }
+ }
+}
+
+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;
+ }
+
+ // 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;
+}
+
+void AidlPowerHalWrapper::sendActualWorkDuration(int64_t actualDurationNanos,
+ nsecs_t timeStampNanos) {
+ ATRACE_CALL();
+
+ if (actualDurationNanos < 0 || !isPowerHintSessionRunning()) {
+ ALOGV("Failed to send actual work duration, skipping");
+ return;
+ }
+
+ 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);
+
+ nsecs_t targetNsec = mTargetDuration;
+ nsecs_t durationNsec = actualDurationNanos;
+
+ if (sTraceHintSessionData) {
+ ATRACE_INT64("Measured duration", durationNsec);
+ ATRACE_INT64("Target error term", targetNsec - durationNsec);
+ }
+
+ 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 AidlPowerHalWrapper::shouldReconnectHAL() {
+ return mShouldReconnectHal;
+}
+
+std::vector<int32_t> AidlPowerHalWrapper::getPowerHintSessionThreadIds() {
+ return mPowerHintThreadIds;
+}
+
+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 0db56aa..3f47ffd 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 @@
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 9f5a7df..5f2b5eb 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -729,7 +729,9 @@
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 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