From b1b277b9f9f33c5b7aa730a77698a7e4d29eacad Mon Sep 17 00:00:00 2001 From: Georg Veichtlbauer Date: Fri, 26 Jan 2024 10:54:44 +0100 Subject: [PATCH] a32: Import Google Pixel power-libperfmgr From hw/pixel @ 9a19470 Change-Id: Ia946accc63762af44db71180d293e05a28ec26e5 --- Android.bp | 4 + .../vintf/framework_compatibility_matrix.xml | 2 +- power-libperfmgr/Android.bp | 58 ++ power-libperfmgr/aidl/Power.cpp | 296 ++++++++++ power-libperfmgr/aidl/Power.h | 65 +++ power-libperfmgr/aidl/PowerExt.cpp | 95 ++++ power-libperfmgr/aidl/PowerExt.h | 52 ++ power-libperfmgr/aidl/PowerHintSession.cpp | 529 ++++++++++++++++++ power-libperfmgr/aidl/PowerHintSession.h | 157 ++++++ power-libperfmgr/aidl/PowerSessionManager.cpp | 262 +++++++++ power-libperfmgr/aidl/PowerSessionManager.h | 116 ++++ ...d.hardware.power-service.a32-libperfmgr.rc | 32 ++ .../android.hardware.power-service.a32.xml | 7 + power-libperfmgr/aidl/service.cpp | 88 +++ .../disp-power/DisplayLowPower.cpp | 87 +++ power-libperfmgr/disp-power/DisplayLowPower.h | 51 ++ .../disp-power/InteractionHandler.cpp | 283 ++++++++++ .../disp-power/InteractionHandler.h | 71 +++ 18 files changed, 2254 insertions(+), 1 deletion(-) create mode 100644 power-libperfmgr/Android.bp create mode 100644 power-libperfmgr/aidl/Power.cpp create mode 100644 power-libperfmgr/aidl/Power.h create mode 100644 power-libperfmgr/aidl/PowerExt.cpp create mode 100644 power-libperfmgr/aidl/PowerExt.h create mode 100644 power-libperfmgr/aidl/PowerHintSession.cpp create mode 100644 power-libperfmgr/aidl/PowerHintSession.h create mode 100644 power-libperfmgr/aidl/PowerSessionManager.cpp create mode 100644 power-libperfmgr/aidl/PowerSessionManager.h create mode 100644 power-libperfmgr/aidl/android.hardware.power-service.a32-libperfmgr.rc create mode 100644 power-libperfmgr/aidl/android.hardware.power-service.a32.xml create mode 100644 power-libperfmgr/aidl/service.cpp create mode 100644 power-libperfmgr/disp-power/DisplayLowPower.cpp create mode 100644 power-libperfmgr/disp-power/DisplayLowPower.h create mode 100644 power-libperfmgr/disp-power/InteractionHandler.cpp create mode 100644 power-libperfmgr/disp-power/InteractionHandler.h diff --git a/Android.bp b/Android.bp index 3b08534..f9167e9 100644 --- a/Android.bp +++ b/Android.bp @@ -5,4 +5,8 @@ // soong_namespace { + imports: [ + "hardware/google/interfaces", + "hardware/google/pixel", + ], } diff --git a/configs/vintf/framework_compatibility_matrix.xml b/configs/vintf/framework_compatibility_matrix.xml index e73d8ec..817f459 100644 --- a/configs/vintf/framework_compatibility_matrix.xml +++ b/configs/vintf/framework_compatibility_matrix.xml @@ -41,7 +41,7 @@ android.hardware.power - 2 + 4 IPower default diff --git a/power-libperfmgr/Android.bp b/power-libperfmgr/Android.bp new file mode 100644 index 0000000..24c4c31 --- /dev/null +++ b/power-libperfmgr/Android.bp @@ -0,0 +1,58 @@ +// +// Copyright (C) 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. + +cc_library { + name: "libdisppower-a32", + proprietary: true, + srcs: [ + "disp-power/DisplayLowPower.cpp", + "disp-power/InteractionHandler.cpp", + ], + shared_libs: [ + "libbase", + "libcutils", + "liblog", + "libperfmgr", + "libutils", + ], +} + +cc_binary { + name: "android.hardware.power-service.a32-libperfmgr", + relative_install_path: "hw", + init_rc: ["aidl/android.hardware.power-service.a32-libperfmgr.rc"], + vintf_fragments: ["aidl/android.hardware.power-service.a32.xml"], + vendor: true, + shared_libs: [ + "android.hardware.power-V4-ndk", + "libbase", + "libcutils", + "liblog", + "libutils", + "libbinder_ndk", + "libdisppower-a32", + "libperfmgr", + "libprocessgroup", + "pixel-power-ext-V1-ndk", + "libprotobuf-cpp-full", + ], + srcs: [ + "aidl/service.cpp", + "aidl/Power.cpp", + "aidl/PowerExt.cpp", + "aidl/PowerHintSession.cpp", + "aidl/PowerSessionManager.cpp", + ], +} diff --git a/power-libperfmgr/aidl/Power.cpp b/power-libperfmgr/aidl/Power.cpp new file mode 100644 index 0000000..382a7b7 --- /dev/null +++ b/power-libperfmgr/aidl/Power.cpp @@ -0,0 +1,296 @@ +/* + * Copyright (C) 2020 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_TAG "powerhal-libperfmgr" + +#include "Power.h" + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "PowerHintSession.h" +#include "PowerSessionManager.h" +#include "disp-power/DisplayLowPower.h" + +namespace aidl { +namespace google { +namespace hardware { +namespace power { +namespace impl { +namespace pixel { + +using ::aidl::google::hardware::power::impl::pixel::PowerHintSession; +using ::android::perfmgr::HintManager; + +constexpr char kPowerHalStateProp[] = "vendor.powerhal.state"; +constexpr char kPowerHalAudioProp[] = "vendor.powerhal.audio"; +constexpr char kPowerHalRenderingProp[] = "vendor.powerhal.rendering"; + +Power::Power(std::shared_ptr dlpw) + : mDisplayLowPower(dlpw), + mInteractionHandler(nullptr), + mVRModeOn(false), + mSustainedPerfModeOn(false) { + mInteractionHandler = std::make_unique(); + mInteractionHandler->Init(); + + std::string state = ::android::base::GetProperty(kPowerHalStateProp, ""); + if (state == "SUSTAINED_PERFORMANCE") { + LOG(INFO) << "Initialize with SUSTAINED_PERFORMANCE on"; + HintManager::GetInstance()->DoHint("SUSTAINED_PERFORMANCE"); + mSustainedPerfModeOn = true; + } else if (state == "VR") { + LOG(INFO) << "Initialize with VR on"; + HintManager::GetInstance()->DoHint(state); + mVRModeOn = true; + } else if (state == "VR_SUSTAINED_PERFORMANCE") { + LOG(INFO) << "Initialize with SUSTAINED_PERFORMANCE and VR on"; + HintManager::GetInstance()->DoHint("VR_SUSTAINED_PERFORMANCE"); + mSustainedPerfModeOn = true; + mVRModeOn = true; + } else { + LOG(INFO) << "Initialize PowerHAL"; + } + + state = ::android::base::GetProperty(kPowerHalAudioProp, ""); + if (state == "AUDIO_STREAMING_LOW_LATENCY") { + LOG(INFO) << "Initialize with AUDIO_LOW_LATENCY on"; + HintManager::GetInstance()->DoHint(state); + } + + state = ::android::base::GetProperty(kPowerHalRenderingProp, ""); + if (state == "EXPENSIVE_RENDERING") { + LOG(INFO) << "Initialize with EXPENSIVE_RENDERING on"; + HintManager::GetInstance()->DoHint("EXPENSIVE_RENDERING"); + } +} + +ndk::ScopedAStatus Power::setMode(Mode type, bool enabled) { + LOG(DEBUG) << "Power setMode: " << toString(type) << " to: " << enabled; + if (HintManager::GetInstance()->GetAdpfProfile() && + HintManager::GetInstance()->GetAdpfProfile()->mReportingRateLimitNs > 0) { + PowerSessionManager::getInstance()->updateHintMode(toString(type), enabled); + } + switch (type) { + case Mode::LOW_POWER: + mDisplayLowPower->SetDisplayLowPower(enabled); + if (enabled) { + HintManager::GetInstance()->DoHint(toString(type)); + } else { + HintManager::GetInstance()->EndHint(toString(type)); + } + break; + case Mode::SUSTAINED_PERFORMANCE: + if (enabled && !mSustainedPerfModeOn) { + if (!mVRModeOn) { // Sustained mode only. + HintManager::GetInstance()->DoHint("SUSTAINED_PERFORMANCE"); + } else { // Sustained + VR mode. + HintManager::GetInstance()->EndHint("VR"); + HintManager::GetInstance()->DoHint("VR_SUSTAINED_PERFORMANCE"); + } + mSustainedPerfModeOn = true; + } else if (!enabled && mSustainedPerfModeOn) { + HintManager::GetInstance()->EndHint("VR_SUSTAINED_PERFORMANCE"); + HintManager::GetInstance()->EndHint("SUSTAINED_PERFORMANCE"); + if (mVRModeOn) { // Switch back to VR Mode. + HintManager::GetInstance()->DoHint("VR"); + } + mSustainedPerfModeOn = false; + } + break; + case Mode::VR: + if (enabled && !mVRModeOn) { + if (!mSustainedPerfModeOn) { // VR mode only. + HintManager::GetInstance()->DoHint("VR"); + } else { // Sustained + VR mode. + HintManager::GetInstance()->EndHint("SUSTAINED_PERFORMANCE"); + HintManager::GetInstance()->DoHint("VR_SUSTAINED_PERFORMANCE"); + } + mVRModeOn = true; + } else if (!enabled && mVRModeOn) { + HintManager::GetInstance()->EndHint("VR_SUSTAINED_PERFORMANCE"); + HintManager::GetInstance()->EndHint("VR"); + if (mSustainedPerfModeOn) { // Switch back to sustained Mode. + HintManager::GetInstance()->DoHint("SUSTAINED_PERFORMANCE"); + } + mVRModeOn = false; + } + break; + case Mode::LAUNCH: + if (mVRModeOn || mSustainedPerfModeOn) { + break; + } + [[fallthrough]]; + case Mode::DOUBLE_TAP_TO_WAKE: + [[fallthrough]]; + case Mode::FIXED_PERFORMANCE: + [[fallthrough]]; + case Mode::EXPENSIVE_RENDERING: + [[fallthrough]]; + case Mode::INTERACTIVE: + [[fallthrough]]; + case Mode::DEVICE_IDLE: + [[fallthrough]]; + case Mode::DISPLAY_INACTIVE: + [[fallthrough]]; + case Mode::AUDIO_STREAMING_LOW_LATENCY: + [[fallthrough]]; + case Mode::CAMERA_STREAMING_SECURE: + [[fallthrough]]; + case Mode::CAMERA_STREAMING_LOW: + [[fallthrough]]; + case Mode::CAMERA_STREAMING_MID: + [[fallthrough]]; + case Mode::CAMERA_STREAMING_HIGH: + [[fallthrough]]; + case Mode::GAME_LOADING: + [[fallthrough]]; + default: + if (enabled) { + HintManager::GetInstance()->DoHint(toString(type)); + } else { + HintManager::GetInstance()->EndHint(toString(type)); + } + break; + } + + return ndk::ScopedAStatus::ok(); +} + +ndk::ScopedAStatus Power::isModeSupported(Mode type, bool *_aidl_return) { + bool supported = HintManager::GetInstance()->IsHintSupported(toString(type)); + // LOW_POWER handled insides PowerHAL specifically + if (type == Mode::LOW_POWER) { + supported = true; + } + LOG(INFO) << "Power mode " << toString(type) << " isModeSupported: " << supported; + *_aidl_return = supported; + return ndk::ScopedAStatus::ok(); +} + +ndk::ScopedAStatus Power::setBoost(Boost type, int32_t durationMs) { + LOG(DEBUG) << "Power setBoost: " << toString(type) << " duration: " << durationMs; + if (HintManager::GetInstance()->GetAdpfProfile() && + HintManager::GetInstance()->GetAdpfProfile()->mReportingRateLimitNs > 0) { + PowerSessionManager::getInstance()->updateHintBoost(toString(type), durationMs); + } + switch (type) { + case Boost::INTERACTION: + if (mVRModeOn || mSustainedPerfModeOn) { + break; + } + mInteractionHandler->Acquire(durationMs); + break; + case Boost::DISPLAY_UPDATE_IMMINENT: + [[fallthrough]]; + case Boost::ML_ACC: + [[fallthrough]]; + case Boost::AUDIO_LAUNCH: + [[fallthrough]]; + case Boost::CAMERA_LAUNCH: + [[fallthrough]]; + case Boost::CAMERA_SHOT: + [[fallthrough]]; + default: + if (mVRModeOn || mSustainedPerfModeOn) { + break; + } + if (durationMs > 0) { + HintManager::GetInstance()->DoHint(toString(type), + std::chrono::milliseconds(durationMs)); + } else if (durationMs == 0) { + HintManager::GetInstance()->DoHint(toString(type)); + } else { + HintManager::GetInstance()->EndHint(toString(type)); + } + break; + } + + return ndk::ScopedAStatus::ok(); +} + +ndk::ScopedAStatus Power::isBoostSupported(Boost type, bool *_aidl_return) { + bool supported = HintManager::GetInstance()->IsHintSupported(toString(type)); + LOG(INFO) << "Power boost " << toString(type) << " isBoostSupported: " << supported; + *_aidl_return = supported; + return ndk::ScopedAStatus::ok(); +} + +constexpr const char *boolToString(bool b) { + return b ? "true" : "false"; +} + +binder_status_t Power::dump(int fd, const char **, uint32_t) { + std::string buf(::android::base::StringPrintf( + "HintManager Running: %s\n" + "VRMode: %s\n" + "SustainedPerformanceMode: %s\n", + boolToString(HintManager::GetInstance()->IsRunning()), boolToString(mVRModeOn), + boolToString(mSustainedPerfModeOn))); + // Dump nodes through libperfmgr + HintManager::GetInstance()->DumpToFd(fd); + PowerSessionManager::getInstance()->dumpToFd(fd); + if (!::android::base::WriteStringToFd(buf, fd)) { + PLOG(ERROR) << "Failed to dump state to fd"; + } + fsync(fd); + return STATUS_OK; +} + +ndk::ScopedAStatus Power::createHintSession(int32_t tgid, int32_t uid, + const std::vector &threadIds, + int64_t durationNanos, + std::shared_ptr *_aidl_return) { + if (!HintManager::GetInstance()->GetAdpfProfile() || + HintManager::GetInstance()->GetAdpfProfile()->mReportingRateLimitNs <= 0) { + *_aidl_return = nullptr; + return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); + } + if (threadIds.size() == 0) { + LOG(ERROR) << "Error: threadIds.size() shouldn't be " << threadIds.size(); + *_aidl_return = nullptr; + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); + } + std::shared_ptr session = + ndk::SharedRefBase::make(tgid, uid, threadIds, durationNanos); + *_aidl_return = session; + return ndk::ScopedAStatus::ok(); +} + +ndk::ScopedAStatus Power::getHintSessionPreferredRate(int64_t *outNanoseconds) { + *outNanoseconds = HintManager::GetInstance()->GetAdpfProfile() + ? HintManager::GetInstance()->GetAdpfProfile()->mReportingRateLimitNs + : 0; + if (*outNanoseconds <= 0) { + return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); + } + + return ndk::ScopedAStatus::ok(); +} + +} // namespace pixel +} // namespace impl +} // namespace power +} // namespace hardware +} // namespace google +} // namespace aidl diff --git a/power-libperfmgr/aidl/Power.h b/power-libperfmgr/aidl/Power.h new file mode 100644 index 0000000..b7fd8f2 --- /dev/null +++ b/power-libperfmgr/aidl/Power.h @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2020 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 + +#include +#include +#include + +#include "disp-power/DisplayLowPower.h" +#include "disp-power/InteractionHandler.h" + +namespace aidl { +namespace google { +namespace hardware { +namespace power { +namespace impl { +namespace pixel { + +using ::aidl::android::hardware::power::Boost; +using ::aidl::android::hardware::power::IPowerHintSession; +using ::aidl::android::hardware::power::Mode; + +class Power : public ::aidl::android::hardware::power::BnPower { + public: + Power(std::shared_ptr dlpw); + ndk::ScopedAStatus setMode(Mode type, bool enabled) override; + ndk::ScopedAStatus isModeSupported(Mode type, bool *_aidl_return) override; + ndk::ScopedAStatus setBoost(Boost type, int32_t durationMs) override; + ndk::ScopedAStatus isBoostSupported(Boost type, bool *_aidl_return) override; + ndk::ScopedAStatus createHintSession(int32_t tgid, int32_t uid, + const std::vector &threadIds, + int64_t durationNanos, + std::shared_ptr *_aidl_return) override; + ndk::ScopedAStatus getHintSessionPreferredRate(int64_t *outNanoseconds) override; + binder_status_t dump(int fd, const char **args, uint32_t numArgs) override; + + private: + std::shared_ptr mDisplayLowPower; + std::unique_ptr mInteractionHandler; + std::atomic mVRModeOn; + std::atomic mSustainedPerfModeOn; +}; + +} // namespace pixel +} // namespace impl +} // namespace power +} // namespace hardware +} // namespace google +} // namespace aidl diff --git a/power-libperfmgr/aidl/PowerExt.cpp b/power-libperfmgr/aidl/PowerExt.cpp new file mode 100644 index 0000000..63e07c3 --- /dev/null +++ b/power-libperfmgr/aidl/PowerExt.cpp @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2020 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_TAG "android.hardware.power-service.a32.ext-libperfmgr" + +#include "PowerExt.h" + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "PowerSessionManager.h" + +namespace aidl { +namespace google { +namespace hardware { +namespace power { +namespace impl { +namespace pixel { + +using ::android::perfmgr::HintManager; + +ndk::ScopedAStatus PowerExt::setMode(const std::string &mode, bool enabled) { + LOG(DEBUG) << "PowerExt setMode: " << mode << " to: " << enabled; + + if (enabled) { + HintManager::GetInstance()->DoHint(mode); + } else { + HintManager::GetInstance()->EndHint(mode); + } + if (HintManager::GetInstance()->GetAdpfProfile() && + HintManager::GetInstance()->GetAdpfProfile()->mReportingRateLimitNs > 0) { + PowerSessionManager::getInstance()->updateHintMode(mode, enabled); + } + + return ndk::ScopedAStatus::ok(); +} + +ndk::ScopedAStatus PowerExt::isModeSupported(const std::string &mode, bool *_aidl_return) { + bool supported = HintManager::GetInstance()->IsHintSupported(mode); + LOG(INFO) << "PowerExt mode " << mode << " isModeSupported: " << supported; + *_aidl_return = supported; + return ndk::ScopedAStatus::ok(); +} + +ndk::ScopedAStatus PowerExt::setBoost(const std::string &boost, int32_t durationMs) { + LOG(DEBUG) << "PowerExt setBoost: " << boost << " duration: " << durationMs; + if (HintManager::GetInstance()->GetAdpfProfile() && + HintManager::GetInstance()->GetAdpfProfile()->mReportingRateLimitNs > 0) { + PowerSessionManager::getInstance()->updateHintBoost(boost, durationMs); + } + + if (durationMs > 0) { + HintManager::GetInstance()->DoHint(boost, std::chrono::milliseconds(durationMs)); + } else if (durationMs == 0) { + HintManager::GetInstance()->DoHint(boost); + } else { + HintManager::GetInstance()->EndHint(boost); + } + + return ndk::ScopedAStatus::ok(); +} + +ndk::ScopedAStatus PowerExt::isBoostSupported(const std::string &boost, bool *_aidl_return) { + bool supported = HintManager::GetInstance()->IsHintSupported(boost); + LOG(INFO) << "PowerExt boost " << boost << " isBoostSupported: " << supported; + *_aidl_return = supported; + return ndk::ScopedAStatus::ok(); +} + +} // namespace pixel +} // namespace impl +} // namespace power +} // namespace hardware +} // namespace google +} // namespace aidl diff --git a/power-libperfmgr/aidl/PowerExt.h b/power-libperfmgr/aidl/PowerExt.h new file mode 100644 index 0000000..6055276 --- /dev/null +++ b/power-libperfmgr/aidl/PowerExt.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2020 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 +#include + +#include +#include +#include + +#include "disp-power/DisplayLowPower.h" + +namespace aidl { +namespace google { +namespace hardware { +namespace power { +namespace impl { +namespace pixel { + +class PowerExt : public ::aidl::google::hardware::power::extension::pixel::BnPowerExt { + public: + PowerExt(std::shared_ptr dlpw) : mDisplayLowPower(dlpw) {} + ndk::ScopedAStatus setMode(const std::string &mode, bool enabled) override; + ndk::ScopedAStatus isModeSupported(const std::string &mode, bool *_aidl_return) override; + ndk::ScopedAStatus setBoost(const std::string &boost, int32_t durationMs) override; + ndk::ScopedAStatus isBoostSupported(const std::string &boost, bool *_aidl_return) override; + + private: + std::shared_ptr mDisplayLowPower; +}; + +} // namespace pixel +} // namespace impl +} // namespace power +} // namespace hardware +} // namespace google +} // namespace aidl diff --git a/power-libperfmgr/aidl/PowerHintSession.cpp b/power-libperfmgr/aidl/PowerHintSession.cpp new file mode 100644 index 0000000..eadc4cf --- /dev/null +++ b/power-libperfmgr/aidl/PowerHintSession.cpp @@ -0,0 +1,529 @@ +/* + * Copyright 2021 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_TAG "powerhal-libperfmgr" +#define ATRACE_TAG (ATRACE_TAG_POWER | ATRACE_TAG_HAL) + +#include "PowerHintSession.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "PowerSessionManager.h" + +namespace aidl { +namespace google { +namespace hardware { +namespace power { +namespace impl { +namespace pixel { + +using ::android::base::StringPrintf; +using ::android::perfmgr::AdpfConfig; +using ::android::perfmgr::HintManager; +using std::chrono::duration_cast; +using std::chrono::nanoseconds; + +namespace { + +static inline int64_t ns_to_100us(int64_t ns) { + return ns / 100000; +} + +} // namespace + +int64_t PowerHintSession::convertWorkDurationToBoostByPid( + const std::vector &actualDurations) { + std::shared_ptr adpfConfig = HintManager::GetInstance()->GetAdpfProfile(); + const nanoseconds &targetDuration = mDescriptor->duration; + int64_t &integral_error = mDescriptor->integral_error; + int64_t &previous_error = mDescriptor->previous_error; + uint64_t samplingWindowP = adpfConfig->mSamplingWindowP; + uint64_t samplingWindowI = adpfConfig->mSamplingWindowI; + uint64_t samplingWindowD = adpfConfig->mSamplingWindowD; + int64_t targetDurationNanos = (int64_t)targetDuration.count(); + int64_t length = actualDurations.size(); + int64_t p_start = + samplingWindowP == 0 || samplingWindowP > length ? 0 : length - samplingWindowP; + int64_t i_start = + samplingWindowI == 0 || samplingWindowI > length ? 0 : length - samplingWindowI; + int64_t d_start = + samplingWindowD == 0 || samplingWindowD > length ? 0 : length - samplingWindowD; + int64_t dt = ns_to_100us(targetDurationNanos); + int64_t err_sum = 0; + int64_t derivative_sum = 0; + for (int64_t i = std::min({p_start, i_start, d_start}); i < length; i++) { + int64_t actualDurationNanos = actualDurations[i].durationNanos; + if (std::abs(actualDurationNanos) > targetDurationNanos * 20) { + ALOGW("The actual duration is way far from the target (%" PRId64 " >> %" PRId64 ")", + actualDurationNanos, targetDurationNanos); + } + // PID control algorithm + int64_t error = ns_to_100us(actualDurationNanos - targetDurationNanos); + if (i >= d_start) { + derivative_sum += error - previous_error; + } + if (i >= p_start) { + err_sum += error; + } + if (i >= i_start) { + integral_error += error * dt; + integral_error = std::min(adpfConfig->getPidIHighDivI(), integral_error); + integral_error = std::max(adpfConfig->getPidILowDivI(), integral_error); + } + previous_error = error; + } + int64_t pOut = static_cast((err_sum > 0 ? adpfConfig->mPidPo : adpfConfig->mPidPu) * + err_sum / (length - p_start)); + int64_t iOut = static_cast(adpfConfig->mPidI * integral_error); + int64_t dOut = + static_cast((derivative_sum > 0 ? adpfConfig->mPidDo : adpfConfig->mPidDu) * + derivative_sum / dt / (length - d_start)); + + int64_t output = pOut + iOut + dOut; + if (ATRACE_ENABLED()) { + traceSessionVal("pid.err", err_sum / (length - p_start)); + traceSessionVal("pid.integral", integral_error); + traceSessionVal("pid.derivative", derivative_sum / dt / (length - d_start)); + traceSessionVal("pid.pOut", pOut); + traceSessionVal("pid.iOut", iOut); + traceSessionVal("pid.dOut", dOut); + traceSessionVal("pid.output", output); + } + return output; +} + +PowerHintSession::PowerHintSession(int32_t tgid, int32_t uid, const std::vector &threadIds, + int64_t durationNanos) + : mStaleTimerHandler(sp::make(this)), + mBoostTimerHandler(sp::make(this)) { + mDescriptor = new AppHintDesc(tgid, uid, threadIds); + mDescriptor->duration = std::chrono::nanoseconds(durationNanos); + mIdString = StringPrintf("%" PRId32 "-%" PRId32 "-%" PRIxPTR, mDescriptor->tgid, + mDescriptor->uid, reinterpret_cast(this) & 0xffff); + + mPowerManagerHandler = PowerSessionManager::getInstance(); + mLastUpdatedTime.store(std::chrono::steady_clock::now()); + if (ATRACE_ENABLED()) { + traceSessionVal("target", mDescriptor->duration.count()); + traceSessionVal("active", mDescriptor->is_active.load()); + } + PowerSessionManager::getInstance()->addPowerSession(this); + // init boost + sendHint(SessionHint::CPU_LOAD_RESET); + ALOGV("PowerHintSession created: %s", mDescriptor->toString().c_str()); +} + +PowerHintSession::~PowerHintSession() { + close(); + ALOGV("PowerHintSession deleted: %s", mDescriptor->toString().c_str()); + if (ATRACE_ENABLED()) { + traceSessionVal("target", 0); + traceSessionVal("actl_last", 0); + traceSessionVal("active", 0); + } + delete mDescriptor; +} + +void PowerHintSession::traceSessionVal(char const *identifier, int64_t val) const { + ATRACE_INT(StringPrintf("adpf.%s-%s", mIdString.c_str(), identifier).c_str(), val); +} + +bool PowerHintSession::isAppSession() { + // Check if uid is in range reserved for applications + return mDescriptor->uid >= AID_APP_START; +} + +void PowerHintSession::updateUniveralBoostMode() { + if (!isAppSession()) { + return; + } + if (ATRACE_ENABLED()) { + const std::string tag = StringPrintf("%s:updateUniveralBoostMode()", mIdString.c_str()); + ATRACE_BEGIN(tag.c_str()); + } + PowerHintMonitor::getInstance()->getLooper()->sendMessage(mPowerManagerHandler, NULL); + if (ATRACE_ENABLED()) { + ATRACE_END(); + } +} + +void PowerHintSession::tryToSendPowerHint(std::string hint) { + if (!mSupportedHints[hint].has_value()) { + mSupportedHints[hint] = HintManager::GetInstance()->IsHintSupported(hint); + } + if (mSupportedHints[hint].value()) { + HintManager::GetInstance()->DoHint(hint); + } +} + +int PowerHintSession::setSessionUclampMin(int32_t min, bool resetStale) { + { + std::lock_guard guard(mSessionLock); + mDescriptor->current_min = min; + } + if (min != 0 && resetStale) { + mStaleTimerHandler->updateTimer(); + } + PowerSessionManager::getInstance()->setUclampMin(this, min); + + if (ATRACE_ENABLED()) { + traceSessionVal("min", min); + } + return 0; +} + +int PowerHintSession::getUclampMin() { + return mDescriptor->current_min; +} + +void PowerHintSession::dumpToStream(std::ostream &stream) { + stream << "ID.Min.Act.Timeout(" << mIdString; + stream << ", " << mDescriptor->current_min; + stream << ", " << mDescriptor->is_active.load(); + stream << ", " << isTimeout() << ")"; +} + +ndk::ScopedAStatus PowerHintSession::pause() { + if (mSessionClosed) { + ALOGE("Error: session is dead"); + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); + } + if (!mDescriptor->is_active.load()) + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); + // Reset to default uclamp value. + mDescriptor->is_active.store(false); + setStale(); + if (ATRACE_ENABLED()) { + traceSessionVal("active", mDescriptor->is_active.load()); + } + updateUniveralBoostMode(); + PowerSessionManager::getInstance()->removeThreadsFromPowerSession(this); + return ndk::ScopedAStatus::ok(); +} + +ndk::ScopedAStatus PowerHintSession::resume() { + if (mSessionClosed) { + ALOGE("Error: session is dead"); + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); + } + if (mDescriptor->is_active.load()) + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); + mDescriptor->is_active.store(true); + PowerSessionManager::getInstance()->addThreadsFromPowerSession(this); + // resume boost + setSessionUclampMin(mDescriptor->current_min); + if (ATRACE_ENABLED()) { + traceSessionVal("active", mDescriptor->is_active.load()); + } + updateUniveralBoostMode(); + return ndk::ScopedAStatus::ok(); +} + +ndk::ScopedAStatus PowerHintSession::close() { + bool sessionClosedExpectedToBe = false; + if (!mSessionClosed.compare_exchange_strong(sessionClosedExpectedToBe, true)) { + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); + } + // Remove the session from PowerSessionManager first to avoid racing. + PowerSessionManager::getInstance()->removePowerSession(this); + mStaleTimerHandler->setSessionDead(); + mBoostTimerHandler->setSessionDead(); + setSessionUclampMin(0); + mDescriptor->is_active.store(false); + updateUniveralBoostMode(); + return ndk::ScopedAStatus::ok(); +} + +ndk::ScopedAStatus PowerHintSession::updateTargetWorkDuration(int64_t targetDurationNanos) { + if (mSessionClosed) { + ALOGE("Error: session is dead"); + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); + } + if (targetDurationNanos <= 0) { + ALOGE("Error: targetDurationNanos(%" PRId64 ") should bigger than 0", targetDurationNanos); + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); + } + targetDurationNanos = + targetDurationNanos * HintManager::GetInstance()->GetAdpfProfile()->mTargetTimeFactor; + ALOGV("update target duration: %" PRId64 " ns", targetDurationNanos); + + mDescriptor->duration = std::chrono::nanoseconds(targetDurationNanos); + if (ATRACE_ENABLED()) { + traceSessionVal("target", mDescriptor->duration.count()); + } + + return ndk::ScopedAStatus::ok(); +} + +ndk::ScopedAStatus PowerHintSession::reportActualWorkDuration( + const std::vector &actualDurations) { + if (mSessionClosed) { + ALOGE("Error: session is dead"); + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); + } + if (mDescriptor->duration.count() == 0LL) { + ALOGE("Expect to call updateTargetWorkDuration() first."); + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); + } + if (actualDurations.size() == 0) { + ALOGE("Error: duration.size() shouldn't be %zu.", actualDurations.size()); + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); + } + if (!mDescriptor->is_active.load()) { + ALOGE("Error: shouldn't report duration during pause state."); + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); + } + std::shared_ptr adpfConfig = HintManager::GetInstance()->GetAdpfProfile(); + mDescriptor->update_count++; + bool isFirstFrame = isTimeout(); + if (ATRACE_ENABLED()) { + traceSessionVal("batch_size", actualDurations.size()); + traceSessionVal("actl_last", actualDurations.back().durationNanos); + traceSessionVal("target", mDescriptor->duration.count()); + traceSessionVal("hint.count", mDescriptor->update_count); + traceSessionVal("hint.overtime", + actualDurations.back().durationNanos - mDescriptor->duration.count() > 0); + } + + mLastUpdatedTime.store(std::chrono::steady_clock::now()); + if (isFirstFrame) { + if (isAppSession()) { + tryToSendPowerHint("ADPF_FIRST_FRAME"); + } + updateUniveralBoostMode(); + } + + disableTemporaryBoost(); + + if (!adpfConfig->mPidOn) { + setSessionUclampMin(adpfConfig->mUclampMinHigh); + return ndk::ScopedAStatus::ok(); + } + + int64_t output = convertWorkDurationToBoostByPid(actualDurations); + + /* apply to all the threads in the group */ + int next_min = std::min(static_cast(adpfConfig->mUclampMinHigh), + mDescriptor->current_min + static_cast(output)); + next_min = std::max(static_cast(adpfConfig->mUclampMinLow), next_min); + setSessionUclampMin(next_min); + + return ndk::ScopedAStatus::ok(); +} + +ndk::ScopedAStatus PowerHintSession::sendHint(SessionHint hint) { + if (mSessionClosed) { + ALOGE("Error: session is dead"); + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); + } + disableTemporaryBoost(); + std::shared_ptr adpfConfig = HintManager::GetInstance()->GetAdpfProfile(); + switch (hint) { + case SessionHint::CPU_LOAD_UP: + mNextUclampMin.store(mDescriptor->current_min); + mBoostTimerHandler->updateTimer(mDescriptor->duration * 2); + setSessionUclampMin(adpfConfig->mUclampMinHigh); + break; + case SessionHint::CPU_LOAD_DOWN: + setSessionUclampMin(adpfConfig->mUclampMinLow); + break; + case SessionHint::CPU_LOAD_RESET: + mNextUclampMin.store(std::max(adpfConfig->mUclampMinInit, + static_cast(mDescriptor->current_min))); + mBoostTimerHandler->updateTimer(duration_cast( + mDescriptor->duration * adpfConfig->mStaleTimeFactor / 2.0)); + setSessionUclampMin(adpfConfig->mUclampMinHigh); + break; + case SessionHint::CPU_LOAD_RESUME: + setSessionUclampMin(mDescriptor->current_min); + break; + default: + ALOGE("Error: hint is invalid"); + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); + } + tryToSendPowerHint(toString(hint)); + mLastUpdatedTime.store(std::chrono::steady_clock::now()); + if (ATRACE_ENABLED()) { + mLastHintSent = static_cast(hint); + traceSessionVal("session_hint", static_cast(hint)); + } + return ndk::ScopedAStatus::ok(); +} + +ndk::ScopedAStatus PowerHintSession::setThreads(const std::vector &threadIds) { + if (mSessionClosed) { + ALOGE("Error: session is dead"); + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); + } + if (threadIds.size() == 0) { + LOG(ERROR) << "Error: threadIds.size() shouldn't be " << threadIds.size(); + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); + } + + PowerSessionManager::getInstance()->removeThreadsFromPowerSession(this); + mDescriptor->threadIds.resize(threadIds.size()); + std::copy(threadIds.begin(), threadIds.end(), back_inserter(mDescriptor->threadIds)); + PowerSessionManager::getInstance()->addThreadsFromPowerSession(this); + // init boost + setSessionUclampMin(HintManager::GetInstance()->GetAdpfProfile()->mUclampMinInit); + return ndk::ScopedAStatus::ok(); +} + +std::string AppHintDesc::toString() const { + std::string out = + StringPrintf("session %" PRIxPTR "\n", reinterpret_cast(this) & 0xffff); + const int64_t durationNanos = duration.count(); + out.append(StringPrintf(" duration: %" PRId64 " ns\n", durationNanos)); + out.append(StringPrintf(" uclamp.min: %d \n", current_min)); + out.append(StringPrintf(" uid: %d, tgid: %d\n", uid, tgid)); + + out.append(" threadIds: ["); + bool first = true; + for (int tid : threadIds) { + if (!first) { + out.append(", "); + } + out.append(std::to_string(tid)); + first = false; + } + out.append("]\n"); + return out; +} + +bool PowerHintSession::isActive() { + return mDescriptor->is_active.load(); +} + +bool PowerHintSession::isTimeout() { + auto now = std::chrono::steady_clock::now(); + time_point staleTime = + mLastUpdatedTime.load() + + nanoseconds(static_cast( + mDescriptor->duration.count() * + HintManager::GetInstance()->GetAdpfProfile()->mStaleTimeFactor)); + return now >= staleTime; +} + +const std::vector &PowerHintSession::getTidList() const { + return mDescriptor->threadIds; +} + +bool PowerHintSession::disableTemporaryBoost() { + if (ATRACE_ENABLED()) { + if (mLastHintSent != -1) { + mLastHintSent = -1; + traceSessionVal("session_hint", -1); + } + } + + // replace temporary uclamp_min value with true min + std::optional trueMin = mNextUclampMin.load(); + if (trueMin.has_value()) { + std::lock_guard guard(mSessionLock); + mDescriptor->current_min = *trueMin; + mNextUclampMin.store(std::nullopt); + return true; + } + + return false; +} + +void PowerHintSession::setStale() { + // Make sure any temporary boost is disabled + disableTemporaryBoost(); + // Reset to default uclamp value. + PowerSessionManager::getInstance()->setUclampMin(this, 0); + // Deliver a task to check if all sessions are inactive. + updateUniveralBoostMode(); + if (ATRACE_ENABLED()) { + traceSessionVal("min", 0); + } +} + +void PowerHintSession::SessionTimerHandler::updateTimer(nanoseconds delay) { + mTimeout.store(steady_clock::now() + delay); + { + std::lock_guard guard(mMessageLock); + sp selfPtr = sp::fromExisting(this); + PowerHintMonitor::getInstance()->getLooper()->removeMessages(selfPtr); + PowerHintMonitor::getInstance()->getLooper()->sendMessageDelayed(delay.count(), selfPtr, + NULL); + } + if (ATRACE_ENABLED()) { + mSession->traceSessionVal(("timer." + mName).c_str(), 0); + } +} + +void PowerHintSession::SessionTimerHandler::handleMessage(const Message &) { + std::lock_guard guard(mClosedLock); + if (mIsSessionDead) { + return; + } + time_point now = steady_clock::now(); + int64_t next = (mTimeout.load() - now).count(); + if (next > 0) { + // Schedule for the stale timeout check. + std::lock_guard guard(mMessageLock); + sp selfPtr = sp::fromExisting(this); + PowerHintMonitor::getInstance()->getLooper()->removeMessages(selfPtr); + PowerHintMonitor::getInstance()->getLooper()->sendMessageDelayed(next, selfPtr, NULL); + } else { + onTimeout(); + } + if (ATRACE_ENABLED()) { + mSession->traceSessionVal(("timer." + mName).c_str(), next > 0 ? 0 : 1); + } +} + +void PowerHintSession::SessionTimerHandler::setSessionDead() { + std::lock_guard guard(mClosedLock); + mIsSessionDead = true; + PowerHintMonitor::getInstance()->getLooper()->removeMessages( + sp::fromExisting(this)); +} + +void PowerHintSession::StaleTimerHandler::updateTimer() { + SessionTimerHandler::updateTimer(duration_cast( + mSession->mDescriptor->duration * + HintManager::GetInstance()->GetAdpfProfile()->mStaleTimeFactor)); +} + +void PowerHintSession::StaleTimerHandler::onTimeout() { + mSession->setStale(); +} + +void PowerHintSession::BoostTimerHandler::onTimeout() { + if (mSession->disableTemporaryBoost()) { + mSession->setSessionUclampMin(mSession->getUclampMin(), false); + } +} + +} // namespace pixel +} // namespace impl +} // namespace power +} // namespace hardware +} // namespace google +} // namespace aidl diff --git a/power-libperfmgr/aidl/PowerHintSession.h b/power-libperfmgr/aidl/PowerHintSession.h new file mode 100644 index 0000000..e1c2523 --- /dev/null +++ b/power-libperfmgr/aidl/PowerHintSession.h @@ -0,0 +1,157 @@ +/* + * Copyright 2021 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 +#include +#include +#include +#include + +#include +#include + +namespace aidl { +namespace google { +namespace hardware { +namespace power { +namespace impl { +namespace pixel { + +using aidl::android::hardware::power::BnPowerHintSession; +using aidl::android::hardware::power::SessionHint; +using aidl::android::hardware::power::WorkDuration; +using ::android::Message; +using ::android::MessageHandler; +using ::android::sp; +using std::chrono::milliseconds; +using std::chrono::nanoseconds; +using std::chrono::steady_clock; +using std::chrono::time_point; + +struct AppHintDesc { + AppHintDesc(int32_t tgid, int32_t uid, std::vector threadIds) + : tgid(tgid), + uid(uid), + threadIds(std::move(threadIds)), + duration(0LL), + current_min(0), + is_active(true), + update_count(0), + integral_error(0), + previous_error(0) {} + std::string toString() const; + const int32_t tgid; + const int32_t uid; + std::vector threadIds; + nanoseconds duration; + int current_min; + // status + std::atomic is_active; + // pid + uint64_t update_count; + int64_t integral_error; + int64_t previous_error; +}; + +class PowerHintSession : public BnPowerHintSession { + public: + explicit PowerHintSession(int32_t tgid, int32_t uid, const std::vector &threadIds, + int64_t durationNanos); + ~PowerHintSession(); + ndk::ScopedAStatus close() override; + ndk::ScopedAStatus pause() override; + ndk::ScopedAStatus resume() override; + ndk::ScopedAStatus updateTargetWorkDuration(int64_t targetDurationNanos) override; + ndk::ScopedAStatus reportActualWorkDuration( + const std::vector &actualDurations) override; + ndk::ScopedAStatus sendHint(SessionHint hint) override; + ndk::ScopedAStatus setThreads(const std::vector &threadIds) override; + bool isActive(); + bool isTimeout(); + void setStale(); + // Is this hint session for a user application + bool isAppSession(); + const std::vector &getTidList() const; + int getUclampMin(); + void dumpToStream(std::ostream &stream); + + // Disable any temporary boost and return to normal operation. It does not + // reset the actual uclamp value, and relies on the caller to do so, to + // prevent double-setting. Returns true if it actually disabled an active boost + bool disableTemporaryBoost(); + + private: + class SessionTimerHandler : public MessageHandler { + public: + SessionTimerHandler(PowerHintSession *session, std::string name) + : mSession(session), mIsSessionDead(false), mName(name) {} + void updateTimer(nanoseconds delay); + void handleMessage(const Message &message) override; + void setSessionDead(); + virtual void onTimeout() = 0; + + protected: + PowerHintSession *mSession; + std::mutex mClosedLock; + std::mutex mMessageLock; + std::atomic> mTimeout; + bool mIsSessionDead; + const std::string mName; + }; + + class StaleTimerHandler : public SessionTimerHandler { + public: + StaleTimerHandler(PowerHintSession *session) : SessionTimerHandler(session, "stale") {} + void updateTimer(); + void onTimeout() override; + }; + + class BoostTimerHandler : public SessionTimerHandler { + public: + BoostTimerHandler(PowerHintSession *session) : SessionTimerHandler(session, "boost") {} + void onTimeout() override; + }; + + private: + void updateUniveralBoostMode(); + int setSessionUclampMin(int32_t min, bool resetStale = true); + void tryToSendPowerHint(std::string hint); + int64_t convertWorkDurationToBoostByPid(const std::vector &actualDurations); + void traceSessionVal(char const *identifier, int64_t val) const; + AppHintDesc *mDescriptor = nullptr; + sp mStaleTimerHandler; + sp mBoostTimerHandler; + std::atomic> mLastUpdatedTime; + sp mPowerManagerHandler; + std::mutex mSessionLock; + std::atomic mSessionClosed = false; + std::string mIdString; + // Used when setting a temporary boost value to hold the true boost + std::atomic> mNextUclampMin; + // To cache the status of whether ADPF hints are supported. + std::unordered_map> mSupportedHints; + // Last session hint sent, used for logging + int mLastHintSent = -1; +}; + +} // namespace pixel +} // namespace impl +} // namespace power +} // namespace hardware +} // namespace google +} // namespace aidl diff --git a/power-libperfmgr/aidl/PowerSessionManager.cpp b/power-libperfmgr/aidl/PowerSessionManager.cpp new file mode 100644 index 0000000..2d2aad2 --- /dev/null +++ b/power-libperfmgr/aidl/PowerSessionManager.cpp @@ -0,0 +1,262 @@ +/* + * Copyright 2021 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_TAG "powerhal-libperfmgr" +#define ATRACE_TAG (ATRACE_TAG_POWER | ATRACE_TAG_HAL) + +#include "PowerSessionManager.h" + +#include +#include +#include +#include +#include +#include + +namespace aidl { +namespace google { +namespace hardware { +namespace power { +namespace impl { +namespace pixel { + +using ::android::perfmgr::AdpfConfig; +using ::android::perfmgr::HintManager; + +namespace { +/* there is no glibc or bionic wrapper */ +struct sched_attr { + __u32 size; + __u32 sched_policy; + __u64 sched_flags; + __s32 sched_nice; + __u32 sched_priority; + __u64 sched_runtime; + __u64 sched_deadline; + __u64 sched_period; + __u32 sched_util_min; + __u32 sched_util_max; +}; + +static int sched_setattr(int pid, struct sched_attr *attr, unsigned int flags) { + if (!HintManager::GetInstance()->GetAdpfProfile()->mUclampMinOn) { + ALOGV("PowerSessionManager:%s: skip", __func__); + return 0; + } + return syscall(__NR_sched_setattr, pid, attr, flags); +} + +static void set_uclamp_min(int tid, int min) { + static constexpr int32_t kMaxUclampValue = 1024; + min = std::max(0, min); + min = std::min(min, kMaxUclampValue); + + sched_attr attr = {}; + attr.size = sizeof(attr); + + attr.sched_flags = (SCHED_FLAG_KEEP_ALL | SCHED_FLAG_UTIL_CLAMP_MIN); + attr.sched_util_min = min; + + int ret = sched_setattr(tid, &attr, 0); + if (ret) { + if (errno == ESRCH) { + ALOGV("sched_setattr failed for thread %d, err=%d", tid, errno); + } else { + ALOGW("sched_setattr failed for thread %d, err=%d", tid, errno); + } + } +} +} // namespace + +void PowerSessionManager::updateHintMode(const std::string &mode, bool enabled) { + ALOGV("PowerSessionManager::updateHintMode: mode: %s, enabled: %d", mode.c_str(), enabled); + if (enabled && mode.compare(0, 8, "REFRESH_") == 0) { + if (mode.compare("REFRESH_120FPS") == 0) { + mDisplayRefreshRate = 120; + } else if (mode.compare("REFRESH_90FPS") == 0) { + mDisplayRefreshRate = 90; + } else if (mode.compare("REFRESH_60FPS") == 0) { + mDisplayRefreshRate = 60; + } + } + if (HintManager::GetInstance()->GetAdpfProfile()) { + HintManager::GetInstance()->SetAdpfProfile(mode); + } +} + +void PowerSessionManager::updateHintBoost(const std::string &boost, int32_t durationMs) { + ATRACE_CALL(); + ALOGV("PowerSessionManager::updateHintBoost: boost: %s, durationMs: %d", boost.c_str(), + durationMs); +} + +int PowerSessionManager::getDisplayRefreshRate() { + return mDisplayRefreshRate; +} + +void PowerSessionManager::addPowerSession(PowerHintSession *session) { + std::lock_guard guard(mLock); + mSessions.insert(session); + addThreadsFromPowerSessionLocked(session); +} + +void PowerSessionManager::removePowerSession(PowerHintSession *session) { + std::lock_guard guard(mLock); + mSessions.erase(session); + removeThreadsFromPowerSessionLocked(session); +} + +void PowerSessionManager::addThreadsFromPowerSession(PowerHintSession *session) { + std::lock_guard guard(mLock); + addThreadsFromPowerSessionLocked(session); +} + +void PowerSessionManager::addThreadsFromPowerSessionLocked(PowerHintSession *session) { + for (auto t : session->getTidList()) { + if (mTidSessionListMap[t].empty()) { + if (!SetTaskProfiles(t, {"ResetUclampGrp"})) { + ALOGW("Failed to set ResetUclampGrp task profile for tid:%d", t); + } + } + mTidSessionListMap[t].insert(session); + } +} + +void PowerSessionManager::removeThreadsFromPowerSession(PowerHintSession *session) { + std::lock_guard guard(mLock); + removeThreadsFromPowerSessionLocked(session); +} + +void PowerSessionManager::removeThreadsFromPowerSessionLocked(PowerHintSession *session) { + for (auto t : session->getTidList()) { + size_t cnt = mTidSessionListMap[t].erase(session); + if (cnt != 0 && mTidSessionListMap[t].empty()) { + if (!SetTaskProfiles(t, {"NoResetUclampGrp"})) { + ALOGW("Failed to set NoResetUclampGrp task profile for tid:%d", t); + } + } + } +} + +void PowerSessionManager::setUclampMin(PowerHintSession *session, int val) { + std::lock_guard guard(mLock); + setUclampMinLocked(session, val); +} + +void PowerSessionManager::setUclampMinLocked(PowerHintSession *session, int val) { + for (auto t : session->getTidList()) { + // Get thex max uclamp.min across sessions which include the tid. + int tidMax = 0; + for (PowerHintSession *s : mTidSessionListMap[t]) { + if (!s->isActive() || s->isTimeout()) + continue; + tidMax = std::max(tidMax, s->getUclampMin()); + } + set_uclamp_min(t, std::max(val, tidMax)); + } +} + +std::optional PowerSessionManager::isAnyAppSessionActive() { + std::lock_guard guard(mLock); + bool active = false; + for (PowerHintSession *s : mSessions) { + // session active and not stale is actually active. + if (s->isActive() && !s->isTimeout() && s->isAppSession()) { + active = true; + break; + } + } + if (active == mActive) { + return std::nullopt; + } else { + mActive = active; + } + + return active; +} + +void PowerSessionManager::handleMessage(const Message &) { + auto active = isAnyAppSessionActive(); + if (!active.has_value()) { + return; + } + if (active.value()) { + disableSystemTopAppBoost(); + } else { + enableSystemTopAppBoost(); + } +} + +void PowerSessionManager::dumpToFd(int fd) { + std::ostringstream dump_buf; + std::lock_guard guard(mLock); + dump_buf << "========== Begin PowerSessionManager ADPF list ==========\n"; + for (PowerHintSession *s : mSessions) { + s->dumpToStream(dump_buf); + dump_buf << " Tid:Ref["; + for (size_t i = 0, len = s->getTidList().size(); i < len; i++) { + int t = s->getTidList()[i]; + dump_buf << t << ":" << mTidSessionListMap[t].size(); + if (i < len - 1) { + dump_buf << ", "; + } + } + dump_buf << "]\n"; + } + dump_buf << "========== End PowerSessionManager ADPF list ==========\n"; + if (!::android::base::WriteStringToFd(dump_buf.str(), fd)) { + ALOGE("Failed to dump one of session list to fd:%d", fd); + } +} + +void PowerSessionManager::enableSystemTopAppBoost() { + if (HintManager::GetInstance()->IsHintSupported(kDisableBoostHintName)) { + ALOGV("PowerSessionManager::enableSystemTopAppBoost!!"); + HintManager::GetInstance()->EndHint(kDisableBoostHintName); + } +} + +void PowerSessionManager::disableSystemTopAppBoost() { + if (HintManager::GetInstance()->IsHintSupported(kDisableBoostHintName)) { + ALOGV("PowerSessionManager::disableSystemTopAppBoost!!"); + HintManager::GetInstance()->DoHint(kDisableBoostHintName); + } +} + +// =========== PowerHintMonitor implementation start from here =========== +void PowerHintMonitor::start() { + if (!isRunning()) { + run("PowerHintMonitor", ::android::PRIORITY_HIGHEST); + } +} + +bool PowerHintMonitor::threadLoop() { + while (true) { + mLooper->pollOnce(-1); + } + return true; +} + +sp PowerHintMonitor::getLooper() { + return mLooper; +} + +} // namespace pixel +} // namespace impl +} // namespace power +} // namespace hardware +} // namespace google +} // namespace aidl diff --git a/power-libperfmgr/aidl/PowerSessionManager.h b/power-libperfmgr/aidl/PowerSessionManager.h new file mode 100644 index 0000000..7c74039 --- /dev/null +++ b/power-libperfmgr/aidl/PowerSessionManager.h @@ -0,0 +1,116 @@ +/* + * Copyright 2021 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 +#include +#include + +#include +#include +#include + +#include "PowerHintSession.h" + +namespace aidl { +namespace google { +namespace hardware { +namespace power { +namespace impl { +namespace pixel { + +using ::android::Looper; +using ::android::Message; +using ::android::MessageHandler; +using ::android::Thread; +using ::android::perfmgr::HintManager; + +constexpr char kPowerHalAdpfDisableTopAppBoost[] = "vendor.powerhal.adpf.disable.hint"; + +class PowerSessionManager : public MessageHandler { + public: + // current hint info + void updateHintMode(const std::string &mode, bool enabled); + void updateHintBoost(const std::string &boost, int32_t durationMs); + int getDisplayRefreshRate(); + // monitoring session status + void addPowerSession(PowerHintSession *session); + void removePowerSession(PowerHintSession *session); + void addThreadsFromPowerSession(PowerHintSession *session); + void addThreadsFromPowerSessionLocked(PowerHintSession *session); + void removeThreadsFromPowerSession(PowerHintSession *session); + void removeThreadsFromPowerSessionLocked(PowerHintSession *session); + void setUclampMin(PowerHintSession *session, int min); + void setUclampMinLocked(PowerHintSession *session, int min); + void handleMessage(const Message &message) override; + void dumpToFd(int fd); + + // Singleton + static sp getInstance() { + static sp instance = new PowerSessionManager(); + return instance; + } + + private: + std::optional isAnyAppSessionActive(); + void disableSystemTopAppBoost(); + void enableSystemTopAppBoost(); + const std::string kDisableBoostHintName; + + std::unordered_set mSessions; // protected by mLock + std::unordered_map> mTidSessionListMap; + bool mActive; // protected by mLock + /** + * mLock to pretect the above data objects opertions. + **/ + std::mutex mLock; + int mDisplayRefreshRate; + // Singleton + PowerSessionManager() + : kDisableBoostHintName(::android::base::GetProperty(kPowerHalAdpfDisableTopAppBoost, + "ADPF_DISABLE_TA_BOOST")), + mActive(false), + mDisplayRefreshRate(60) {} + PowerSessionManager(PowerSessionManager const &) = delete; + void operator=(PowerSessionManager const &) = delete; +}; + +class PowerHintMonitor : public Thread { + public: + void start(); + bool threadLoop() override; + sp getLooper(); + // Singleton + static sp getInstance() { + static sp instance = new PowerHintMonitor(); + return instance; + } + PowerHintMonitor(PowerHintMonitor const &) = delete; + void operator=(PowerHintMonitor const &) = delete; + + private: + sp mLooper; + // Singleton + PowerHintMonitor() : Thread(false), mLooper(new Looper(true)) {} +}; + +} // namespace pixel +} // namespace impl +} // namespace power +} // namespace hardware +} // namespace google +} // namespace aidl diff --git a/power-libperfmgr/aidl/android.hardware.power-service.a32-libperfmgr.rc b/power-libperfmgr/aidl/android.hardware.power-service.a32-libperfmgr.rc new file mode 100644 index 0000000..9dd885c --- /dev/null +++ b/power-libperfmgr/aidl/android.hardware.power-service.a32-libperfmgr.rc @@ -0,0 +1,32 @@ +service vendor.power-hal-aidl /vendor/bin/hw/android.hardware.power-service.a32-libperfmgr + class hal + user root + group system + priority -20 + +on late-fs + start vendor.power-hal-aidl + +# Restart powerHAL when framework died +on property:init.svc.zygote=restarting && property:vendor.powerhal.state=* + setprop vendor.powerhal.state "" + setprop vendor.powerhal.audio "" + setprop vendor.powerhal.rendering "" + restart vendor.power-hal-aidl + +# Clean up after b/163539793 resolved +on property:vendor.powerhal.dalvik.vm.dex2oat-threads=* + setprop dalvik.vm.dex2oat-threads ${vendor.powerhal.dalvik.vm.dex2oat-threads} + setprop dalvik.vm.restore-dex2oat-threads ${vendor.powerhal.dalvik.vm.dex2oat-threads} + +on property:vendor.powerhal.dalvik.vm.dex2oat-cpu-set=* + setprop dalvik.vm.dex2oat-cpu-set ${vendor.powerhal.dalvik.vm.dex2oat-cpu-set} + setprop dalvik.vm.restore-dex2oat-cpu-set ${vendor.powerhal.dalvik.vm.dex2oat-cpu-set} + +# Restart powerHAL when debug property set +on property:ro.debuggable=1 && property:vendor.powerhal.config.debug=* + restart vendor.power-hal-aidl + +on property:persist.vendor.powerhal.config.debug=* + setprop vendor.powerhal.config.debug ${persist.vendor.powerhal.config.debug} + diff --git a/power-libperfmgr/aidl/android.hardware.power-service.a32.xml b/power-libperfmgr/aidl/android.hardware.power-service.a32.xml new file mode 100644 index 0000000..f5dd6b9 --- /dev/null +++ b/power-libperfmgr/aidl/android.hardware.power-service.a32.xml @@ -0,0 +1,7 @@ + + + android.hardware.power + 4 + IPower/default + + diff --git a/power-libperfmgr/aidl/service.cpp b/power-libperfmgr/aidl/service.cpp new file mode 100644 index 0000000..c3e2dd1 --- /dev/null +++ b/power-libperfmgr/aidl/service.cpp @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2020 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_TAG "powerhal-libperfmgr" + +#include +#include +#include +#include +#include +#include + +#include + +#include "Power.h" +#include "PowerExt.h" +#include "PowerSessionManager.h" +#include "disp-power/DisplayLowPower.h" + +using aidl::google::hardware::power::impl::pixel::DisplayLowPower; +using aidl::google::hardware::power::impl::pixel::Power; +using aidl::google::hardware::power::impl::pixel::PowerExt; +using aidl::google::hardware::power::impl::pixel::PowerHintMonitor; +using aidl::google::hardware::power::impl::pixel::PowerSessionManager; +using ::android::perfmgr::HintManager; + +constexpr std::string_view kPowerHalInitProp("vendor.powerhal.init"); + +int main() { + // Parse config but do not start the looper + std::shared_ptr hm = HintManager::GetInstance(); + if (!hm) { + LOG(FATAL) << "HintManager Init failed"; + } + + std::shared_ptr dlpw = std::make_shared(); + + // single thread + ABinderProcess_setThreadPoolMaxThreadCount(0); + + // core service + std::shared_ptr pw = ndk::SharedRefBase::make(dlpw); + ndk::SpAIBinder pwBinder = pw->asBinder(); + AIBinder_setMinSchedulerPolicy(pwBinder.get(), SCHED_NORMAL, -20); + + // extension service + std::shared_ptr pwExt = ndk::SharedRefBase::make(dlpw); + auto pwExtBinder = pwExt->asBinder(); + AIBinder_setMinSchedulerPolicy(pwExtBinder.get(), SCHED_NORMAL, -20); + + // attach the extension to the same binder we will be registering + CHECK(STATUS_OK == AIBinder_setExtension(pwBinder.get(), pwExt->asBinder().get())); + + const std::string instance = std::string() + Power::descriptor + "/default"; + binder_status_t status = AServiceManager_addService(pw->asBinder().get(), instance.c_str()); + CHECK(status == STATUS_OK); + LOG(INFO) << "Pixel Power HAL AIDL Service with Extension is started."; + + if (HintManager::GetInstance()->GetAdpfProfile()) { + PowerHintMonitor::getInstance()->start(); + } + + std::thread initThread([&]() { + ::android::base::WaitForProperty(kPowerHalInitProp.data(), "1"); + HintManager::GetInstance()->Start(); + dlpw->Init(); + }); + initThread.detach(); + + ABinderProcess_joinThreadPool(); + + // should not reach + LOG(ERROR) << "Pixel Power HAL AIDL Service with Extension just died."; + return EXIT_FAILURE; +} diff --git a/power-libperfmgr/disp-power/DisplayLowPower.cpp b/power-libperfmgr/disp-power/DisplayLowPower.cpp new file mode 100644 index 0000000..f2da574 --- /dev/null +++ b/power-libperfmgr/disp-power/DisplayLowPower.cpp @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2020 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_TAG "powerhal-libperfmgr" + +#include +#include + +#include +#include + +#include "DisplayLowPower.h" + +namespace aidl { +namespace google { +namespace hardware { +namespace power { +namespace impl { +namespace pixel { + +DisplayLowPower::DisplayLowPower() : mFossStatus(false) {} + +void DisplayLowPower::Init() { + ConnectPpsDaemon(); +} + +void DisplayLowPower::SetDisplayLowPower(bool enable) { + SetFoss(enable); +} + +void DisplayLowPower::ConnectPpsDaemon() { + constexpr const char kPpsDaemon[] = "pps"; + + mPpsSocket.reset( + socket_local_client(kPpsDaemon, ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_STREAM)); + if (mPpsSocket.get() < 0) { + ALOGW("Connecting to PPS daemon failed (%s)", strerror(errno)); + } +} + +int DisplayLowPower::SendPpsCommand(const std::string_view cmd) { + if (TEMP_FAILURE_RETRY(write(mPpsSocket.get(), cmd.data(), cmd.size())) < 0) { + ALOGE("Failed to send pps command '%s' over socket (%s)", cmd.data(), strerror(errno)); + return -1; + } + + return 0; +} + +void DisplayLowPower::SetFoss(bool enable) { + if (mPpsSocket.get() < 0 || mFossStatus == enable) { + return; + } + + ALOGI("%s foss", (enable) ? "Enable" : "Disable"); + + std::string_view foss_cmd; + if (enable) { + foss_cmd = "foss:on"; + } else { + foss_cmd = "foss:off"; + } + + if (!SendPpsCommand(foss_cmd)) { + mFossStatus = enable; + } +} + +} // namespace pixel +} // namespace impl +} // namespace power +} // namespace hardware +} // namespace google +} // namespace aidl diff --git a/power-libperfmgr/disp-power/DisplayLowPower.h b/power-libperfmgr/disp-power/DisplayLowPower.h new file mode 100644 index 0000000..53eb6c9 --- /dev/null +++ b/power-libperfmgr/disp-power/DisplayLowPower.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2020 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 + +#include + +namespace aidl { +namespace google { +namespace hardware { +namespace power { +namespace impl { +namespace pixel { + +class DisplayLowPower { + public: + DisplayLowPower(); + ~DisplayLowPower() {} + void Init(); + void SetDisplayLowPower(bool enable); + + private: + void ConnectPpsDaemon(); + int SendPpsCommand(const std::string_view cmd); + void SetFoss(bool enable); + + ::android::base::unique_fd mPpsSocket; + bool mFossStatus; +}; + +} // namespace pixel +} // namespace impl +} // namespace power +} // namespace hardware +} // namespace google +} // namespace aidl diff --git a/power-libperfmgr/disp-power/InteractionHandler.cpp b/power-libperfmgr/disp-power/InteractionHandler.cpp new file mode 100644 index 0000000..d2357a6 --- /dev/null +++ b/power-libperfmgr/disp-power/InteractionHandler.cpp @@ -0,0 +1,283 @@ +/* + * Copyright (C) 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_TAG "powerhal-libperfmgr" +#define ATRACE_TAG (ATRACE_TAG_POWER | ATRACE_TAG_HAL) + +#include "InteractionHandler.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define MAX_LENGTH 64 + +#define MSINSEC 1000L +#define NSINMS 1000000L + +namespace aidl { +namespace google { +namespace hardware { +namespace power { +namespace impl { +namespace pixel { + +namespace { + +static const bool kDisplayIdleSupport = + ::android::base::GetBoolProperty("vendor.powerhal.disp.idle_support", true); +static const std::array kDispIdlePath = {"/sys/class/drm/card0/device/idle_state", + "/sys/class/graphics/fb0/idle_state"}; +static const uint32_t kWaitMs = + ::android::base::GetUintProperty("vendor.powerhal.disp.idle_wait", /*default*/ 100U); +static const uint32_t kMinDurationMs = + ::android::base::GetUintProperty("vendor.powerhal.interaction.min", /*default*/ 1400U); +static const uint32_t kMaxDurationMs = + ::android::base::GetUintProperty("vendor.powerhal.interaction.max", /*default*/ 5650U); +static const uint32_t kDurationOffsetMs = + ::android::base::GetUintProperty("vendor.powerhal.interaction.offset", /*default*/ 650U); + +static size_t CalcTimespecDiffMs(struct timespec start, struct timespec end) { + size_t diff_in_ms = 0; + diff_in_ms += (end.tv_sec - start.tv_sec) * MSINSEC; + diff_in_ms += (end.tv_nsec - start.tv_nsec) / NSINMS; + return diff_in_ms; +} + +static int FbIdleOpen(void) { + int fd; + for (const auto &path : kDispIdlePath) { + fd = open(path, O_RDONLY); + if (fd >= 0) + return fd; + } + ALOGE("Unable to open fb idle state path (%d)", errno); + return -1; +} + +} // namespace + +using ::android::perfmgr::HintManager; + +InteractionHandler::InteractionHandler() + : mState(INTERACTION_STATE_UNINITIALIZED), mDurationMs(0) {} + +InteractionHandler::~InteractionHandler() { + Exit(); +} + +bool InteractionHandler::Init() { + std::lock_guard lk(mLock); + + if (mState != INTERACTION_STATE_UNINITIALIZED) + return true; + + int fd = FbIdleOpen(); + if (fd < 0) + return false; + mIdleFd = fd; + + mEventFd = eventfd(0, EFD_NONBLOCK); + if (mEventFd < 0) { + ALOGE("Unable to create event fd (%d)", errno); + close(mIdleFd); + return false; + } + + mState = INTERACTION_STATE_IDLE; + mThread = std::unique_ptr(new std::thread(&InteractionHandler::Routine, this)); + + return true; +} + +void InteractionHandler::Exit() { + std::unique_lock lk(mLock); + if (mState == INTERACTION_STATE_UNINITIALIZED) + return; + + AbortWaitLocked(); + mState = INTERACTION_STATE_UNINITIALIZED; + lk.unlock(); + + mCond.notify_all(); + mThread->join(); + + close(mEventFd); + close(mIdleFd); +} + +void InteractionHandler::PerfLock() { + ALOGV("%s: acquiring perf lock", __func__); + if (!HintManager::GetInstance()->DoHint("INTERACTION")) { + ALOGE("%s: do hint INTERACTION failed", __func__); + } +} + +void InteractionHandler::PerfRel() { + ALOGV("%s: releasing perf lock", __func__); + if (!HintManager::GetInstance()->EndHint("INTERACTION")) { + ALOGE("%s: end hint INTERACTION failed", __func__); + } +} + +void InteractionHandler::Acquire(int32_t duration) { + ATRACE_CALL(); + + std::lock_guard lk(mLock); + + int inputDuration = duration + kDurationOffsetMs; + int finalDuration; + if (inputDuration > kMaxDurationMs) + finalDuration = kMaxDurationMs; + else if (inputDuration > kMinDurationMs) + finalDuration = inputDuration; + else + finalDuration = kMinDurationMs; + + // Fallback to do boost directly + // 1) override property is set OR + // 2) InteractionHandler not initialized + if (!kDisplayIdleSupport || mState == INTERACTION_STATE_UNINITIALIZED) { + HintManager::GetInstance()->DoHint("INTERACTION", std::chrono::milliseconds(finalDuration)); + return; + } + + struct timespec cur_timespec; + clock_gettime(CLOCK_MONOTONIC, &cur_timespec); + if (mState != INTERACTION_STATE_IDLE && finalDuration <= mDurationMs) { + size_t elapsed_time = CalcTimespecDiffMs(mLastTimespec, cur_timespec); + // don't hint if previous hint's duration covers this hint's duration + if (elapsed_time <= (mDurationMs - finalDuration)) { + ALOGV("%s: Previous duration (%d) cover this (%d) elapsed: %lld", __func__, + static_cast(mDurationMs), static_cast(finalDuration), + static_cast(elapsed_time)); + return; + } + } + mLastTimespec = cur_timespec; + mDurationMs = finalDuration; + + ALOGV("%s: input: %d final duration: %d", __func__, duration, finalDuration); + + if (mState == INTERACTION_STATE_WAITING) + AbortWaitLocked(); + else if (mState == INTERACTION_STATE_IDLE) + PerfLock(); + + mState = INTERACTION_STATE_INTERACTION; + mCond.notify_one(); +} + +void InteractionHandler::Release() { + std::lock_guard lk(mLock); + if (mState == INTERACTION_STATE_WAITING) { + ATRACE_CALL(); + PerfRel(); + mState = INTERACTION_STATE_IDLE; + } else { + // clear any wait aborts pending in event fd + uint64_t val; + ssize_t ret = read(mEventFd, &val, sizeof(val)); + + ALOGW_IF(ret < 0, "%s: failed to clear eventfd (%zd, %d)", __func__, ret, errno); + } +} + +// should be called while locked +void InteractionHandler::AbortWaitLocked() { + uint64_t val = 1; + ssize_t ret = write(mEventFd, &val, sizeof(val)); + if (ret != sizeof(val)) + ALOGW("Unable to write to event fd (%zd)", ret); +} + +void InteractionHandler::WaitForIdle(int32_t wait_ms, int32_t timeout_ms) { + char data[MAX_LENGTH]; + ssize_t ret; + struct pollfd pfd[2]; + + ATRACE_CALL(); + + ALOGV("%s: wait:%d timeout:%d", __func__, wait_ms, timeout_ms); + + pfd[0].fd = mEventFd; + pfd[0].events = POLLIN; + pfd[1].fd = mIdleFd; + pfd[1].events = POLLPRI | POLLERR; + + ret = poll(pfd, 1, wait_ms); + if (ret > 0) { + ALOGV("%s: wait aborted", __func__); + return; + } else if (ret < 0) { + ALOGE("%s: error in poll while waiting", __func__); + return; + } + + ret = pread(mIdleFd, data, sizeof(data), 0); + if (!ret) { + ALOGE("%s: Unexpected EOF!", __func__); + return; + } + + if (!strncmp(data, "idle", 4)) { + ALOGV("%s: already idle", __func__); + return; + } + + ret = poll(pfd, 2, timeout_ms); + if (ret < 0) + ALOGE("%s: Error on waiting for idle (%zd)", __func__, ret); + else if (ret == 0) + ALOGV("%s: timed out waiting for idle", __func__); + else if (pfd[0].revents) + ALOGV("%s: wait for idle aborted", __func__); + else if (pfd[1].revents) + ALOGV("%s: idle detected", __func__); +} + +void InteractionHandler::Routine() { + pthread_setname_np(pthread_self(), "DispIdle"); + std::unique_lock lk(mLock, std::defer_lock); + + while (true) { + lk.lock(); + mCond.wait(lk, [&] { return mState != INTERACTION_STATE_IDLE; }); + if (mState == INTERACTION_STATE_UNINITIALIZED) + return; + mState = INTERACTION_STATE_WAITING; + lk.unlock(); + + WaitForIdle(kWaitMs, mDurationMs); + Release(); + } +} + +} // namespace pixel +} // namespace impl +} // namespace power +} // namespace hardware +} // namespace google +} // namespace aidl diff --git a/power-libperfmgr/disp-power/InteractionHandler.h b/power-libperfmgr/disp-power/InteractionHandler.h new file mode 100644 index 0000000..c88f7d4 --- /dev/null +++ b/power-libperfmgr/disp-power/InteractionHandler.h @@ -0,0 +1,71 @@ +/* + * Copyright (C) 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. + */ + +#pragma once + +#include +#include +#include +#include +#include + +namespace aidl { +namespace google { +namespace hardware { +namespace power { +namespace impl { +namespace pixel { + +enum InteractionState { + INTERACTION_STATE_UNINITIALIZED, + INTERACTION_STATE_IDLE, + INTERACTION_STATE_INTERACTION, + INTERACTION_STATE_WAITING, +}; + +class InteractionHandler { + public: + InteractionHandler(); + ~InteractionHandler(); + bool Init(); + void Exit(); + void Acquire(int32_t duration); + + private: + void Release(); + void WaitForIdle(int32_t wait_ms, int32_t timeout_ms); + void AbortWaitLocked(); + void Routine(); + + void PerfLock(); + void PerfRel(); + + enum InteractionState mState; + int mIdleFd; + int mEventFd; + int32_t mDurationMs; + struct timespec mLastTimespec; + std::unique_ptr mThread; + std::mutex mLock; + std::condition_variable mCond; +}; + +} // namespace pixel +} // namespace impl +} // namespace power +} // namespace hardware +} // namespace google +} // namespace aidl