diff options
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 |