Expanded poweradvisor to handle adpf hint sessions

This patch exposes several new methods from the PowerAdvisor to enable SurfaceFlinger to set up hint sessions and send updates for the desired and actual durations.

Bug: 195990840
Test: make;
manual testing

Change-Id: Ice16c09309be81d9d661dfb818882cfea8e4d4aa
diff --git a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
index 1765caf..5c2390e 100644
--- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
+++ b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
@@ -19,7 +19,10 @@
 #undef LOG_TAG
 #define LOG_TAG "PowerAdvisor"
 
+#include <unistd.h>
 #include <cinttypes>
+#include <cstdint>
+#include <optional>
 
 #include <android-base/properties.h>
 #include <utils/Log.h>
@@ -27,6 +30,9 @@
 
 #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>
+
 #include <binder/IServiceManager.h>
 
 #include "../SurfaceFlingerProperties.h"
@@ -47,10 +53,14 @@
 
 using android::hardware::power::Boost;
 using android::hardware::power::IPower;
+using android::hardware::power::IPowerHintSession;
 using android::hardware::power::Mode;
-using base::GetIntProperty;
+using android::hardware::power::WorkDuration;
+
 using scheduler::OneShotTimer;
 
+class AidlPowerHalWrapper;
+
 PowerAdvisor::~PowerAdvisor() = default;
 
 namespace {
@@ -83,6 +93,13 @@
 
 void PowerAdvisor::onBootFinished() {
     mBootFinished.store(true);
+    {
+        std::lock_guard lock(mPowerHalMutex);
+        HalWrapper* halWrapper = getPowerHal();
+        if (halWrapper != nullptr && usePowerHintSession()) {
+            mPowerHintSessionRunning = halWrapper->startPowerHintSession();
+        }
+    }
 }
 
 void PowerAdvisor::setExpensiveRenderingExpected(DisplayId displayId, bool expected) {
@@ -136,6 +153,80 @@
     }
 }
 
+// checks both if it supports and if it's enabled
+bool PowerAdvisor::usePowerHintSession() {
+    // uses cached value since the underlying support and flag are unlikely to change at runtime
+    ALOGE_IF(!mPowerHintEnabled.has_value(), "Power hint session cannot be used before boot!");
+    return mPowerHintEnabled.value_or(false) && supportsPowerHintSession();
+}
+
+bool PowerAdvisor::supportsPowerHintSession() {
+    // cache to avoid needing lock every time
+    if (!mSupportsPowerHint.has_value()) {
+        std::lock_guard lock(mPowerHalMutex);
+        HalWrapper* const halWrapper = getPowerHal();
+        mSupportsPowerHint = halWrapper->supportsPowerHintSession();
+    }
+    return *mSupportsPowerHint;
+}
+
+bool PowerAdvisor::isPowerHintSessionRunning() {
+    return mPowerHintSessionRunning;
+}
+
+void PowerAdvisor::setTargetWorkDuration(int64_t targetDurationNanos) {
+    // we check "supports" here not "usePowerHintSession" because this needs to work
+    // before the session is actually running, and "use" will always fail before boot
+    // we store the values passed in before boot to start the session with during onBootFinished
+    if (!supportsPowerHintSession()) {
+        ALOGV("Power hint session target duration cannot be set, skipping");
+        return;
+    }
+    {
+        std::lock_guard lock(mPowerHalMutex);
+        HalWrapper* const halWrapper = getPowerHal();
+        if (halWrapper != nullptr) {
+            halWrapper->setTargetWorkDuration(targetDurationNanos);
+        }
+    }
+}
+
+void PowerAdvisor::setPowerHintSessionThreadIds(const std::vector<int32_t>& threadIds) {
+    // we check "supports" here not "usePowerHintSession" because this needs to wsork
+    // before the session is actually running, and "use" will always fail before boot.
+    // we store the values passed in before boot to start the session with during onBootFinished
+    if (!supportsPowerHintSession()) {
+        ALOGV("Power hint session thread ids cannot be set, skipping");
+        return;
+    }
+    {
+        std::lock_guard lock(mPowerHalMutex);
+        HalWrapper* const halWrapper = getPowerHal();
+        if (halWrapper != nullptr) {
+            halWrapper->setPowerHintSessionThreadIds(const_cast<std::vector<int32_t>&>(threadIds));
+        }
+    }
+}
+
+void PowerAdvisor::sendActualWorkDuration(int64_t actualDurationNanos, nsecs_t timeStampNanos) {
+    if (!mBootFinished || !usePowerHintSession()) {
+        ALOGV("Actual work duration power hint cannot be sent, skipping");
+        return;
+    }
+    {
+        std::lock_guard lock(mPowerHalMutex);
+        HalWrapper* const halWrapper = getPowerHal();
+        if (halWrapper != nullptr) {
+            halWrapper->sendActualWorkDuration(actualDurationNanos, timeStampNanos);
+        }
+    }
+}
+
+// needs to be set after the flag is known but before PowerAdvisor enters onBootFinished
+void PowerAdvisor::enablePowerHint(bool enabled) {
+    mPowerHintEnabled = enabled;
+}
+
 class HidlPowerHalWrapper : public PowerAdvisor::HalWrapper {
 public:
     HidlPowerHalWrapper(sp<V1_3::IPower> powerHal) : mPowerHal(std::move(powerHal)) {}
@@ -178,6 +269,26 @@
         return true;
     }
 
