ADPF: use PID algorithm to control cpu resource
The patch includes:
1. Move from folder adpf to aidl.
2. Add PowerSessionManager class to maintain hint status.
And PowerHintMointor looper thread for monitoring or updating
PowerHintSession status.
3. Use PID algorithm to replace the step-wise alogrithm for cpu resource
control.
Test: build, boot to home, trace analysis
Bug: 177493042
Change-Id: Ib7d3f414225b18954350341ca22b7be87a6202e7
diff --git a/aidl/power-libperfmgr/Android.mk b/aidl/power-libperfmgr/Android.mk
index 25d960f..7014c7f 100644
--- a/aidl/power-libperfmgr/Android.mk
+++ b/aidl/power-libperfmgr/Android.mk
@@ -25,7 +25,6 @@
LOCAL_SHARED_LIBRARIES := \
android.hardware.power-V2-ndk_platform \
- libadpf-pixel \
libbase \
libbinder_ndk \
libcutils \
@@ -39,7 +38,9 @@
service.cpp \
InteractionHandler.cpp \
Power.cpp \
- PowerExt.cpp
+ PowerExt.cpp \
+ PowerHintSession.cpp \
+ PowerSessionManager.cpp
LOCAL_CFLAGS := -Wno-unused-parameter -Wno-unused-variable
diff --git a/aidl/power-libperfmgr/Power.cpp b/aidl/power-libperfmgr/Power.cpp
index 2ba98a0..7fb113e 100644
--- a/aidl/power-libperfmgr/Power.cpp
+++ b/aidl/power-libperfmgr/Power.cpp
@@ -31,7 +31,8 @@
#include <utils/Log.h>
#include <utils/Trace.h>
-#include "adpf/PowerHintSession.h"
+#include "PowerHintSession.h"
+#include "PowerSessionManager.h"
namespace aidl {
namespace google {
@@ -40,7 +41,7 @@
namespace impl {
namespace pixel {
-using ::aidl::google::hardware::power::impl::pixel::adpf::PowerHintSession;
+using ::aidl::google::hardware::power::impl::pixel::PowerHintSession;
#ifdef MODE_EXT
extern bool isDeviceSpecificModeSupported(Mode type, bool* _aidl_return);
@@ -89,6 +90,7 @@
ndk::ScopedAStatus Power::setMode(Mode type, bool enabled) {
LOG(DEBUG) << "Power setMode: " << toString(type) << " to: " << enabled;
ATRACE_INT(toString(type).c_str(), enabled);
+ PowerSessionManager::getInstance().updateHintMode(toString(type), enabled);
#ifdef MODE_EXT
if (setDeviceSpecificMode(type, enabled)) {
return ndk::ScopedAStatus::ok();
diff --git a/aidl/power-libperfmgr/Power.h b/aidl/power-libperfmgr/Power.h
index 9dff2da..6f4a938 100644
--- a/aidl/power-libperfmgr/Power.h
+++ b/aidl/power-libperfmgr/Power.h
@@ -56,7 +56,6 @@
std::unique_ptr<InteractionHandler> mInteractionHandler;
std::atomic<bool> mSustainedPerfModeOn;
const int64_t mAdpfRate;
- // TODO(jimmyshiu@): hold weak_ptr of PowerHintSession
};
} // namespace pixel
diff --git a/aidl/power-libperfmgr/PowerExt.cpp b/aidl/power-libperfmgr/PowerExt.cpp
index 6c268b6..c3c2252 100644
--- a/aidl/power-libperfmgr/PowerExt.cpp
+++ b/aidl/power-libperfmgr/PowerExt.cpp
@@ -18,6 +18,7 @@
#define LOG_TAG "android.hardware.power-service.xiaomi.ext-libperfmgr"
#include "PowerExt.h"
+#include "PowerSessionManager.h"
#include <mutex>
@@ -46,6 +47,7 @@
} else {
mHintManager->EndHint(mode);
}
+ PowerSessionManager::getInstance().updateHintMode(mode, enabled);
return ndk::ScopedAStatus::ok();
}
diff --git a/aidl/power-libperfmgr/PowerHintSession.cpp b/aidl/power-libperfmgr/PowerHintSession.cpp
new file mode 100644
index 0000000..908983f
--- /dev/null
+++ b/aidl/power-libperfmgr/PowerHintSession.cpp
@@ -0,0 +1,444 @@
+/*
+ * 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 <android-base/logging.h>
+#include <android-base/parsedouble.h>
+#include <android-base/properties.h>
+#include <android-base/stringprintf.h>
+#include <log/log.h>
+#include <time.h>
+#include <utils/Trace.h>
+
+#include <sys/syscall.h>
+
+#include "PowerHintSession.h"
+#include "PowerSessionManager.h"
+
+namespace aidl {
+namespace google {
+namespace hardware {
+namespace power {
+namespace impl {
+namespace pixel {
+
+using ::android::base::StringPrintf;
+using std::chrono::duration_cast;
+using std::chrono::nanoseconds;
+using std::literals::chrono_literals::operator""s;
+
+constexpr char kPowerHalAdpfPidOffset[] = "vendor.powerhal.adpf.pid.offset";
+constexpr char kPowerHalAdpfPidP[] = "vendor.powerhal.adpf.pid.p";
+constexpr char kPowerHalAdpfPidI[] = "vendor.powerhal.adpf.pid.i";
+constexpr char kPowerHalAdpfPidIClamp[] = "vendor.powerhal.adpf.pid.i_clamp";
+constexpr char kPowerHalAdpfPidD[] = "vendor.powerhal.adpf.pid.d";
+constexpr char kPowerHalAdpfPidInitialIntegral[] = "vendor.powerhal.adpf.pid.i_init";
+constexpr char kPowerHalAdpfUclampEnable[] = "vendor.powerhal.adpf.uclamp";
+constexpr char kPowerHalAdpfUclampCapRatio[] = "vendor.powerhal.adpf.uclamp.cap_ratio";
+constexpr char kPowerHalAdpfUclampGranularity[] = "vendor.powerhal.adpf.uclamp.granularity";
+constexpr char kPowerHalAdpfStaleTimeout[] = "vendor.powerhal.adpf.stale_timeout_ms";
+constexpr char kPowerHalAdpfSamplingWindow[] = "vendor.powerhal.adpf.sampling_window";
+
+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) {
+ static const bool kPowerHalAdpfUclamp =
+ ::android::base::GetBoolProperty(kPowerHalAdpfUclampEnable, true);
+ if (!kPowerHalAdpfUclamp) {
+ ALOGV("PowerHintSession:%s: skip", __func__);
+ return 0;
+ }
+ return syscall(__NR_sched_setattr, pid, attr, flags);
+}
+
+static inline void TRACE_ADPF_PID(uintptr_t session_id, int32_t uid, int32_t tgid, uint64_t count,
+ int64_t err, int64_t integral, int64_t previous, int64_t p,
+ int64_t i, int64_t d, int32_t output) {
+ if (ATRACE_ENABLED()) {
+ std::string idstr = StringPrintf("adpf.%" PRId32 "-%" PRId32 "-%" PRIxPTR, tgid, uid,
+ session_id & 0xffff);
+ std::string sz = StringPrintf("%s-pid.count", idstr.c_str());
+ ATRACE_INT(sz.c_str(), count);
+ sz = StringPrintf("%s-pid.err", idstr.c_str());
+ ATRACE_INT(sz.c_str(), err);
+ sz = StringPrintf("%s-pid.accu", idstr.c_str());
+ ATRACE_INT(sz.c_str(), integral);
+ sz = StringPrintf("%s-pid.prev", idstr.c_str());
+ ATRACE_INT(sz.c_str(), previous);
+ sz = StringPrintf("%s-pid.pOut", idstr.c_str());
+ ATRACE_INT(sz.c_str(), p);
+ sz = StringPrintf("%s-pid.iOut", idstr.c_str());
+ ATRACE_INT(sz.c_str(), i);
+ sz = StringPrintf("%s-pid.dOut", idstr.c_str());
+ ATRACE_INT(sz.c_str(), d);
+ sz = StringPrintf("%s-pid.output", idstr.c_str());
+ ATRACE_INT(sz.c_str(), output);
+ }
+}
+
+static int64_t ns_to_100us(int64_t ns) {
+ return ns / 100000;
+}
+
+double getDoubleProperty(const char *prop, double value) {
+ std::string result = ::android::base::GetProperty(prop, std::to_string(value).c_str());
+ if (!::android::base::ParseDouble(result.c_str(), &value)) {
+ ALOGE("PowerHintSession : failed to parse double in %s", prop);
+ }
+ return value;
+}
+
+static double sPidOffset = getDoubleProperty(kPowerHalAdpfPidOffset, 0.0);
+static double sPidP = getDoubleProperty(kPowerHalAdpfPidP, 2.0);
+static double sPidI = getDoubleProperty(kPowerHalAdpfPidI, 0.001);
+static double sPidD = getDoubleProperty(kPowerHalAdpfPidD, 100.0);
+static const int64_t sPidIInit =
+ (sPidI == 0) ? 0
+ : static_cast<int64_t>(::android::base::GetIntProperty<int64_t>(
+ kPowerHalAdpfPidInitialIntegral, 100) /
+ sPidI);
+static const int64_t sPidIClamp =
+ (sPidI == 0) ? 0
+ : std::abs(static_cast<int64_t>(::android::base::GetIntProperty<int64_t>(
+ kPowerHalAdpfPidIClamp, 512) /
+ sPidI));
+static const int sUclampCap =
+ static_cast<int>(getDoubleProperty(kPowerHalAdpfUclampCapRatio, 0.5) * 1024);
+static const uint32_t sUclampGranularity =
+ ::android::base::GetUintProperty<uint32_t>(kPowerHalAdpfUclampGranularity, 5);
+static const int64_t sStaleTimeoutMs =
+ ::android::base::GetIntProperty<int64_t>(kPowerHalAdpfStaleTimeout, 3000);
+static const size_t sSamplingWindow =
+ ::android::base::GetUintProperty<size_t>(kPowerHalAdpfSamplingWindow, 1);
+
+} // namespace
+
+PowerHintSession::PowerHintSession(int32_t tgid, int32_t uid, const std::vector<int32_t> &threadIds,
+ int64_t durationNanos) {
+ mDescriptor = new AppHintDesc(tgid, uid, threadIds, sUclampCap);
+ mDescriptor->duration = std::chrono::nanoseconds(durationNanos);
+ mStaleHandler = sp<StaleHandler>(new StaleHandler(this, sStaleTimeoutMs));
+ mPowerManagerHandler = sp<MessageHandler>(&PowerSessionManager::getInstance());
+
+ if (ATRACE_ENABLED()) {
+ std::string sz =
+ StringPrintf("adpf.%" PRId32 "-%" PRId32 "-%" PRIxPTR "-target", mDescriptor->tgid,
+ mDescriptor->uid, reinterpret_cast<uintptr_t>(this) & 0xffff);
+ ATRACE_INT(sz.c_str(), (int64_t)mDescriptor->duration.count());
+ sz = StringPrintf("adpf.%" PRId32 "-%" PRId32 "-%" PRIxPTR "-active", mDescriptor->tgid,
+ mDescriptor->uid, reinterpret_cast<uintptr_t>(this) & 0xffff);
+ ATRACE_INT(sz.c_str(), mDescriptor->is_active.load());
+ }
+ PowerSessionManager::getInstance().addPowerSession(this);
+ ALOGD("PowerHintSession created: %s", mDescriptor->toString().c_str());
+}
+
+PowerHintSession::~PowerHintSession() {
+ close();
+ ALOGD("PowerHintSession deleted: %s", mDescriptor->toString().c_str());
+ if (ATRACE_ENABLED()) {
+ std::string sz =
+ StringPrintf("adpf.%" PRId32 "-%" PRId32 "-%" PRIxPTR "-target", mDescriptor->tgid,
+ mDescriptor->uid, reinterpret_cast<uintptr_t>(this) & 0xffff);
+ ATRACE_INT(sz.c_str(), 0);
+ sz = StringPrintf("adpf.%" PRId32 "-%" PRId32 "-%" PRIxPTR "-actl_last", mDescriptor->tgid,
+ mDescriptor->uid, reinterpret_cast<uintptr_t>(this) & 0xffff);
+ ATRACE_INT(sz.c_str(), 0);
+ sz = StringPrintf("adpf.%" PRId32 "-%" PRId32 "-%" PRIxPTR "-active", mDescriptor->tgid,
+ mDescriptor->uid, reinterpret_cast<uintptr_t>(this) & 0xffff);
+ ATRACE_INT(sz.c_str(), 0);
+ }
+ delete mDescriptor;
+}
+
+void PowerHintSession::updateUniveralBoostMode() {
+ PowerHintMonitor::getInstance().getLooper()->sendMessage(mPowerManagerHandler, NULL);
+}
+
+int PowerHintSession::setUclamp(int32_t min, int32_t max) {
+ std::lock_guard<std::mutex> guard(mLock);
+ min = std::max(0, min);
+ min = std::min(min, max);
+ max = std::max(0, max);
+ max = std::max(min, max);
+ if (ATRACE_ENABLED()) {
+ std::string sz =
+ StringPrintf("adpf.%" PRId32 "-%" PRId32 "-%" PRIxPTR "-min", mDescriptor->tgid,
+ mDescriptor->uid, reinterpret_cast<uintptr_t>(this) & 0xffff);
+ ATRACE_INT(sz.c_str(), min);
+ sz = StringPrintf("adpf.%" PRId32 "-%" PRId32 "-%" PRIxPTR "-max", mDescriptor->tgid,
+ mDescriptor->uid, reinterpret_cast<uintptr_t>(this) & 0xffff);
+ ATRACE_INT(sz.c_str(), max);
+ }
+ for (const auto tid : mDescriptor->threadIds) {
+ sched_attr attr = {};
+ attr.size = sizeof(attr);
+
+ attr.sched_flags = (SCHED_FLAG_KEEP_ALL | SCHED_FLAG_UTIL_CLAMP);
+ attr.sched_util_min = min;
+ attr.sched_util_max = max;
+
+ int ret = sched_setattr(tid, &attr, 0);
+ if (ret) {
+ ALOGW("sched_setattr failed for thread %d, err=%d", tid, errno);
+ }
+ ALOGV("PowerHintSession tid: %d, uclamp(%d, %d)", tid, min, max);
+ }
+ return 0;
+}
+
+ndk::ScopedAStatus PowerHintSession::pause() {
+ if (!mDescriptor->is_active.load())
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+ // Reset to default uclamp value.
+ setUclamp(0, 1024);
+ mDescriptor->is_active.store(false);
+ if (ATRACE_ENABLED()) {
+ std::string sz =
+ StringPrintf("%" PRId32 "-%" PRId32 "-%" PRIxPTR "-active", mDescriptor->tgid,
+ mDescriptor->uid, reinterpret_cast<uintptr_t>(this) & 0xffff);
+ ATRACE_INT(sz.c_str(), mDescriptor->is_active.load());
+ }
+ updateUniveralBoostMode();
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus PowerHintSession::resume() {
+ if (mDescriptor->is_active.load())
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+ mDescriptor->is_active.store(true);
+ mDescriptor->integral_error = std::max(sPidIInit, mDescriptor->integral_error);
+ if (ATRACE_ENABLED()) {
+ std::string sz =
+ StringPrintf("%" PRId32 "-%" PRId32 "-%" PRIxPTR "-active", mDescriptor->tgid,
+ mDescriptor->uid, reinterpret_cast<uintptr_t>(this) & 0xffff);
+ ATRACE_INT(sz.c_str(), mDescriptor->is_active.load());
+ }
+ updateUniveralBoostMode();
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus PowerHintSession::close() {
+ PowerHintMonitor::getInstance().getLooper()->removeMessages(mStaleHandler);
+ // Reset to (0, 1024) uclamp value -- instead of threads' original setting.
+ setUclamp(0, 1024);
+ PowerSessionManager::getInstance().removePowerSession(this);
+ updateUniveralBoostMode();
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus PowerHintSession::updateTargetWorkDuration(int64_t targetDurationNanos) {
+ if (targetDurationNanos <= 0) {
+ ALOGE("Error: targetDurationNanos(%" PRId64 ") should bigger than 0", targetDurationNanos);
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+ }
+ ALOGV("update target duration: %" PRId64 " ns", targetDurationNanos);
+ double ratio =
+ targetDurationNanos == 0 ? 1.0 : mDescriptor->duration.count() / targetDurationNanos;
+ mDescriptor->integral_error =
+ std::max(sPidIInit, static_cast<int64_t>(mDescriptor->integral_error * ratio));
+
+ mDescriptor->duration = std::chrono::nanoseconds(targetDurationNanos);
+ if (ATRACE_ENABLED()) {
+ std::string sz =
+ StringPrintf("adpf.%" PRId32 "-%" PRId32 "-%" PRIxPTR "-target", mDescriptor->tgid,
+ mDescriptor->uid, reinterpret_cast<uintptr_t>(this) & 0xffff);
+ ATRACE_INT(sz.c_str(), (int64_t)mDescriptor->duration.count());
+ }
+
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus PowerHintSession::reportActualWorkDuration(
+ const std::vector<WorkDuration> &actualDurations) {
+ 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);
+ }
+ if (PowerHintMonitor::getInstance().isRunning() && isStale()) {
+ if (ATRACE_ENABLED()) {
+ std::string sz = StringPrintf("adpf.%" PRId32 "-%" PRId32 "-%" PRIxPTR "-stale",
+ mDescriptor->tgid, mDescriptor->uid,
+ reinterpret_cast<uintptr_t>(this) & 0xffff);
+ ATRACE_INT(sz.c_str(), 0);
+ }
+ mDescriptor->integral_error = std::max(sPidIInit, mDescriptor->integral_error);
+ }
+ int64_t targetDurationNanos = (int64_t)mDescriptor->duration.count();
+ size_t length = actualDurations.size();
+ size_t start = sSamplingWindow == 0 || sSamplingWindow > length ? 0 : length - sSamplingWindow;
+ int64_t actualDurationNanos = 0;
+ int64_t dt = ns_to_100us(targetDurationNanos);
+ int64_t error = 0;
+ int64_t derivative = 0;
+ for (size_t i = start; i < length; i++) {
+ actualDurationNanos = actualDurations[i].durationNanos;
+ if (std::abs(actualDurationNanos) > targetDurationNanos * 20) {
+ ALOGW("The actual duration is way far from the target (%" PRId64 " >> %" PRId64 ")",
+ actualDurationNanos, targetDurationNanos);
+ }
+ if (ATRACE_ENABLED()) {
+ std::string sz = StringPrintf("adpf.%" PRId32 "-%" PRId32 "-%" PRIxPTR "-actl_last",
+ mDescriptor->tgid, mDescriptor->uid,
+ reinterpret_cast<uintptr_t>(this) & 0xffff);
+ ATRACE_INT(sz.c_str(), actualDurationNanos);
+ sz = StringPrintf("adpf.%" PRId32 "-%" PRId32 "-%" PRIxPTR "-target", mDescriptor->tgid,
+ mDescriptor->uid, reinterpret_cast<uintptr_t>(this) & 0xffff);
+ ATRACE_INT(sz.c_str(), (int64_t)mDescriptor->duration.count());
+ }
+ // PID control algorithm
+ error = ns_to_100us(actualDurationNanos - targetDurationNanos) +
+ static_cast<int64_t>(sPidOffset);
+ mDescriptor->integral_error = mDescriptor->integral_error + error * dt;
+ mDescriptor->integral_error = std::min(sPidIClamp, mDescriptor->integral_error);
+ mDescriptor->integral_error = std::max(-sPidIClamp, mDescriptor->integral_error);
+ derivative = (error - mDescriptor->previous_error) / dt;
+ mDescriptor->previous_error = error;
+ }
+ int64_t pOut = static_cast<int64_t>(sPidP * error);
+ int64_t iOut = static_cast<int64_t>(sPidI * mDescriptor->integral_error);
+ int64_t dOut = static_cast<int64_t>(sPidD * derivative);
+
+ int64_t output = pOut + iOut + dOut;
+ TRACE_ADPF_PID(reinterpret_cast<uintptr_t>(this) & 0xffff, mDescriptor->uid, mDescriptor->tgid,
+ mDescriptor->update_count, error, mDescriptor->integral_error, derivative, pOut,
+ iOut, dOut, static_cast<int>(output));
+ mDescriptor->update_count++;
+
+ mStaleHandler->updateStaleTimer();
+
+ /* apply to all the threads in the group */
+ if (output != 0) {
+ int next_min = std::min(sUclampCap, mDescriptor->current_min + static_cast<int>(output));
+ next_min = std::max(0, next_min);
+ if (std::abs(mDescriptor->current_min - next_min) > sUclampGranularity) {
+ setUclamp(next_min, 1024);
+ mDescriptor->current_min = next_min;
+ }
+ }
+
+ return ndk::ScopedAStatus::ok();
+}
+
+std::string AppHintDesc::toString() const {
+ std::string out =
+ StringPrintf("session %" PRIxPTR "\n", reinterpret_cast<uintptr_t>(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::isStale() {
+ auto now = std::chrono::steady_clock::now();
+ return now >= mStaleHandler->getStaleTime();
+}
+
+void PowerHintSession::setStale() {
+ if (ATRACE_ENABLED()) {
+ std::string sz =
+ StringPrintf("adpf.%" PRId32 "-%" PRId32 "-%" PRIxPTR "-stale", mDescriptor->tgid,
+ mDescriptor->uid, reinterpret_cast<uintptr_t>(this) & 0xffff);
+ ATRACE_INT(sz.c_str(), 1);
+ }
+ // Reset to default uclamp value.
+ setUclamp(0, 1024);
+ // Deliver a task to check if all sessions are inactive.
+ updateUniveralBoostMode();
+}
+
+void PowerHintSession::StaleHandler::updateStaleTimer() {
+ std::lock_guard<std::mutex> guard(mStaleLock);
+ if (PowerHintMonitor::getInstance().isRunning()) {
+ auto when = getStaleTime();
+ auto now = std::chrono::steady_clock::now();
+ mLastUpdatedTime.store(now);
+ if (now > when) {
+ mSession->updateUniveralBoostMode();
+ }
+ if (!mIsMonitoringStale.load()) {
+ auto next = getStaleTime();
+ PowerHintMonitor::getInstance().getLooper()->sendMessageDelayed(
+ duration_cast<nanoseconds>(next - now).count(), this, NULL);
+ mIsMonitoringStale.store(true);
+ }
+ }
+}
+
+time_point<steady_clock> PowerHintSession::StaleHandler::getStaleTime() {
+ return mLastUpdatedTime.load() + kStaleTimeout;
+}
+
+void PowerHintSession::StaleHandler::handleMessage(const Message &) {
+ std::lock_guard<std::mutex> guard(mStaleLock);
+ auto now = std::chrono::steady_clock::now();
+ auto when = getStaleTime();
+ // Check if the session is stale based on the last_updated_time.
+ if (now > when) {
+ mSession->setStale();
+ mIsMonitoringStale.store(false);
+ return;
+ }
+ // Schedule for the next checking time.
+ PowerHintMonitor::getInstance().getLooper()->sendMessageDelayed(
+ duration_cast<nanoseconds>(when - now).count(), this, NULL);
+}
+
+} // namespace pixel
+} // namespace impl
+} // namespace power
+} // namespace hardware
+} // namespace google
+} // namespace aidl
diff --git a/aidl/power-libperfmgr/PowerHintSession.h b/aidl/power-libperfmgr/PowerHintSession.h
new file mode 100644
index 0000000..4a2e817
--- /dev/null
+++ b/aidl/power-libperfmgr/PowerHintSession.h
@@ -0,0 +1,118 @@
+/*
+ * 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 <aidl/android/hardware/power/BnPowerHintSession.h>
+#include <aidl/android/hardware/power/WorkDuration.h>
+#include <utils/Looper.h>
+#include <utils/Thread.h>
+
+#include <mutex>
+#include <unordered_map>
+
+namespace aidl {
+namespace google {
+namespace hardware {
+namespace power {
+namespace impl {
+namespace pixel {
+
+using aidl::android::hardware::power::BnPowerHintSession;
+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<int> threadIds, int uclamp_min)
+ : tgid(tgid),
+ uid(uid),
+ threadIds(std::move(threadIds)),
+ duration(0LL),
+ current_min(uclamp_min),
+ is_active(true),
+ update_count(0),
+ integral_error(0),
+ previous_error(0) {}
+ std::string toString() const;
+ const int32_t tgid;
+ const int32_t uid;
+ const std::vector<int> threadIds;
+ nanoseconds duration;
+ int current_min;
+ // status
+ std::atomic<bool> is_active;
+ // pid
+ uint64_t update_count;
+ int64_t integral_error;
+ int64_t previous_error;
+};
+
+class PowerHintSession : public BnPowerHintSession {
+ public:
+ PowerHintSession(int32_t tgid, int32_t uid, const std::vector<int32_t> &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<WorkDuration> &actualDurations) override;
+ bool isActive();
+ bool isStale();
+
+ private:
+ class StaleHandler : public MessageHandler {
+ public:
+ StaleHandler(PowerHintSession *session, int64_t timeout_ms)
+ : kStaleTimeout(timeout_ms),
+ mSession(session),
+ mIsMonitoringStale(false),
+ mLastUpdatedTime(steady_clock::now()) {}
+ void handleMessage(const Message &message) override;
+ void updateStaleTimer();
+ time_point<steady_clock> getStaleTime();
+
+ private:
+ const milliseconds kStaleTimeout;
+ PowerHintSession *mSession;
+ std::atomic<bool> mIsMonitoringStale;
+ std::atomic<time_point<steady_clock>> mLastUpdatedTime;
+ std::mutex mStaleLock;
+ };
+
+ private:
+ void setStale();
+ void updateUniveralBoostMode();
+ int setUclamp(int32_t max, int32_t min);
+ AppHintDesc *mDescriptor = nullptr;
+ sp<StaleHandler> mStaleHandler;
+ sp<MessageHandler> mPowerManagerHandler;
+ std::mutex mLock;
+};
+
+} // namespace pixel
+} // namespace impl
+} // namespace power
+} // namespace hardware
+} // namespace google
+} // namespace aidl
diff --git a/aidl/power-libperfmgr/PowerSessionManager.cpp b/aidl/power-libperfmgr/PowerSessionManager.cpp
new file mode 100644
index 0000000..f3cb29d
--- /dev/null
+++ b/aidl/power-libperfmgr/PowerSessionManager.cpp
@@ -0,0 +1,128 @@
+/*
+ * 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 <log/log.h>
+#include <utils/Trace.h>
+
+#include "PowerSessionManager.h"
+
+namespace aidl {
+namespace google {
+namespace hardware {
+namespace power {
+namespace impl {
+namespace pixel {
+
+void PowerSessionManager::setHintManager(std::shared_ptr<HintManager> const &hint_manager) {
+ // Only initialize hintmanager instance if hint is supported.
+ if (hint_manager->IsHintSupported(kDisableBoostHintName)) {
+ mHintManager = hint_manager;
+ }
+}
+
+void PowerSessionManager::updateHintMode(const std::string &mode, bool enabled) {
+ ALOGD("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;
+ }
+ }
+}
+
+int PowerSessionManager::getDisplayRefreshRate() {
+ return mDisplayRefreshRate;
+}
+
+void PowerSessionManager::addPowerSession(PowerHintSession *session) {
+ std::lock_guard<std::mutex> guard(mLock);
+ mSessions.insert(session);
+}
+
+void PowerSessionManager::removePowerSession(PowerHintSession *session) {
+ std::lock_guard<std::mutex> guard(mLock);
+ mSessions.erase(session);
+}
+
+bool PowerSessionManager::isAnySessionActive() {
+ std::lock_guard<std::mutex> guard(mLock);
+ for (PowerHintSession *s : mSessions) {
+ // session active and not stale is actually active.
+ if (s->isActive() && !s->isStale()) {
+ return true;
+ }
+ }
+ return false;
+}
+
+void PowerSessionManager::handleMessage(const Message &) {
+ if (isAnySessionActive()) {
+ disableSystemTopAppBoost();
+ } else {
+ enableSystemTopAppBoost();
+ }
+}
+
+void PowerSessionManager::enableSystemTopAppBoost() {
+ if (mHintManager) {
+ ALOGD("PowerSessionManager::enableSystemTopAppBoost!!");
+ if (ATRACE_ENABLED()) {
+ ATRACE_INT(kDisableBoostHintName.c_str(), 0);
+ }
+ mHintManager->EndHint(kDisableBoostHintName);
+ }
+}
+
+void PowerSessionManager::disableSystemTopAppBoost() {
+ if (mHintManager) {
+ ALOGD("PowerSessionManager::disableSystemTopAppBoost!!");
+ if (ATRACE_ENABLED()) {
+ ATRACE_INT(kDisableBoostHintName.c_str(), 1);
+ }
+ mHintManager->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;
+}
+
+Looper *PowerHintMonitor::getLooper() {
+ return mLooper;
+}
+
+} // namespace pixel
+} // namespace impl
+} // namespace power
+} // namespace hardware
+} // namespace google
+} // namespace aidl
diff --git a/aidl/power-libperfmgr/PowerSessionManager.h b/aidl/power-libperfmgr/PowerSessionManager.h
new file mode 100644
index 0000000..992e876
--- /dev/null
+++ b/aidl/power-libperfmgr/PowerSessionManager.h
@@ -0,0 +1,103 @@
+/*
+ * 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 "PowerHintSession.h"
+
+#include <android-base/properties.h>
+#include <perfmgr/HintManager.h>
+#include <utils/Looper.h>
+
+#include <mutex>
+#include <unordered_set>
+
+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);
+ int getDisplayRefreshRate();
+ // monitoring session status
+ void addPowerSession(PowerHintSession *session);
+ void removePowerSession(PowerHintSession *session);
+ bool isAnySessionActive();
+ void handleMessage(const Message &message) override;
+ void setHintManager(std::shared_ptr<HintManager> const &hint_manager);
+
+ // Singleton
+ static PowerSessionManager &getInstance() {
+ static PowerSessionManager instance;
+ return instance;
+ }
+
+ private:
+ void disableSystemTopAppBoost();
+ void enableSystemTopAppBoost();
+ const std::string kDisableBoostHintName;
+ std::shared_ptr<HintManager> mHintManager;
+ std::unordered_set<PowerHintSession *> mSessions;
+ std::mutex mLock;
+ int mDisplayRefreshRate;
+ // Singleton
+ PowerSessionManager()
+ : kDisableBoostHintName(::android::base::GetProperty(kPowerHalAdpfDisableTopAppBoost,
+ "ADPF_DISABLE_TA_BOOST")),
+ mHintManager(nullptr),
+ mDisplayRefreshRate(60) {}
+ PowerSessionManager(PowerSessionManager const &) = delete;
+ void operator=(PowerSessionManager const &) = delete;
+};
+
+class PowerHintMonitor : public Thread {
+ public:
+ void start();
+ bool threadLoop() override;
+ Looper *getLooper();
+ // Singleton
+ static PowerHintMonitor &getInstance() {
+ static PowerHintMonitor instance;
+ return instance;
+ }
+ PowerHintMonitor(PowerHintMonitor const &) = delete;
+ void operator=(PowerHintMonitor const &) = delete;
+
+ private:
+ Looper *mLooper;
+ // Singleton
+ PowerHintMonitor() : Thread(false), mLooper(new Looper(true)) {}
+};
+
+} // namespace pixel
+} // namespace impl
+} // namespace power
+} // namespace hardware
+} // namespace google
+} // namespace aidl
diff --git a/aidl/power-libperfmgr/service.cpp b/aidl/power-libperfmgr/service.cpp
index c2bcc44..09a8429 100644
--- a/aidl/power-libperfmgr/service.cpp
+++ b/aidl/power-libperfmgr/service.cpp
@@ -25,9 +25,12 @@
#include "Power.h"
#include "PowerExt.h"
+#include "PowerSessionManager.h"
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");
@@ -65,6 +68,11 @@
CHECK(status == STATUS_OK);
LOG(INFO) << "Xiaomi Power HAL AIDL Service with Extension is started.";
+ if (::android::base::GetIntProperty("vendor.powerhal.adpf.rate", -1) != -1) {
+ PowerHintMonitor::getInstance().start();
+ PowerSessionManager::getInstance().setHintManager(hm);
+ }
+
std::thread initThread([&]() {
::android::base::WaitForProperty(kPowerHalInitProp.data(), "1");
hm->Start();