| /* | 
 |  * Copyright 2018 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. | 
 |  */ | 
 |  | 
 | //#define LOG_NDEBUG 0 | 
 |  | 
 | #define ATRACE_TAG ATRACE_TAG_GRAPHICS | 
 |  | 
 | #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> | 
 | #include <utils/Mutex.h> | 
 | #include <utils/Trace.h> | 
 |  | 
 | #include <android/hardware/power/1.3/IPower.h> | 
 | #include <android/hardware/power/IPowerHintSession.h> | 
 | #include <android/hardware/power/WorkDuration.h> | 
 |  | 
 | #include <binder/IServiceManager.h> | 
 |  | 
 | #include "../SurfaceFlingerProperties.h" | 
 |  | 
 | #include "PowerAdvisor.h" | 
 | #include "SurfaceFlinger.h" | 
 |  | 
 | namespace android { | 
 | namespace Hwc2 { | 
 |  | 
 | PowerAdvisor::~PowerAdvisor() = default; | 
 |  | 
 | namespace impl { | 
 |  | 
 | namespace V1_0 = android::hardware::power::V1_0; | 
 | namespace V1_3 = android::hardware::power::V1_3; | 
 | using V1_3::PowerHint; | 
 |  | 
 | using android::hardware::power::Boost; | 
 | using android::hardware::power::IPower; | 
 | using android::hardware::power::IPowerHintSession; | 
 | using android::hardware::power::Mode; | 
 | using android::hardware::power::WorkDuration; | 
 |  | 
 | PowerAdvisor::~PowerAdvisor() = default; | 
 |  | 
 | namespace { | 
 | std::chrono::milliseconds getUpdateTimeout() { | 
 |     // Default to a timeout of 80ms if nothing else is specified | 
 |     static std::chrono::milliseconds timeout = | 
 |             std::chrono::milliseconds(sysprop::display_update_imminent_timeout_ms(80)); | 
 |     return timeout; | 
 | } | 
 |  | 
 | void traceExpensiveRendering(bool enabled) { | 
 |     if (enabled) { | 
 |         ATRACE_ASYNC_BEGIN("ExpensiveRendering", 0); | 
 |     } else { | 
 |         ATRACE_ASYNC_END("ExpensiveRendering", 0); | 
 |     } | 
 | } | 
 |  | 
 | } // namespace | 
 |  | 
 | PowerAdvisor::PowerAdvisor(SurfaceFlinger& flinger) : mFlinger(flinger) { | 
 |     if (getUpdateTimeout() > 0ms) { | 
 |         mScreenUpdateTimer.emplace("UpdateImminentTimer", getUpdateTimeout(), | 
 |                                    /* resetCallback */ nullptr, | 
 |                                    /* timeoutCallback */ | 
 |                                    [this] { | 
 |                                        while (true) { | 
 |                                            auto timeSinceLastUpdate = std::chrono::nanoseconds( | 
 |                                                    systemTime() - mLastScreenUpdatedTime.load()); | 
 |                                            if (timeSinceLastUpdate >= getUpdateTimeout()) { | 
 |                                                break; | 
 |                                            } | 
 |                                            // We may try to disable expensive rendering and allow | 
 |                                            // for sending DISPLAY_UPDATE_IMMINENT hints too early if | 
 |                                            // we idled very shortly after updating the screen, so | 
 |                                            // make sure we wait enough time. | 
 |                                            std::this_thread::sleep_for(getUpdateTimeout() - | 
 |                                                                        timeSinceLastUpdate); | 
 |                                        } | 
 |                                        mSendUpdateImminent.store(true); | 
 |                                        mFlinger.disableExpensiveRendering(); | 
 |                                    }); | 
 |     } | 
 | } | 
 |  | 
 | void PowerAdvisor::init() { | 
 |     // Defer starting the screen update timer until SurfaceFlinger finishes construction. | 
 |     if (mScreenUpdateTimer) { | 
 |         mScreenUpdateTimer->start(); | 
 |     } | 
 | } | 
 |  | 
 | void PowerAdvisor::onBootFinished() { | 
 |     mBootFinished.store(true); | 
 | } | 
 |  | 
 | void PowerAdvisor::setExpensiveRenderingExpected(DisplayId displayId, bool expected) { | 
 |     if (expected) { | 
 |         mExpensiveDisplays.insert(displayId); | 
 |     } else { | 
 |         mExpensiveDisplays.erase(displayId); | 
 |     } | 
 |  | 
 |     const bool expectsExpensiveRendering = !mExpensiveDisplays.empty(); | 
 |     if (mNotifiedExpensiveRendering != expectsExpensiveRendering) { | 
 |         std::lock_guard lock(mPowerHalMutex); | 
 |         HalWrapper* const halWrapper = getPowerHal(); | 
 |         if (halWrapper == nullptr) { | 
 |             return; | 
 |         } | 
 |  | 
 |         if (!halWrapper->setExpensiveRendering(expectsExpensiveRendering)) { | 
 |             // The HAL has become unavailable; attempt to reconnect later | 
 |             mReconnectPowerHal = true; | 
 |             return; | 
 |         } | 
 |  | 
 |         mNotifiedExpensiveRendering = expectsExpensiveRendering; | 
 |     } | 
 | } | 
 |  | 
 | void PowerAdvisor::notifyDisplayUpdateImminent() { | 
 |     // Only start sending this notification once the system has booted so we don't introduce an | 
 |     // early-boot dependency on Power HAL | 
 |     if (!mBootFinished.load()) { | 
 |         return; | 
 |     } | 
 |  | 
 |     if (mSendUpdateImminent.exchange(false)) { | 
 |         std::lock_guard lock(mPowerHalMutex); | 
 |         HalWrapper* const halWrapper = getPowerHal(); | 
 |         if (halWrapper == nullptr) { | 
 |             return; | 
 |         } | 
 |  | 
 |         if (!halWrapper->notifyDisplayUpdateImminent()) { | 
 |             // The HAL has become unavailable; attempt to reconnect later | 
 |             mReconnectPowerHal = true; | 
 |             return; | 
 |         } | 
 |  | 
 |         if (mScreenUpdateTimer) { | 
 |             mScreenUpdateTimer->reset(); | 
 |         } else { | 
 |             // If we don't have a screen update timer, then we don't throttle power hal calls so | 
 |             // flip this bit back to allow for calling into power hal again. | 
 |             mSendUpdateImminent.store(true); | 
 |         } | 
 |     } | 
 |  | 
 |     if (mScreenUpdateTimer) { | 
 |         mLastScreenUpdatedTime.store(systemTime()); | 
 |     } | 
 | } | 
 |  | 
 | // 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 | 
 |     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 && halWrapper->supportsPowerHintSession(); | 
 |     } | 
 |     return *mSupportsPowerHint; | 
 | } | 
 |  | 
 | bool PowerAdvisor::isPowerHintSessionRunning() { | 
 |     return mPowerHintSessionRunning; | 
 | } | 
 |  | 
 | void PowerAdvisor::setTargetWorkDuration(Duration targetDuration) { | 
 |     if (!usePowerHintSession()) { | 
 |         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(targetDuration); | 
 |         } | 
 |     } | 
 | } | 
 |  | 
 | void PowerAdvisor::sendActualWorkDuration() { | 
 |     if (!mBootFinished || !usePowerHintSession()) { | 
 |         ALOGV("Actual work duration power hint cannot be sent, skipping"); | 
 |         return; | 
 |     } | 
 |     const std::optional<Duration> actualDuration = estimateWorkDuration(false); | 
 |     if (actualDuration.has_value()) { | 
 |         std::lock_guard lock(mPowerHalMutex); | 
 |         HalWrapper* const halWrapper = getPowerHal(); | 
 |         if (halWrapper != nullptr) { | 
 |             halWrapper->sendActualWorkDuration(*actualDuration + kTargetSafetyMargin, | 
 |                                                TimePoint::now()); | 
 |         } | 
 |     } | 
 | } | 
 |  | 
 | void PowerAdvisor::sendPredictedWorkDuration() { | 
 |     if (!mBootFinished || !usePowerHintSession()) { | 
 |         ALOGV("Actual work duration power hint cannot be sent, skipping"); | 
 |         return; | 
 |     } | 
 |  | 
 |     const std::optional<Duration> predictedDuration = estimateWorkDuration(true); | 
 |  | 
 |     if (predictedDuration.has_value()) { | 
 |         std::lock_guard lock(mPowerHalMutex); | 
 |         HalWrapper* const halWrapper = getPowerHal(); | 
 |         if (halWrapper != nullptr) { | 
 |             halWrapper->sendActualWorkDuration(*predictedDuration + kTargetSafetyMargin, | 
 |                                                TimePoint::now()); | 
 |         } | 
 |     } | 
 | } | 
 |  | 
 | void PowerAdvisor::enablePowerHint(bool enabled) { | 
 |     mPowerHintEnabled = enabled; | 
 | } | 
 |  | 
 | bool PowerAdvisor::startPowerHintSession(const std::vector<int32_t>& threadIds) { | 
 |     if (!usePowerHintSession()) { | 
 |         ALOGI("Power hint session cannot be started, skipping"); | 
 |     } | 
 |     { | 
 |         std::lock_guard lock(mPowerHalMutex); | 
 |         HalWrapper* halWrapper = getPowerHal(); | 
 |         if (halWrapper != nullptr && usePowerHintSession()) { | 
 |             halWrapper->setPowerHintSessionThreadIds(threadIds); | 
 |             mPowerHintSessionRunning = halWrapper->startPowerHintSession(); | 
 |         } | 
 |     } | 
 |     return mPowerHintSessionRunning; | 
 | } | 
 |  | 
 | void PowerAdvisor::setGpuFenceTime(DisplayId displayId, std::unique_ptr<FenceTime>&& fenceTime) { | 
 |     DisplayTimingData& displayData = mDisplayTimingData[displayId]; | 
 |     if (displayData.gpuEndFenceTime) { | 
 |         nsecs_t signalTime = displayData.gpuEndFenceTime->getSignalTime(); | 
 |         if (signalTime != Fence::SIGNAL_TIME_INVALID && signalTime != Fence::SIGNAL_TIME_PENDING) { | 
 |             for (auto&& [_, otherDisplayData] : mDisplayTimingData) { | 
 |                 // If the previous display started before us but ended after we should have | 
 |                 // started, then it likely delayed our start time and we must compensate for that. | 
 |                 // Displays finishing earlier should have already made their way through this call | 
 |                 // and swapped their timing into "lastValid" from "latest", so we check that here. | 
 |                 if (!otherDisplayData.lastValidGpuStartTime.has_value()) continue; | 
 |                 if ((*otherDisplayData.lastValidGpuStartTime < *displayData.gpuStartTime) && | 
 |                     (*otherDisplayData.lastValidGpuEndTime > *displayData.gpuStartTime)) { | 
 |                     displayData.lastValidGpuStartTime = *otherDisplayData.lastValidGpuEndTime; | 
 |                     break; | 
 |                 } | 
 |             } | 
 |             displayData.lastValidGpuStartTime = displayData.gpuStartTime; | 
 |             displayData.lastValidGpuEndTime = TimePoint::fromNs(signalTime); | 
 |         } | 
 |     } | 
 |     displayData.gpuEndFenceTime = std::move(fenceTime); | 
 |     displayData.gpuStartTime = TimePoint::now(); | 
 | } | 
 |  | 
 | void PowerAdvisor::setHwcValidateTiming(DisplayId displayId, TimePoint validateStartTime, | 
 |                                         TimePoint validateEndTime) { | 
 |     DisplayTimingData& displayData = mDisplayTimingData[displayId]; | 
 |     displayData.hwcValidateStartTime = validateStartTime; | 
 |     displayData.hwcValidateEndTime = validateEndTime; | 
 | } | 
 |  | 
 | void PowerAdvisor::setHwcPresentTiming(DisplayId displayId, TimePoint presentStartTime, | 
 |                                        TimePoint presentEndTime) { | 
 |     DisplayTimingData& displayData = mDisplayTimingData[displayId]; | 
 |     displayData.hwcPresentStartTime = presentStartTime; | 
 |     displayData.hwcPresentEndTime = presentEndTime; | 
 | } | 
 |  | 
 | void PowerAdvisor::setSkippedValidate(DisplayId displayId, bool skipped) { | 
 |     mDisplayTimingData[displayId].skippedValidate = skipped; | 
 | } | 
 |  | 
 | void PowerAdvisor::setRequiresClientComposition(DisplayId displayId, | 
 |                                                 bool requiresClientComposition) { | 
 |     mDisplayTimingData[displayId].usedClientComposition = requiresClientComposition; | 
 | } | 
 |  | 
 | void PowerAdvisor::setExpectedPresentTime(TimePoint expectedPresentTime) { | 
 |     mExpectedPresentTimes.append(expectedPresentTime); | 
 | } | 
 |  | 
 | void PowerAdvisor::setSfPresentTiming(TimePoint presentFenceTime, TimePoint presentEndTime) { | 
 |     mLastSfPresentEndTime = presentEndTime; | 
 |     mLastPresentFenceTime = presentFenceTime; | 
 | } | 
 |  | 
 | void PowerAdvisor::setFrameDelay(Duration frameDelayDuration) { | 
 |     mFrameDelayDuration = frameDelayDuration; | 
 | } | 
 |  | 
 | void PowerAdvisor::setHwcPresentDelayedTime(DisplayId displayId, TimePoint earliestFrameStartTime) { | 
 |     mDisplayTimingData[displayId].hwcPresentDelayedTime = earliestFrameStartTime; | 
 | } | 
 |  | 
 | void PowerAdvisor::setCommitStart(TimePoint commitStartTime) { | 
 |     mCommitStartTimes.append(commitStartTime); | 
 | } | 
 |  | 
 | void PowerAdvisor::setCompositeEnd(TimePoint compositeEndTime) { | 
 |     mLastPostcompDuration = compositeEndTime - mLastSfPresentEndTime; | 
 | } | 
 |  | 
 | void PowerAdvisor::setDisplays(std::vector<DisplayId>& displayIds) { | 
 |     mDisplayIds = displayIds; | 
 | } | 
 |  | 
 | void PowerAdvisor::setTotalFrameTargetWorkDuration(Duration targetDuration) { | 
 |     mTotalFrameTargetDuration = targetDuration; | 
 | } | 
 |  | 
 | std::vector<DisplayId> PowerAdvisor::getOrderedDisplayIds( | 
 |         std::optional<TimePoint> DisplayTimingData::*sortBy) { | 
 |     std::vector<DisplayId> sortedDisplays; | 
 |     std::copy_if(mDisplayIds.begin(), mDisplayIds.end(), std::back_inserter(sortedDisplays), | 
 |                  [&](DisplayId id) { | 
 |                      return mDisplayTimingData.count(id) && | 
 |                              (mDisplayTimingData[id].*sortBy).has_value(); | 
 |                  }); | 
 |     std::sort(sortedDisplays.begin(), sortedDisplays.end(), [&](DisplayId idA, DisplayId idB) { | 
 |         return *(mDisplayTimingData[idA].*sortBy) < *(mDisplayTimingData[idB].*sortBy); | 
 |     }); | 
 |     return sortedDisplays; | 
 | } | 
 |  | 
 | std::optional<Duration> PowerAdvisor::estimateWorkDuration(bool earlyHint) { | 
 |     if (earlyHint && (!mExpectedPresentTimes.isFull() || !mCommitStartTimes.isFull())) { | 
 |         return std::nullopt; | 
 |     } | 
 |  | 
 |     // Tracks when we finish presenting to hwc | 
 |     TimePoint estimatedEndTime = mCommitStartTimes[0]; | 
 |  | 
 |     // How long we spent this frame not doing anything, waiting for fences or vsync | 
 |     Duration idleDuration = 0ns; | 
 |  | 
 |     // Most recent previous gpu end time in the current frame, probably from a prior display, used | 
 |     // as the start time for the next gpu operation if it ran over time since it probably blocked | 
 |     std::optional<TimePoint> previousValidGpuEndTime; | 
 |  | 
 |     // The currently estimated gpu end time for the frame, | 
 |     // used to accumulate gpu time as we iterate over the active displays | 
 |     std::optional<TimePoint> estimatedGpuEndTime; | 
 |  | 
 |     // If we're predicting at the start of the frame, we use last frame as our reference point | 
 |     // If we're predicting at the end of the frame, we use the current frame as a reference point | 
 |     TimePoint referenceFrameStartTime = (earlyHint ? mCommitStartTimes[-1] : mCommitStartTimes[0]); | 
 |  | 
 |     // When the prior frame should be presenting to the display | 
 |     // If we're predicting at the start of the frame, we use last frame's expected present time | 
 |     // If we're predicting at the end of the frame, the present fence time is already known | 
 |     TimePoint lastFramePresentTime = | 
 |             (earlyHint ? mExpectedPresentTimes[-1] : mLastPresentFenceTime); | 
 |  | 
 |     // The timing info for the previously calculated display, if there was one | 
 |     std::optional<DisplayTimeline> previousDisplayReferenceTiming; | 
 |     std::vector<DisplayId>&& displayIds = | 
 |             getOrderedDisplayIds(&DisplayTimingData::hwcPresentStartTime); | 
 |     DisplayTimeline referenceTiming, estimatedTiming; | 
 |  | 
 |     // Iterate over the displays that use hwc in the same order they are presented | 
 |     for (DisplayId displayId : displayIds) { | 
 |         if (mDisplayTimingData.count(displayId) == 0) { | 
 |             continue; | 
 |         } | 
 |  | 
 |         auto& displayData = mDisplayTimingData.at(displayId); | 
 |  | 
 |         // mLastPresentFenceTime should always be the time of the reference frame, since it will be | 
 |         // the previous frame's present fence if called at the start, and current frame's if called | 
 |         // at the end | 
 |         referenceTiming = displayData.calculateDisplayTimeline(mLastPresentFenceTime); | 
 |  | 
 |         // If this is the first display, include the duration before hwc present starts | 
 |         if (!previousDisplayReferenceTiming.has_value()) { | 
 |             estimatedEndTime += referenceTiming.hwcPresentStartTime - referenceFrameStartTime; | 
 |         } else { // Otherwise add the time since last display's hwc present finished | 
 |             estimatedEndTime += referenceTiming.hwcPresentStartTime - | 
 |                     previousDisplayReferenceTiming->hwcPresentEndTime; | 
 |         } | 
 |  | 
 |         // Late hint can re-use reference timing here since it's estimating its own reference frame | 
 |         estimatedTiming = earlyHint | 
 |                 ? referenceTiming.estimateTimelineFromReference(lastFramePresentTime, | 
 |                                                                 estimatedEndTime) | 
 |                 : referenceTiming; | 
 |  | 
 |         // Update predicted present finish time with this display's present time | 
 |         estimatedEndTime = estimatedTiming.hwcPresentEndTime; | 
 |  | 
 |         // Track how long we spent waiting for the fence, can be excluded from the timing estimate | 
 |         idleDuration += estimatedTiming.probablyWaitsForPresentFence | 
 |                 ? lastFramePresentTime - estimatedTiming.presentFenceWaitStartTime | 
 |                 : 0ns; | 
 |  | 
 |         // Track how long we spent waiting to present, can be excluded from the timing estimate | 
 |         idleDuration += earlyHint ? 0ns : referenceTiming.hwcPresentDelayDuration; | 
 |  | 
 |         // Estimate the reference frame's gpu timing | 
 |         auto gpuTiming = displayData.estimateGpuTiming(previousValidGpuEndTime); | 
 |         if (gpuTiming.has_value()) { | 
 |             previousValidGpuEndTime = gpuTiming->startTime + gpuTiming->duration; | 
 |  | 
 |             // Estimate the prediction frame's gpu end time from the reference frame | 
 |             estimatedGpuEndTime = std::max(estimatedTiming.hwcPresentStartTime, | 
 |                                            estimatedGpuEndTime.value_or(TimePoint{0ns})) + | 
 |                     gpuTiming->duration; | 
 |         } | 
 |         previousDisplayReferenceTiming = referenceTiming; | 
 |     } | 
 |     ATRACE_INT64("Idle duration", idleDuration.ns()); | 
 |  | 
 |     TimePoint estimatedFlingerEndTime = earlyHint ? estimatedEndTime : mLastSfPresentEndTime; | 
 |  | 
 |     // Don't count time spent idly waiting in the estimate as we could do more work in that time | 
 |     estimatedEndTime -= idleDuration; | 
 |     estimatedFlingerEndTime -= idleDuration; | 
 |  | 
 |     // We finish the frame when both present and the gpu are done, so wait for the later of the two | 
 |     // Also add the frame delay duration since the target did not move while we were delayed | 
 |     Duration totalDuration = mFrameDelayDuration + | 
 |             std::max(estimatedEndTime, estimatedGpuEndTime.value_or(TimePoint{0ns})) - | 
 |             mCommitStartTimes[0]; | 
 |  | 
 |     // We finish SurfaceFlinger when post-composition finishes, so add that in here | 
 |     Duration flingerDuration = | 
 |             estimatedFlingerEndTime + mLastPostcompDuration - mCommitStartTimes[0]; | 
 |  | 
 |     // Combine the two timings into a single normalized one | 
 |     Duration combinedDuration = combineTimingEstimates(totalDuration, flingerDuration); | 
 |  | 
 |     return std::make_optional(combinedDuration); | 
 | } | 
 |  | 
 | Duration PowerAdvisor::combineTimingEstimates(Duration totalDuration, Duration flingerDuration) { | 
 |     Duration targetDuration{0ns}; | 
 |     { | 
 |         std::lock_guard lock(mPowerHalMutex); | 
 |         targetDuration = *getPowerHal()->getTargetWorkDuration(); | 
 |     } | 
 |     if (!mTotalFrameTargetDuration.has_value()) return flingerDuration; | 
 |  | 
 |     // Normalize total to the flinger target (vsync period) since that's how often we actually send | 
 |     // hints | 
 |     Duration normalizedTotalDuration = Duration::fromNs((targetDuration.ns() * totalDuration.ns()) / | 
 |                                                         mTotalFrameTargetDuration->ns()); | 
 |     return std::max(flingerDuration, normalizedTotalDuration); | 
 | } | 
 |  | 
 | PowerAdvisor::DisplayTimeline PowerAdvisor::DisplayTimeline::estimateTimelineFromReference( | 
 |         TimePoint fenceTime, TimePoint displayStartTime) { | 
 |     DisplayTimeline estimated; | 
 |     estimated.hwcPresentStartTime = displayStartTime; | 
 |  | 
 |     // We don't predict waiting for vsync alignment yet | 
 |     estimated.hwcPresentDelayDuration = 0ns; | 
 |  | 
 |     // How long we expect to run before we start waiting for the fence | 
 |     // For now just re-use last frame's post-present duration and assume it will not change much | 
 |     // Excludes time spent waiting for vsync since that's not going to be consistent | 
 |     estimated.presentFenceWaitStartTime = estimated.hwcPresentStartTime + | 
 |             (presentFenceWaitStartTime - (hwcPresentStartTime + hwcPresentDelayDuration)); | 
 |     estimated.probablyWaitsForPresentFence = fenceTime > estimated.presentFenceWaitStartTime; | 
 |     estimated.hwcPresentEndTime = postPresentFenceHwcPresentDuration + | 
 |             (estimated.probablyWaitsForPresentFence ? fenceTime | 
 |                                                     : estimated.presentFenceWaitStartTime); | 
 |     return estimated; | 
 | } | 
 |  | 
 | PowerAdvisor::DisplayTimeline PowerAdvisor::DisplayTimingData::calculateDisplayTimeline( | 
 |         TimePoint fenceTime) { | 
 |     DisplayTimeline timeline; | 
 |     // How long between calling hwc present and trying to wait on the fence | 
 |     const Duration fenceWaitStartDelay = | 
 |             (skippedValidate ? kFenceWaitStartDelaySkippedValidate : kFenceWaitStartDelayValidated); | 
 |  | 
 |     // Did our reference frame wait for an appropriate vsync before calling into hwc | 
 |     const bool waitedOnHwcPresentTime = hwcPresentDelayedTime.has_value() && | 
 |             *hwcPresentDelayedTime > *hwcPresentStartTime && | 
 |             *hwcPresentDelayedTime < *hwcPresentEndTime; | 
 |  | 
 |     // Use validate start here if we skipped it because we did validate + present together | 
 |     timeline.hwcPresentStartTime = skippedValidate ? *hwcValidateStartTime : *hwcPresentStartTime; | 
 |  | 
 |     // Use validate end here if we skipped it because we did validate + present together | 
 |     timeline.hwcPresentEndTime = skippedValidate ? *hwcValidateEndTime : *hwcPresentEndTime; | 
 |  | 
 |     // How long hwc present was delayed waiting for the next appropriate vsync | 
 |     timeline.hwcPresentDelayDuration = | 
 |             (waitedOnHwcPresentTime ? *hwcPresentDelayedTime - *hwcPresentStartTime : 0ns); | 
 |     // When we started waiting for the present fence after calling into hwc present | 
 |     timeline.presentFenceWaitStartTime = | 
 |             timeline.hwcPresentStartTime + timeline.hwcPresentDelayDuration + fenceWaitStartDelay; | 
 |     timeline.probablyWaitsForPresentFence = fenceTime > timeline.presentFenceWaitStartTime && | 
 |             fenceTime < timeline.hwcPresentEndTime; | 
 |  | 
 |     // How long we ran after we finished waiting for the fence but before hwc present finished | 
 |     timeline.postPresentFenceHwcPresentDuration = timeline.hwcPresentEndTime - | 
 |             (timeline.probablyWaitsForPresentFence ? fenceTime | 
 |                                                    : timeline.presentFenceWaitStartTime); | 
 |     return timeline; | 
 | } | 
 |  | 
 | std::optional<PowerAdvisor::GpuTimeline> PowerAdvisor::DisplayTimingData::estimateGpuTiming( | 
 |         std::optional<TimePoint> previousEndTime) { | 
 |     if (!(usedClientComposition && lastValidGpuStartTime.has_value() && gpuEndFenceTime)) { | 
 |         return std::nullopt; | 
 |     } | 
 |     const TimePoint latestGpuStartTime = | 
 |             std::max(previousEndTime.value_or(TimePoint{0ns}), *gpuStartTime); | 
 |     const nsecs_t gpuEndFenceSignal = gpuEndFenceTime->getSignalTime(); | 
 |     Duration gpuDuration{0ns}; | 
 |     if (gpuEndFenceSignal != Fence::SIGNAL_TIME_INVALID && | 
 |         gpuEndFenceSignal != Fence::SIGNAL_TIME_PENDING) { | 
 |         const TimePoint latestGpuEndTime = TimePoint::fromNs(gpuEndFenceSignal); | 
 |  | 
 |         // If we know how long the most recent gpu duration was, use that | 
 |         gpuDuration = latestGpuEndTime - latestGpuStartTime; | 
 |     } else if (lastValidGpuEndTime.has_value()) { | 
 |         // If we don't have the fence data, use the most recent information we do have | 
 |         gpuDuration = *lastValidGpuEndTime - *lastValidGpuStartTime; | 
 |         if (gpuEndFenceSignal == Fence::SIGNAL_TIME_PENDING) { | 
 |             // If pending but went over the previous duration, use current time as the end | 
 |             gpuDuration = std::max(gpuDuration, Duration{TimePoint::now() - latestGpuStartTime}); | 
 |         } | 
 |     } | 
 |     return GpuTimeline{.duration = gpuDuration, .startTime = latestGpuStartTime}; | 
 | } | 
 |  | 
 | class HidlPowerHalWrapper : public PowerAdvisor::HalWrapper { | 
 | public: | 
 |     HidlPowerHalWrapper(sp<V1_3::IPower> powerHal) : mPowerHal(std::move(powerHal)) {} | 
 |  | 
 |     ~HidlPowerHalWrapper() override = default; | 
 |  | 
 |     static std::unique_ptr<HalWrapper> connect() { | 
 |         // Power HAL 1.3 is not guaranteed to be available, thus we need to query | 
 |         // Power HAL 1.0 first and try to cast it to Power HAL 1.3. | 
 |         sp<V1_3::IPower> powerHal = nullptr; | 
 |         sp<V1_0::IPower> powerHal_1_0 = V1_0::IPower::getService(); | 
 |         if (powerHal_1_0 != nullptr) { | 
 |             // Try to cast to Power HAL 1.3 | 
 |             powerHal = V1_3::IPower::castFrom(powerHal_1_0); | 
 |             if (powerHal == nullptr) { | 
 |                 ALOGW("No Power HAL 1.3 service in system, disabling PowerAdvisor"); | 
 |             } else { | 
 |                 ALOGI("Loaded Power HAL 1.3 service"); | 
 |             } | 
 |         } else { | 
 |             ALOGW("No Power HAL found, disabling PowerAdvisor"); | 
 |         } | 
 |  | 
 |         if (powerHal == nullptr) { | 
 |             return nullptr; | 
 |         } | 
 |  | 
 |         return std::make_unique<HidlPowerHalWrapper>(std::move(powerHal)); | 
 |     } | 
 |  | 
 |     bool setExpensiveRendering(bool enabled) override { | 
 |         ALOGV("HIDL setExpensiveRendering %s", enabled ? "T" : "F"); | 
 |         auto ret = mPowerHal->powerHintAsync_1_3(PowerHint::EXPENSIVE_RENDERING, enabled); | 
 |         if (ret.isOk()) { | 
 |             traceExpensiveRendering(enabled); | 
 |         } | 
 |         return ret.isOk(); | 
 |     } | 
 |  | 
 |     bool notifyDisplayUpdateImminent() override { | 
 |         // Power HAL 1.x doesn't have a notification for this | 
 |         ALOGV("HIDL notifyUpdateImminent received but can't send"); | 
 |         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(Duration) override {} | 
 |  | 
 |     void sendActualWorkDuration(Duration, TimePoint) override {} | 
 |  | 
 |     bool shouldReconnectHAL() override { return false; } | 
 |  | 
 |     std::vector<int32_t> getPowerHintSessionThreadIds() override { return std::vector<int32_t>{}; } | 
 |  | 
 |     std::optional<Duration> getTargetWorkDuration() override { return std::nullopt; } | 
 |  | 
 | private: | 
 |     const sp<V1_3::IPower> mPowerHal = nullptr; | 
 | }; | 
 |  | 
 | AidlPowerHalWrapper::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() { | 
 |     if (mPowerHintSession != nullptr) { | 
 |         mPowerHintSession->close(); | 
 |         mPowerHintSession = nullptr; | 
 |     } | 
 | } | 
 |  | 
 | 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.ns(), | 
 |                                             &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 AidlPowerHalWrapper::setTargetWorkDuration(Duration targetDuration) { | 
 |     ATRACE_CALL(); | 
 |     mTargetDuration = targetDuration; | 
 |     if (sTraceHintSessionData) ATRACE_INT64("Time target", targetDuration.ns()); | 
 |     if (isPowerHintSessionRunning() && (targetDuration != mLastTargetDurationSent)) { | 
 |         ALOGV("Sending target time: %" PRId64 "ns", targetDuration.ns()); | 
 |         mLastTargetDurationSent = targetDuration; | 
 |         auto ret = mPowerHintSession->updateTargetWorkDuration(targetDuration.ns()); | 
 |         if (!ret.isOk()) { | 
 |             ALOGW("Failed to set power hint target work duration with error: %s", | 
 |                   ret.exceptionMessage().c_str()); | 
 |             mShouldReconnectHal = true; | 
 |         } | 
 |     } | 
 | } | 
 |  | 
 | void AidlPowerHalWrapper::sendActualWorkDuration(Duration actualDuration, TimePoint timestamp) { | 
 |     ATRACE_CALL(); | 
 |     if (actualDuration < 0ns || !isPowerHintSessionRunning()) { | 
 |         ALOGV("Failed to send actual work duration, skipping"); | 
 |         return; | 
 |     } | 
 |     mActualDuration = actualDuration; | 
 |     WorkDuration duration; | 
 |     duration.durationNanos = actualDuration.ns(); | 
 |     duration.timeStampNanos = timestamp.ns(); | 
 |     mPowerHintQueue.push_back(duration); | 
 |  | 
 |     if (sTraceHintSessionData) { | 
 |         ATRACE_INT64("Measured duration", actualDuration.ns()); | 
 |         ATRACE_INT64("Target error term", Duration{actualDuration - mTargetDuration}.ns()); | 
 |  | 
 |         ATRACE_INT64("Reported duration", actualDuration.ns()); | 
 |         ATRACE_INT64("Reported target", mLastTargetDurationSent.ns()); | 
 |         ATRACE_INT64("Reported target error term", | 
 |                      Duration{actualDuration - mLastTargetDurationSent}.ns()); | 
 |     } | 
 |  | 
 |     ALOGV("Sending actual work duration of: %" PRId64 " on reported target: %" PRId64 | 
 |           " with error: %" PRId64, | 
 |           actualDuration.ns(), mLastTargetDurationSent.ns(), | 
 |           Duration{actualDuration - mLastTargetDurationSent}.ns()); | 
 |  | 
 |     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(); | 
 | } | 
 |  | 
 | bool AidlPowerHalWrapper::shouldReconnectHAL() { | 
 |     return mShouldReconnectHal; | 
 | } | 
 |  | 
 | std::vector<int32_t> AidlPowerHalWrapper::getPowerHintSessionThreadIds() { | 
 |     return mPowerHintThreadIds; | 
 | } | 
 |  | 
 | std::optional<Duration> AidlPowerHalWrapper::getTargetWorkDuration() { | 
 |     return mTargetDuration; | 
 | } | 
 |  | 
 | const bool AidlPowerHalWrapper::sTraceHintSessionData = | 
 |         base::GetBoolProperty(std::string("debug.sf.trace_hint_sessions"), false); | 
 |  | 
 | PowerAdvisor::HalWrapper* PowerAdvisor::getPowerHal() { | 
 |     if (!mHasHal) { | 
 |         return nullptr; | 
 |     } | 
 |  | 
 |     // Grab old hint session values before we destroy any existing wrapper | 
 |     std::vector<int32_t> oldPowerHintSessionThreadIds; | 
 |     std::optional<Duration> oldTargetWorkDuration; | 
 |  | 
 |     if (mHalWrapper != nullptr) { | 
 |         oldPowerHintSessionThreadIds = mHalWrapper->getPowerHintSessionThreadIds(); | 
 |         oldTargetWorkDuration = mHalWrapper->getTargetWorkDuration(); | 
 |     } | 
 |  | 
 |     // If we used to have a HAL, but it stopped responding, attempt to reconnect | 
 |     if (mReconnectPowerHal) { | 
 |         mHalWrapper = nullptr; | 
 |         mReconnectPowerHal = false; | 
 |     } | 
 |  | 
 |     if (mHalWrapper != nullptr) { | 
 |         auto wrapper = mHalWrapper.get(); | 
 |         // If the wrapper is fine, return it, but if it indicates a reconnect, remake it | 
 |         if (!wrapper->shouldReconnectHAL()) { | 
 |             return wrapper; | 
 |         } | 
 |         ALOGD("Reconnecting Power HAL"); | 
 |         mHalWrapper = nullptr; | 
 |     } | 
 |  | 
 |     // At this point, we know for sure there is no running session | 
 |     mPowerHintSessionRunning = false; | 
 |  | 
 |     // First attempt to connect to the AIDL Power HAL | 
 |     mHalWrapper = AidlPowerHalWrapper::connect(); | 
 |  | 
 |     // If that didn't succeed, attempt to connect to the HIDL Power HAL | 
 |     if (mHalWrapper == nullptr) { | 
 |         mHalWrapper = HidlPowerHalWrapper::connect(); | 
 |     } else { | 
 |         ALOGD("Successfully connecting AIDL Power HAL"); | 
 |         // If AIDL, pass on any existing hint session values | 
 |         mHalWrapper->setPowerHintSessionThreadIds(oldPowerHintSessionThreadIds); | 
 |         // Only set duration and start if duration is defined | 
 |         if (oldTargetWorkDuration.has_value()) { | 
 |             mHalWrapper->setTargetWorkDuration(*oldTargetWorkDuration); | 
 |             // Only start if possible to run and both threadids and duration are defined | 
 |             if (usePowerHintSession() && !oldPowerHintSessionThreadIds.empty()) { | 
 |                 mPowerHintSessionRunning = mHalWrapper->startPowerHintSession(); | 
 |             } | 
 |         } | 
 |     } | 
 |  | 
 |     // If we make it to this point and still don't have a HAL, it's unlikely we | 
 |     // will, so stop trying | 
 |     if (mHalWrapper == nullptr) { | 
 |         mHasHal = false; | 
 |     } | 
 |  | 
 |     return mHalWrapper.get(); | 
 | } | 
 |  | 
 | } // namespace impl | 
 | } // namespace Hwc2 | 
 | } // namespace android |