+    bool supportsPowerHintSession() override { return false; }
+
+    bool isPowerHintSessionRunning() override { return false; }
+
+    void restartPowerHintSession() override {}
+
+    void setPowerHintSessionThreadIds(const std::vector<int32_t>&) override {}
+
+    bool startPowerHintSession() override { return false; }
+
+    void setTargetWorkDuration(int64_t) override {}
+
+    void sendActualWorkDuration(int64_t, nsecs_t) override {}
+
+    bool shouldReconnectHAL() override { return false; }
+
+    std::vector<int32_t> getPowerHintSessionThreadIds() override { return std::vector<int32_t>{}; }
+
+    std::optional<int64_t> getTargetWorkDuration() override { return std::nullopt; }
+
 private:
     const sp<V1_3::IPower> mPowerHal = nullptr;
 };
@@ -195,9 +306,21 @@
         if (!ret.isOk()) {
             mHasDisplayUpdateImminent = false;
         }
+
+        // This just gives a number not a binder status, so no .isOk()
+        mSupportsPowerHints = mPowerHal->getInterfaceVersion() >= 2;
+
+        if (mSupportsPowerHints) {
+            mPowerHintQueue.reserve(MAX_QUEUE_SIZE);
+        }
     }
 
-    ~AidlPowerHalWrapper() override = default;
+    ~AidlPowerHalWrapper() override {
+        if (mPowerHintSession != nullptr) {
+            mPowerHintSession->close();
+            mPowerHintSession = nullptr;
+        }
+    };
 
     static std::unique_ptr<HalWrapper> connect() {
         // This only waits if the service is actually declared
@@ -232,10 +355,147 @@
         return ret.isOk();
     }
 
+    // only version 2+ of the aidl supports power hint sessions, hidl has no support
+    bool supportsPowerHintSession() override { return mSupportsPowerHints; }
+
+    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 || !mPowerHintTargetDuration.has_value() ||
+            mPowerHintThreadIds.empty()) {
+            ALOGV("Cannot start power hint session, skipping");
+            return false;
+        }
+        auto ret = mPowerHal->createHintSession(getpid(), static_cast<int32_t>(getuid()),
+                                                mPowerHintThreadIds, *mPowerHintTargetDuration,
+                                                &mPowerHintSession);
+        if (!ret.isOk()) {
+            ALOGW("Failed to start power hint session with error: %s",
+                  ret.exceptionToString(ret.exceptionCode()).c_str());
+            // Indicate to the poweradvisor that this wrapper likely needs to be remade
+            mShouldReconnectHal = true;
+        }
+        return isPowerHintSessionRunning();
+    }
+
+    bool shouldSetTargetDuration(int64_t targetDurationNanos) {
+        if (!mLastTargetDurationSent.has_value()) {
+            return true;
+        }
+
+        // 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)) >=
+                ALLOWED_TARGET_DEVIATION_PERCENT;
+    }
+
+    void setTargetWorkDuration(int64_t targetDurationNanos) override {
+        mPowerHintTargetDuration = targetDurationNanos;
+        if (shouldSetTargetDuration(targetDurationNanos) && isPowerHintSessionRunning()) {
+            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 have exceeded the max queue size
+        if (!mLastMessageReported.has_value() || mPowerHintQueue.size() >= MAX_QUEUE_SIZE) {
+            return true;
+        }
+
+        // duration of most recent timing
+        const double mostRecentActualDuration =
+                static_cast<double>(mPowerHintQueue.back().durationNanos);
+        // duration of the last timing actually reported to the powerhal
+        const double lastReportedActualDuration =
+                static_cast<double>(mLastMessageReported->durationNanos);
+
+        // report if the change in duration from then to now exceeds the threshold
+        return abs(1.0 - mostRecentActualDuration / lastReportedActualDuration) >=
+                ALLOWED_ACTUAL_DEVIATION_PERCENT;
+    }
+
+    void sendActualWorkDuration(int64_t actualDurationNanos, nsecs_t timeStampNanos) override {
+        if (actualDurationNanos < 0 || !isPowerHintSessionRunning()) {
+            ALOGV("Failed to send actual work duration, skipping");
+            return;
+        }
+
+        WorkDuration duration;
+        duration.durationNanos = actualDurationNanos;
+        duration.timeStampNanos = timeStampNanos;
+        mPowerHintQueue.push_back(duration);
+
+        // 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()) {
+            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();
+            mLastMessageReported = duration;
+        }
+    }
+
+    bool shouldReconnectHAL() override { return mShouldReconnectHal; }
+
+    std::vector<int32_t> getPowerHintSessionThreadIds() override { return mPowerHintThreadIds; }
+
+    std::optional<int64_t> getTargetWorkDuration() override { return mPowerHintTargetDuration; }
+
 private:
+    // max number of messages allowed in mPowerHintQueue before reporting is forced
+    static constexpr int32_t MAX_QUEUE_SIZE = 15;
+    // max percent the actual duration can vary without causing a report (eg: 0.1 = 10%)
+    static constexpr double ALLOWED_ACTUAL_DEVIATION_PERCENT = 0.1;
+    // max percent the target duration can vary without causing a report (eg: 0.05 = 5%)
+    static constexpr double ALLOWED_TARGET_DEVIATION_PERCENT = 0.05;
+
     const sp<IPower> mPowerHal = nullptr;
     bool mHasExpensiveRendering = false;
     bool mHasDisplayUpdateImminent = false;
+    bool mShouldReconnectHal = false; // used to indicate an error state and need for reconstruction
+    // This is not thread safe, but is currently protected by mPowerHalMutex so it needs no lock
+    sp<IPowerHintSession> mPowerHintSession = nullptr;
+    std::vector<WorkDuration> mPowerHintQueue;
+    // halwrapper owns these values so we can init when we want and reconnect if broken
+    std::optional<int64_t> mPowerHintTargetDuration;
+    std::vector<int32_t> mPowerHintThreadIds;
+    // keep track of the last messages sent for rate limiter change detection
+    std::optional<WorkDuration> mLastMessageReported;
+    std::optional<int64_t> mLastTargetDurationSent;
+    bool mSupportsPowerHints;
 };
 
 PowerAdvisor::HalWrapper* PowerAdvisor::getPowerHal() {
@@ -246,6 +506,15 @@
         return nullptr;
     }
 
+    // grab old hint session values before we destroy any existing wrapper
+    std::vector<int32_t> oldPowerHintSessionThreadIds;
+    std::optional<int64_t> oldTargetWorkDuration;
+
+    if (sHalWrapper != nullptr) {
+        oldPowerHintSessionThreadIds = sHalWrapper->getPowerHintSessionThreadIds();
+        oldTargetWorkDuration = sHalWrapper->getTargetWorkDuration();
+    }
+
     // If we used to have a HAL, but it stopped responding, attempt to reconnect
     if (mReconnectPowerHal) {
         sHalWrapper = nullptr;
@@ -253,15 +522,34 @@
     }
 
     if (sHalWrapper != nullptr) {
-        return sHalWrapper.get();
+        auto wrapper = sHalWrapper.get();
+        // if the wrapper is fine, return it, but if it indicates a reconnect, remake it
+        if (!wrapper->shouldReconnectHAL()) {
+            return wrapper;
+        }
+        sHalWrapper = nullptr;
     }
 
+    // at this point, we know for sure there is no running session
+    mPowerHintSessionRunning = false;
+
     // First attempt to connect to the AIDL Power HAL
     sHalWrapper = AidlPowerHalWrapper::connect();
 
     // If that didn't succeed, attempt to connect to the HIDL Power HAL
     if (sHalWrapper == nullptr) {
         sHalWrapper = HidlPowerHalWrapper::connect();
+    } else { // if AIDL, pass on any existing hint session values
+        // thread ids always safe to set
+        sHalWrapper->setPowerHintSessionThreadIds(oldPowerHintSessionThreadIds);
+        // only set duration and start if duration is defined
+        if (oldTargetWorkDuration.has_value()) {
+            sHalWrapper->setTargetWorkDuration(*oldTargetWorkDuration);
+            // only start if possible to run and both threadids and duration are defined
+            if (usePowerHintSession() && !oldPowerHintSessionThreadIds.empty()) {
+                mPowerHintSessionRunning = sHalWrapper->startPowerHintSession();
+            }
+        }
     }
 
     // If we make it to this point and still don't have a HAL, it's unlikely we