Snap for 8692753 from 03168e2a876d44b23f5b4811892bfe3134cf0cf4 to tm-frc-odp-release
Change-Id: I29d66563b0a6f96b02d56cf7e9fdb76ab49bff31
diff --git a/power-libperfmgr/aidl/Power.cpp b/power-libperfmgr/aidl/Power.cpp
index cff09ff..5446918 100644
--- a/power-libperfmgr/aidl/Power.cpp
+++ b/power-libperfmgr/aidl/Power.cpp
@@ -188,6 +188,7 @@
ndk::ScopedAStatus Power::setBoost(Boost type, int32_t durationMs) {
LOG(DEBUG) << "Power setBoost: " << toString(type) << " duration: " << durationMs;
+ PowerSessionManager::getInstance()->updateHintBoost(toString(type), durationMs);
switch (type) {
case Boost::INTERACTION:
if (mVRModeOn || mSustainedPerfModeOn) {
@@ -243,6 +244,7 @@
boolToString(mSustainedPerfModeOn)));
// Dump nodes through libperfmgr
HintManager::GetInstance()->DumpToFd(fd);
+ PowerSessionManager::getInstance()->dumpToFd(fd);
mAdaptiveCpu->DumpToFd(fd);
if (!::android::base::WriteStringToFd(buf, fd)) {
PLOG(ERROR) << "Failed to dump state to fd";
diff --git a/power-libperfmgr/aidl/PowerExt.cpp b/power-libperfmgr/aidl/PowerExt.cpp
index f00ff99..751e5e8 100644
--- a/power-libperfmgr/aidl/PowerExt.cpp
+++ b/power-libperfmgr/aidl/PowerExt.cpp
@@ -66,6 +66,7 @@
ndk::ScopedAStatus PowerExt::setBoost(const std::string &boost, int32_t durationMs) {
LOG(DEBUG) << "PowerExt setBoost: " << boost << " duration: " << durationMs;
+ PowerSessionManager::getInstance()->updateHintBoost(boost, durationMs);
if (durationMs > 0) {
HintManager::GetInstance()->DoHint(boost, std::chrono::milliseconds(durationMs));
diff --git a/power-libperfmgr/aidl/PowerHintSession.cpp b/power-libperfmgr/aidl/PowerHintSession.cpp
index f3eec9e..ef95960 100644
--- a/power-libperfmgr/aidl/PowerHintSession.cpp
+++ b/power-libperfmgr/aidl/PowerHintSession.cpp
@@ -47,27 +47,6 @@
using std::chrono::nanoseconds;
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("PowerHintSession:%s: skip", __func__);
- return 0;
- }
- return syscall(__NR_sched_setattr, pid, attr, flags);
-}
static inline int64_t ns_to_100us(int64_t ns) {
return ns / 100000;
@@ -160,7 +139,7 @@
}
PowerSessionManager::getInstance()->addPowerSession(this);
// init boost
- setUclamp(HintManager::GetInstance()->GetAdpfProfile()->mUclampMinHigh);
+ setSessionUclampMin(HintManager::GetInstance()->GetAdpfProfile()->mUclampMinInit);
ALOGV("PowerHintSession created: %s", mDescriptor->toString().c_str());
}
@@ -205,35 +184,31 @@
}
}
-int PowerHintSession::setUclamp(int32_t min, bool update) {
- std::lock_guard<std::mutex> guard(mLock);
- min = std::max(0, min);
- min = std::min(min, kMaxUclampValue);
+int PowerHintSession::setSessionUclampMin(int32_t min) {
+ {
+ std::lock_guard<std::mutex> guard(mSessionLock);
+ mDescriptor->current_min = min;
+ }
+ PowerSessionManager::getInstance()->setUclampMin(this, min);
if (ATRACE_ENABLED()) {
const std::string idstr = getIdString();
std::string sz = StringPrintf("adpf.%s-min", idstr.c_str());
ATRACE_INT(sz.c_str(), min);
}
- 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 = kMaxUclampValue;
-
- 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, kMaxUclampValue);
- }
- if (update) {
- mDescriptor->current_min = min;
- }
return 0;
}
+int PowerHintSession::getUclampMin() {
+ return mDescriptor->current_min;
+}
+
+void PowerHintSession::dumpToStream(std::ostream &stream) {
+ stream << "ID.Min.Act.Stale(" << getIdString();
+ stream << ", " << mDescriptor->current_min;
+ stream << ", " << mDescriptor->is_active;
+ stream << ", " << isStale() << ")";
+}
+
ndk::ScopedAStatus PowerHintSession::pause() {
if (mSessionClosed) {
ALOGE("Error: session is dead");
@@ -242,8 +217,8 @@
if (!mDescriptor->is_active.load())
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
// Reset to default uclamp value.
- setUclamp(0, false);
mDescriptor->is_active.store(false);
+ setStale();
if (ATRACE_ENABLED()) {
const std::string idstr = getIdString();
std::string sz = StringPrintf("adpf.%s-active", idstr.c_str());
@@ -263,7 +238,7 @@
mDescriptor->is_active.store(true);
mHintTimerHandler->updateHintTimer(0);
// resume boost
- setUclamp(mDescriptor->current_min, false);
+ setSessionUclampMin(mDescriptor->current_min);
if (ATRACE_ENABLED()) {
const std::string idstr = getIdString();
std::string sz = StringPrintf("adpf.%s-active", idstr.c_str());
@@ -278,7 +253,7 @@
if (!mSessionClosed.compare_exchange_strong(sessionClosedExpectedToBe, true)) {
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
}
- setUclamp(0);
+ setSessionUclampMin(0);
PowerSessionManager::getInstance()->removePowerSession(this);
updateUniveralBoostMode();
return ndk::ScopedAStatus::ok();
@@ -342,21 +317,10 @@
actualDurations.back().durationNanos - mDescriptor->duration.count() > 0);
}
- if (PowerHintMonitor::getInstance()->isRunning() && isStale()) {
- mDescriptor->integral_error =
- std::max(adpfConfig->getPidIInitDivI(), mDescriptor->integral_error);
- if (ATRACE_ENABLED()) {
- const std::string idstr = getIdString();
- std::string sz = StringPrintf("adpf.%s-wakeup", idstr.c_str());
- ATRACE_INT(sz.c_str(), mDescriptor->integral_error);
- ATRACE_INT(sz.c_str(), 0);
- }
- }
-
mHintTimerHandler->updateHintTimer(actualDurations);
if (!adpfConfig->mPidOn) {
- setUclamp(adpfConfig->mUclampMinHigh);
+ setSessionUclampMin(adpfConfig->mUclampMinHigh);
return ndk::ScopedAStatus::ok();
}
int64_t output = convertWorkDurationToBoostByPid(
@@ -367,7 +331,7 @@
int next_min = std::min(static_cast<int>(adpfConfig->mUclampMinHigh),
mDescriptor->current_min + static_cast<int>(output));
next_min = std::max(static_cast<int>(adpfConfig->mUclampMinLow), next_min);
- setUclamp(next_min);
+ setSessionUclampMin(next_min);
mAdaptiveCpu->ReportWorkDurations(actualDurations, mDescriptor->duration);
@@ -410,13 +374,44 @@
void PowerHintSession::setStale() {
// Reset to default uclamp value.
- setUclamp(0, false);
+ PowerSessionManager::getInstance()->setUclampMin(this, 0);
// Deliver a task to check if all sessions are inactive.
updateUniveralBoostMode();
}
+void PowerHintSession::wakeup() {
+ if (ATRACE_ENABLED()) {
+ std::string tag =
+ StringPrintf("wakeup.%s(a:%d,s:%d)", getIdString().c_str(), isActive(), isStale());
+ ATRACE_NAME(tag.c_str());
+ }
+ // We only wake up non-paused and stale sessions
+ if (!isActive() || !isStale())
+ return;
+ std::shared_ptr<AdpfConfig> adpfConfig = HintManager::GetInstance()->GetAdpfProfile();
+ int min = std::max(mDescriptor->current_min, static_cast<int>(adpfConfig->mUclampMinInit));
+ {
+ std::lock_guard<std::mutex> guard(mSessionLock);
+ mDescriptor->current_min = min;
+ }
+ PowerSessionManager::getInstance()->setUclampMinLocked(this, min);
+ PowerHintMonitor::getInstance()->getLooper()->removeMessages(mHintTimerHandler);
+ PowerHintMonitor::getInstance()->getLooper()->sendMessage(
+ mHintTimerHandler, Message(static_cast<int>(HintTimerHandler::POKE)));
+
+ if (ATRACE_ENABLED()) {
+ const std::string idstr = getIdString();
+ std::string sz = StringPrintf("adpf.%s-min", idstr.c_str());
+ ATRACE_INT(sz.c_str(), min);
+ }
+}
+
void PowerHintSession::HintTimerHandler::updateHintTimer(int64_t actualDurationNs) {
std::lock_guard<std::mutex> guard(mStaleLock);
+ PowerHintSession::HintTimerHandler::updateHintTimerLocked(actualDurationNs);
+}
+
+void PowerHintSession::HintTimerHandler::updateHintTimerLocked(int64_t actualDurationNs) {
std::shared_ptr<AdpfConfig> adpfConfig = HintManager::GetInstance()->GetAdpfProfile();
HintTimerState prevState = mState;
mState = MONITORING;
@@ -426,7 +421,7 @@
? mSession->mDescriptor->work_period
: mSession->mDescriptor->duration.count()) -
actualDurationNs);
- mNextStartTime.store(now + nextStartDur);
+ mNextStartTime.store(actualDurationNs <= 0 ? now : now + nextStartDur);
if (prevState != MONITORING) {
int64_t next =
static_cast<int64_t>(duration_cast<nanoseconds>(getEarlyBoostTime() - now).count());
@@ -501,11 +496,15 @@
ATRACE_CALL();
}
-void PowerHintSession::HintTimerHandler::handleMessage(const Message &) {
+void PowerHintSession::HintTimerHandler::handleMessage(const Message &msg) {
std::lock_guard<std::mutex> guard(mStaleLock);
if (mIsSessionDead) {
return;
}
+ if (msg.what == POKE) {
+ updateHintTimerLocked(0);
+ return;
+ }
std::shared_ptr<AdpfConfig> adpfConfig = HintManager::GetInstance()->GetAdpfProfile();
auto now = std::chrono::steady_clock::now();
auto staleTime = getStaleTime();
@@ -517,7 +516,7 @@
// Schedule for the early hint check.
PowerHintMonitor::getInstance()->getLooper()->removeMessages(mSession->mHintTimerHandler);
PowerHintMonitor::getInstance()->getLooper()->sendMessageDelayed(
- next, mSession->mHintTimerHandler, NULL);
+ next, mSession->mHintTimerHandler, static_cast<int>(HintTimerHandler::TIMER));
if (ATRACE_ENABLED()) {
const std::string idstr = mSession->getIdString();
std::string sz = StringPrintf("adpf.%s-timer.nexthint", idstr.c_str());
@@ -529,13 +528,13 @@
} else { // Check if it's time to do early boost.
if (adpfConfig->mEarlyBoostOn) {
mState = EARLY_BOOST;
- mSession->setUclamp(adpfConfig->mUclampMinHigh);
+ mSession->setSessionUclampMin(adpfConfig->mUclampMinHigh);
}
int64_t next = static_cast<int64_t>(duration_cast<nanoseconds>(staleTime - now).count());
// Schedule for the stale timeout check.
PowerHintMonitor::getInstance()->getLooper()->removeMessages(mSession->mHintTimerHandler);
PowerHintMonitor::getInstance()->getLooper()->sendMessageDelayed(
- next, mSession->mHintTimerHandler, NULL);
+ next, mSession->mHintTimerHandler, static_cast<int>(HintTimerHandler::TIMER));
}
if (ATRACE_ENABLED()) {
const std::string idstr = mSession->getIdString();
diff --git a/power-libperfmgr/aidl/PowerHintSession.h b/power-libperfmgr/aidl/PowerHintSession.h
index 62a89b7..600434d 100644
--- a/power-libperfmgr/aidl/PowerHintSession.h
+++ b/power-libperfmgr/aidl/PowerHintSession.h
@@ -43,7 +43,6 @@
using std::chrono::steady_clock;
using std::chrono::time_point;
-static const int32_t kMaxUclampValue = 1024;
struct AppHintDesc {
AppHintDesc(int32_t tgid, int32_t uid, std::vector<int> threadIds)
: tgid(tgid),
@@ -51,7 +50,6 @@
threadIds(std::move(threadIds)),
duration(0LL),
current_min(0),
- transitioanl_min(0),
is_active(true),
update_count(0),
integral_error(0),
@@ -64,7 +62,6 @@
const std::vector<int> threadIds;
nanoseconds duration;
int current_min;
- int transitioanl_min;
// status
std::atomic<bool> is_active;
// pid
@@ -89,14 +86,21 @@
const std::vector<WorkDuration> &actualDurations) override;
bool isActive();
bool isStale();
+ void wakeup();
+ void setStale();
// Is this hint session for a user application
bool isAppSession();
const std::vector<int> &getTidList() const;
- int restoreUclamp();
+ int getUclampMin();
+ void dumpToStream(std::ostream &stream);
private:
class HintTimerHandler : public MessageHandler {
public:
+ enum MsgType {
+ TIMER = 0,
+ POKE,
+ };
enum HintTimerState {
STALE,
MONITORING,
@@ -111,6 +115,7 @@
void handleMessage(const Message &message) override;
// Update HintTimer by actual work duration.
void updateHintTimer(int64_t actualDurationNs);
+ void updateHintTimerLocked(int64_t actualDurationNs);
// Update HintTimer by a list of work durations which could be used for
// calculating the work period.
void updateHintTimer(const std::vector<WorkDuration> &actualDurations);
@@ -128,15 +133,14 @@
};
private:
- void setStale();
void updateUniveralBoostMode();
- int setUclamp(int32_t min, bool update = true);
+ int setSessionUclampMin(int32_t min);
std::string getIdString() const;
const std::shared_ptr<AdaptiveCpu> mAdaptiveCpu;
AppHintDesc *mDescriptor = nullptr;
sp<HintTimerHandler> mHintTimerHandler;
sp<MessageHandler> mPowerManagerHandler;
- std::mutex mLock;
+ std::mutex mSessionLock;
std::atomic<bool> mSessionClosed = false;
};
diff --git a/power-libperfmgr/aidl/PowerSessionManager.cpp b/power-libperfmgr/aidl/PowerSessionManager.cpp
index be03e44..984b0d0 100644
--- a/power-libperfmgr/aidl/PowerSessionManager.cpp
+++ b/power-libperfmgr/aidl/PowerSessionManager.cpp
@@ -19,9 +19,11 @@
#include "PowerSessionManager.h"
+#include <android-base/file.h>
#include <log/log.h>
#include <perfmgr/HintManager.h>
#include <processgroup/processgroup.h>
+#include <sys/syscall.h>
#include <utils/Trace.h>
namespace aidl {
@@ -33,6 +35,47 @@
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) {
+ 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) {
@@ -49,6 +92,22 @@
}
}
+void PowerSessionManager::updateHintBoost(const std::string &boost, int32_t durationMs) {
+ ATRACE_CALL();
+ ALOGV("PowerSessionManager::updateHintBoost: boost: %s, durationMs: %d", boost.c_str(),
+ durationMs);
+ if (boost.compare("DISPLAY_UPDATE_IMMINENT") == 0) {
+ wakeSessions();
+ }
+}
+
+void PowerSessionManager::wakeSessions() {
+ std::lock_guard<std::mutex> guard(mLock);
+ for (PowerHintSession *s : mSessions) {
+ s->wakeup();
+ }
+}
+
int PowerSessionManager::getDisplayRefreshRate() {
return mDisplayRefreshRate;
}
@@ -56,6 +115,7 @@
void PowerSessionManager::addPowerSession(PowerHintSession *session) {
std::lock_guard<std::mutex> guard(mLock);
for (auto t : session->getTidList()) {
+ mTidSessionListMap[t].insert(session);
if (mTidRefCountMap.find(t) == mTidRefCountMap.end()) {
if (!SetTaskProfiles(t, {"ResetUclampGrp"})) {
ALOGW("Failed to set ResetUclampGrp task profile for tid:%d", t);
@@ -80,6 +140,7 @@
ALOGE("Unexpected Error! Failed to look up tid:%d in TidRefCountMap", t);
continue;
}
+ mTidSessionListMap[t].erase(session);
mTidRefCountMap[t]--;
if (mTidRefCountMap[t] <= 0) {
if (!SetTaskProfiles(t, {"NoResetUclampGrp"})) {
@@ -91,6 +152,25 @@
mSessions.erase(session);
}
+void PowerSessionManager::setUclampMin(PowerHintSession *session, int val) {
+ std::lock_guard<std::mutex> 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->isStale())
+ continue;
+ tidMax = std::max(tidMax, s->getUclampMin());
+ }
+ val = std::max(val, tidMax);
+ set_uclamp_min(t, val);
+ }
+}
+
std::optional<bool> PowerSessionManager::isAnyAppSessionActive() {
std::lock_guard<std::mutex> guard(mLock);
bool active = false;
@@ -122,6 +202,28 @@
}
}
+void PowerSessionManager::dumpToFd(int fd) {
+ std::ostringstream dump_buf;
+ std::lock_guard<std::mutex> 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!!");
diff --git a/power-libperfmgr/aidl/PowerSessionManager.h b/power-libperfmgr/aidl/PowerSessionManager.h
index 9d3a7de..9007659 100644
--- a/power-libperfmgr/aidl/PowerSessionManager.h
+++ b/power-libperfmgr/aidl/PowerSessionManager.h
@@ -45,12 +45,15 @@
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 setUclampMin(PowerHintSession *session, int min);
+ void setUclampMinLocked(PowerHintSession *session, int min);
void handleMessage(const Message &message) override;
+ void dumpToFd(int fd);
// Singleton
static sp<PowerSessionManager> getInstance() {
@@ -59,21 +62,27 @@
}
private:
+ void wakeSessions();
std::optional<bool> isAnyAppSessionActive();
void disableSystemTopAppBoost();
void enableSystemTopAppBoost();
const std::string kDisableBoostHintName;
+
std::unordered_set<PowerHintSession *> mSessions; // protected by mLock
std::unordered_map<int, int> mTidRefCountMap; // protected by mLock
+ std::unordered_map<int, std::unordered_set<PowerHintSession *>> mTidSessionListMap;
+ bool mActive; // protected by mLock
+ /**
+ * mLock to pretect the above data objects opertions.
+ **/
std::mutex mLock;
int mDisplayRefreshRate;
- bool mActive; // protected by mLock
// Singleton
PowerSessionManager()
: kDisableBoostHintName(::android::base::GetProperty(kPowerHalAdpfDisableTopAppBoost,
"ADPF_DISABLE_TA_BOOST")),
- mDisplayRefreshRate(60),
- mActive(false) {}
+ mActive(false),
+ mDisplayRefreshRate(60) {}
PowerSessionManager(PowerSessionManager const &) = delete;
void operator=(PowerSessionManager const &) = delete;
};
diff --git a/power-libperfmgr/libperfmgr/AdpfConfig.cc b/power-libperfmgr/libperfmgr/AdpfConfig.cc
index 1a5f729..36e5209 100644
--- a/power-libperfmgr/libperfmgr/AdpfConfig.cc
+++ b/power-libperfmgr/libperfmgr/AdpfConfig.cc
@@ -19,11 +19,10 @@
#include "perfmgr/AdpfConfig.h"
+#include <android-base/file.h>
#include <android-base/logging.h>
-#include <android-base/parsedouble.h>
-#include <android-base/properties.h>
-#include <string>
+#include <sstream>
namespace android {
namespace perfmgr {
@@ -38,5 +37,33 @@
return (mPidI == 0) ? 0 : static_cast<int64_t>(mPidILow / mPidI);
}
+void AdpfConfig::dumpToFd(int fd) {
+ std::ostringstream dump_buf;
+ dump_buf << "Name: " << mName << "\n";
+ dump_buf << "PID_On: " << mPidOn << "\n";
+ dump_buf << "PID_Po: " << mPidPo << "\n";
+ dump_buf << "PID_Pu: " << mPidPu << "\n";
+ dump_buf << "PID_I: " << mPidI << "\n";
+ dump_buf << "PID_I_Init: " << mPidIInit << "\n";
+ dump_buf << "PID_I_High: " << mPidIHigh << "\n";
+ dump_buf << "PID_I_Low: " << mPidILow << "\n";
+ dump_buf << "PID_Do: " << mPidDo << "\n";
+ dump_buf << "PID_Du: " << mPidDu << "\n";
+ dump_buf << "SamplingWindow_P: " << mSamplingWindowP << "\n";
+ dump_buf << "SamplingWindow_I: " << mSamplingWindowI << "\n";
+ dump_buf << "SamplingWindow_D: " << mSamplingWindowD << "\n";
+ dump_buf << "UclampMin_On: " << mUclampMinOn << "\n";
+ dump_buf << "UclampMin_High: " << mUclampMinHigh << "\n";
+ dump_buf << "UclampMin_Low: " << mUclampMinLow << "\n";
+ dump_buf << "ReportingRateLimitNs: " << mReportingRateLimitNs << "\n";
+ dump_buf << "EarlyBoost_On: " << mEarlyBoostOn << "\n";
+ dump_buf << "EarlyBoost_TimeFactor: " << mEarlyBoostTimeFactor << "\n";
+ dump_buf << "TargetTimeFactor: " << mTargetTimeFactor << "\n";
+ dump_buf << "StaleTimeFactor: " << mStaleTimeFactor << "\n";
+ if (!android::base::WriteStringToFd(dump_buf.str(), fd)) {
+ LOG(ERROR) << "Failed to dump ADPF profile to fd: " << fd;
+ }
+}
+
} // namespace perfmgr
} // namespace android
diff --git a/power-libperfmgr/libperfmgr/HintManager.cc b/power-libperfmgr/libperfmgr/HintManager.cc
index e19f205..73ebc43 100644
--- a/power-libperfmgr/libperfmgr/HintManager.cc
+++ b/power-libperfmgr/libperfmgr/HintManager.cc
@@ -256,6 +256,19 @@
if (!android::base::WriteStringToFd(footer, fd)) {
LOG(ERROR) << "Failed to dump fd: " << fd;
}
+
+ // Dump current ADPF profile
+ if (GetAdpfProfile()) {
+ header = "========== Begin current adpf profile ==========\n";
+ if (!android::base::WriteStringToFd(header, fd)) {
+ LOG(ERROR) << "Failed to dump fd: " << fd;
+ }
+ GetAdpfProfile()->dumpToFd(fd);
+ footer = "========== End current adpf profile ==========\n";
+ if (!android::base::WriteStringToFd(footer, fd)) {
+ LOG(ERROR) << "Failed to dump fd: " << fd;
+ }
+ }
fsync(fd);
}
@@ -627,6 +640,7 @@
int64_t pidIHighLimit;
int64_t pidILowLimit;
bool adpfUclamp;
+ uint32_t uclampMinInit;
uint32_t uclampMinHighLimit;
uint32_t uclampMinLowLimit;
uint64_t samplingWindowP;
@@ -734,6 +748,13 @@
}
adpfUclamp = adpfs[i]["UclampMin_On"].asBool();
+ if (adpfs[i]["UclampMin_Init"].empty() || !adpfs[i]["UclampMin_Init"].isInt()) {
+ LOG(ERROR) << "Failed to read AdpfConfig[" << name << "][UclampMin_Init]'s Values";
+ adpfs_parsed.clear();
+ return adpfs_parsed;
+ }
+ uclampMinInit = adpfs[i]["UclampMin_Init"].asInt();
+
if (adpfs[i]["UclampMin_High"].empty() || !adpfs[i]["UclampMin_High"].isUInt()) {
LOG(ERROR) << "Failed to read AdpfConfig[" << name << "][UclampMin_High]'s Values";
adpfs_parsed.clear();
@@ -810,9 +831,9 @@
adpfs_parsed.emplace_back(std::make_shared<AdpfConfig>(
name, pidOn, pidPOver, pidPUnder, pidI, pidIInit, pidIHighLimit, pidILowLimit,
- pidDOver, pidDUnder, adpfUclamp, uclampMinHighLimit, uclampMinLowLimit,
- samplingWindowP, samplingWindowI, samplingWindowD, reportingRate, earlyBoostOn,
- earlyBoostTimeFactor, targetTimeFactor, staleTimeFactor));
+ pidDOver, pidDUnder, adpfUclamp, uclampMinInit, uclampMinHighLimit,
+ uclampMinLowLimit, samplingWindowP, samplingWindowI, samplingWindowD, reportingRate,
+ earlyBoostOn, earlyBoostTimeFactor, targetTimeFactor, staleTimeFactor));
}
LOG(INFO) << adpfs_parsed.size() << " AdpfConfigs parsed successfully";
return adpfs_parsed;
diff --git a/power-libperfmgr/libperfmgr/include/perfmgr/AdpfConfig.h b/power-libperfmgr/libperfmgr/include/perfmgr/AdpfConfig.h
index d7873fb..bbe4d09 100644
--- a/power-libperfmgr/libperfmgr/include/perfmgr/AdpfConfig.h
+++ b/power-libperfmgr/libperfmgr/include/perfmgr/AdpfConfig.h
@@ -35,6 +35,7 @@
double mPidDu;
// Uclamp boost control
bool mUclampMinOn;
+ uint32_t mUclampMinInit;
uint32_t mUclampMinHigh;
uint32_t mUclampMinLow;
// Batch update control
@@ -42,6 +43,7 @@
uint64_t mSamplingWindowI;
uint64_t mSamplingWindowD;
int64_t mReportingRateLimitNs;
+ int64_t mFreezeDurationNs;
bool mEarlyBoostOn;
double mEarlyBoostTimeFactor;
double mTargetTimeFactor;
@@ -51,13 +53,16 @@
int64_t getPidIInitDivI();
int64_t getPidIHighDivI();
int64_t getPidILowDivI();
+ bool isEarlyBoostTimerEnabled();
+ bool isStaleTimerEnabled();
+ void dumpToFd(int fd);
AdpfConfig(std::string name, bool pidOn, double pidPo, double pidPu, double pidI,
int64_t pidIInit, int64_t pidIHigh, int64_t pidILow, double pidDo, double pidDu,
- bool uclampMinOn, uint32_t uclampMinHigh, uint32_t uclampMinLow,
- uint64_t samplingWindowP, uint64_t samplingWindowI, uint64_t samplingWindowD,
- int64_t reportingRateLimitNs, bool earlyBoostOn, double earlyBoostTimeFactor,
- double targetTimeFactor, double staleTimeFactor)
+ bool uclampMinOn, uint32_t uclampMinInit, uint32_t uclampMinHigh,
+ uint32_t uclampMinLow, uint64_t samplingWindowP, uint64_t samplingWindowI,
+ uint64_t samplingWindowD, int64_t reportingRateLimitNs, bool earlyBoostOn,
+ double earlyBoostTimeFactor, double targetTimeFactor, double staleTimeFactor)
: mName(std::move(name)),
mPidOn(pidOn),
mPidPo(pidPo),
@@ -69,6 +74,7 @@
mPidDo(pidDo),
mPidDu(pidDu),
mUclampMinOn(uclampMinOn),
+ mUclampMinInit(uclampMinInit),
mUclampMinHigh(uclampMinHigh),
mUclampMinLow(uclampMinLow),
mSamplingWindowP(samplingWindowP),
diff --git a/power-libperfmgr/libperfmgr/tests/HintManagerTest.cc b/power-libperfmgr/libperfmgr/tests/HintManagerTest.cc
index 7e13930..48a8494 100644
--- a/power-libperfmgr/libperfmgr/tests/HintManagerTest.cc
+++ b/power-libperfmgr/libperfmgr/tests/HintManagerTest.cc
@@ -143,6 +143,7 @@
"SamplingWindow_I": 0,
"SamplingWindow_D": 1,
"UclampMin_On": true,
+ "UclampMin_Init": 100,
"UclampMin_High": 384,
"UclampMin_Low": 0,
"ReportingRateLimitNs": 166666660,
@@ -166,6 +167,7 @@
"SamplingWindow_I": 0,
"SamplingWindow_D": 0,
"UclampMin_On": true,
+ "UclampMin_Init": 200,
"UclampMin_High": 157,
"UclampMin_Low": 157,
"ReportingRateLimitNs": 83333330,
@@ -758,6 +760,8 @@
EXPECT_EQ(0LLU, adpfs[1]->mSamplingWindowD);
EXPECT_TRUE(adpfs[0]->mUclampMinOn);
EXPECT_TRUE(adpfs[1]->mUclampMinOn);
+ EXPECT_EQ(100U, adpfs[0]->mUclampMinInit);
+ EXPECT_EQ(200U, adpfs[1]->mUclampMinInit);
EXPECT_EQ(384U, adpfs[0]->mUclampMinHigh);
EXPECT_EQ(157U, adpfs[1]->mUclampMinHigh);
EXPECT_EQ(0U, adpfs[0]->mUclampMinLow);
diff --git a/thermal/Android.bp b/thermal/Android.bp
index b91625b..4ad1cab 100644
--- a/thermal/Android.bp
+++ b/thermal/Android.bp
@@ -21,12 +21,28 @@
pid: {
apply_1_0: {
srcs: [
+ "pid_1_0/service.cpp",
+ "pid_1_0/Thermal.cpp",
+ "pid_1_0/thermal-helper.cpp",
"pid_1_0/utils/thermal_throttling.cpp",
+ "pid_1_0/utils/thermal_info.cpp",
+ "pid_1_0/utils/thermal_files.cpp",
+ "pid_1_0/utils/power_files.cpp",
+ "pid_1_0/utils/powerhal_helper.cpp",
+ "pid_1_0/utils/thermal_watcher.cpp",
],
},
apply_2_0: {
srcs: [
+ "service.cpp",
+ "Thermal.cpp",
+ "thermal-helper.cpp",
"utils/thermal_throttling.cpp",
+ "utils/thermal_info.cpp",
+ "utils/thermal_files.cpp",
+ "utils/power_files.cpp",
+ "utils/powerhal_helper.cpp",
+ "utils/thermal_watcher.cpp",
],
},
},
@@ -45,16 +61,6 @@
init_rc: [
"android.hardware.thermal@2.0-service.pixel.rc",
],
- srcs: [
- "service.cpp",
- "Thermal.cpp",
- "thermal-helper.cpp",
- "utils/thermal_info.cpp",
- "utils/thermal_files.cpp",
- "utils/power_files.cpp",
- "utils/powerhal_helper.cpp",
- "utils/thermal_watcher.cpp",
- ],
shared_libs: [
"libbase",
"libcutils",
diff --git a/thermal/pid_1_0/Thermal.cpp b/thermal/pid_1_0/Thermal.cpp
new file mode 100644
index 0000000..2f41445
--- /dev/null
+++ b/thermal/pid_1_0/Thermal.cpp
@@ -0,0 +1,740 @@
+/*
+ * 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 ATRACE_TAG (ATRACE_TAG_THERMAL | ATRACE_TAG_HAL)
+
+#include "Thermal.h"
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <hidl/HidlTransportSupport.h>
+#include <utils/Trace.h>
+
+#include <cerrno>
+#include <mutex>
+#include <string>
+
+#include "thermal-helper.h"
+
+namespace android {
+namespace hardware {
+namespace thermal {
+namespace V2_0 {
+namespace implementation {
+
+namespace {
+
+using ::android::hardware::interfacesEqual;
+using ::android::hardware::thermal::V1_0::ThermalStatus;
+using ::android::hardware::thermal::V1_0::ThermalStatusCode;
+
+template <typename T, typename U>
+Return<void> setFailureAndCallback(T _hidl_cb, hidl_vec<U> data, std::string_view debug_msg) {
+ ThermalStatus status;
+ status.code = ThermalStatusCode::FAILURE;
+ status.debugMessage = debug_msg.data();
+ _hidl_cb(status, data);
+ return Void();
+}
+
+template <typename T, typename U>
+Return<void> setInitFailureAndCallback(T _hidl_cb, hidl_vec<U> data) {
+ return setFailureAndCallback(_hidl_cb, data, "Failure initializing thermal HAL");
+}
+
+} // namespace
+
+// On init we will spawn a thread which will continually watch for
+// throttling. When throttling is seen, if we have a callback registered
+// the thread will call notifyThrottling() else it will log the dropped
+// throttling event and do nothing. The thread is only killed when
+// Thermal() is killed.
+Thermal::Thermal()
+ : thermal_helper_(
+ std::bind(&Thermal::sendThermalChangedCallback, this, std::placeholders::_1)) {}
+
+// Methods from ::android::hardware::thermal::V1_0::IThermal.
+Return<void> Thermal::getTemperatures(getTemperatures_cb _hidl_cb) {
+ ThermalStatus status;
+ status.code = ThermalStatusCode::SUCCESS;
+ hidl_vec<Temperature_1_0> temperatures;
+
+ if (!thermal_helper_.isInitializedOk()) {
+ LOG(ERROR) << "ThermalHAL not initialized properly.";
+ return setInitFailureAndCallback(_hidl_cb, temperatures);
+ }
+
+ if (!thermal_helper_.fillTemperatures(&temperatures)) {
+ return setFailureAndCallback(_hidl_cb, temperatures, "Failed to read thermal sensors.");
+ }
+
+ _hidl_cb(status, temperatures);
+ return Void();
+}
+
+Return<void> Thermal::getCpuUsages(getCpuUsages_cb _hidl_cb) {
+ ThermalStatus status;
+ status.code = ThermalStatusCode::SUCCESS;
+ hidl_vec<CpuUsage> cpu_usages;
+
+ if (!thermal_helper_.isInitializedOk()) {
+ return setInitFailureAndCallback(_hidl_cb, cpu_usages);
+ }
+
+ if (!thermal_helper_.fillCpuUsages(&cpu_usages)) {
+ return setFailureAndCallback(_hidl_cb, cpu_usages, "Failed to get CPU usages.");
+ }
+
+ _hidl_cb(status, cpu_usages);
+ return Void();
+}
+
+Return<void> Thermal::getCoolingDevices(getCoolingDevices_cb _hidl_cb) {
+ ThermalStatus status;
+ status.code = ThermalStatusCode::SUCCESS;
+ hidl_vec<CoolingDevice_1_0> cooling_devices;
+
+ if (!thermal_helper_.isInitializedOk()) {
+ return setInitFailureAndCallback(_hidl_cb, cooling_devices);
+ }
+ _hidl_cb(status, cooling_devices);
+ return Void();
+}
+
+Return<void> Thermal::getCurrentTemperatures(bool filterType, TemperatureType_2_0 type,
+ getCurrentTemperatures_cb _hidl_cb) {
+ ThermalStatus status;
+ status.code = ThermalStatusCode::SUCCESS;
+ hidl_vec<Temperature_2_0> temperatures;
+
+ if (!thermal_helper_.isInitializedOk()) {
+ LOG(ERROR) << "ThermalHAL not initialized properly.";
+ return setInitFailureAndCallback(_hidl_cb, temperatures);
+ }
+
+ if (!thermal_helper_.fillCurrentTemperatures(filterType, false, type, &temperatures)) {
+ return setFailureAndCallback(_hidl_cb, temperatures, "Failed to read thermal sensors.");
+ }
+
+ _hidl_cb(status, temperatures);
+ return Void();
+}
+
+Return<void> Thermal::getTemperatureThresholds(bool filterType, TemperatureType_2_0 type,
+ getTemperatureThresholds_cb _hidl_cb) {
+ ThermalStatus status;
+ status.code = ThermalStatusCode::SUCCESS;
+ hidl_vec<TemperatureThreshold> temperatures;
+
+ if (!thermal_helper_.isInitializedOk()) {
+ LOG(ERROR) << "ThermalHAL not initialized properly.";
+ return setInitFailureAndCallback(_hidl_cb, temperatures);
+ }
+
+ if (!thermal_helper_.fillTemperatureThresholds(filterType, type, &temperatures)) {
+ return setFailureAndCallback(_hidl_cb, temperatures, "Failed to read thermal sensors.");
+ }
+
+ _hidl_cb(status, temperatures);
+ return Void();
+}
+
+Return<void> Thermal::getCurrentCoolingDevices(bool filterType, CoolingType type,
+ getCurrentCoolingDevices_cb _hidl_cb) {
+ ThermalStatus status;
+ status.code = ThermalStatusCode::SUCCESS;
+ hidl_vec<CoolingDevice_2_0> cooling_devices;
+
+ if (!thermal_helper_.isInitializedOk()) {
+ LOG(ERROR) << "ThermalHAL not initialized properly.";
+ return setInitFailureAndCallback(_hidl_cb, cooling_devices);
+ }
+
+ if (!thermal_helper_.fillCurrentCoolingDevices(filterType, type, &cooling_devices)) {
+ return setFailureAndCallback(_hidl_cb, cooling_devices, "Failed to read cooling devices.");
+ }
+
+ _hidl_cb(status, cooling_devices);
+ return Void();
+}
+
+Return<void> Thermal::registerThermalChangedCallback(const sp<IThermalChangedCallback> &callback,
+ bool filterType, TemperatureType_2_0 type,
+ registerThermalChangedCallback_cb _hidl_cb) {
+ ThermalStatus status;
+ hidl_vec<Temperature_2_0> temperatures;
+
+ ATRACE_CALL();
+ if (callback == nullptr) {
+ status.code = ThermalStatusCode::FAILURE;
+ status.debugMessage = "Invalid nullptr callback";
+ LOG(ERROR) << status.debugMessage;
+ _hidl_cb(status);
+ return Void();
+ } else {
+ status.code = ThermalStatusCode::SUCCESS;
+ }
+ std::lock_guard<std::mutex> _lock(thermal_callback_mutex_);
+ if (std::any_of(callbacks_.begin(), callbacks_.end(), [&](const CallbackSetting &c) {
+ return interfacesEqual(c.callback, callback);
+ })) {
+ status.code = ThermalStatusCode::FAILURE;
+ status.debugMessage = "Same callback registered already";
+ LOG(ERROR) << status.debugMessage;
+ } else {
+ callbacks_.emplace_back(callback, filterType, type);
+ LOG(INFO) << "a callback has been registered to ThermalHAL, isFilter: " << filterType
+ << " Type: " << android::hardware::thermal::V2_0::toString(type);
+ }
+ _hidl_cb(status);
+
+ // Send notification right away after thermal callback registration
+ if (thermal_helper_.fillCurrentTemperatures(filterType, true, type, &temperatures)) {
+ for (const auto &t : temperatures) {
+ if (!filterType || t.type == type) {
+ LOG(INFO) << "Sending notification: "
+ << " Type: " << android::hardware::thermal::V2_0::toString(t.type)
+ << " Name: " << t.name << " CurrentValue: " << t.value
+ << " ThrottlingStatus: "
+ << android::hardware::thermal::V2_0::toString(t.throttlingStatus);
+ callback->notifyThrottling(t);
+ }
+ }
+ }
+
+ return Void();
+}
+
+Return<void> Thermal::unregisterThermalChangedCallback(
+ const sp<IThermalChangedCallback> &callback, unregisterThermalChangedCallback_cb _hidl_cb) {
+ ThermalStatus status;
+ if (callback == nullptr) {
+ status.code = ThermalStatusCode::FAILURE;
+ status.debugMessage = "Invalid nullptr callback";
+ LOG(ERROR) << status.debugMessage;
+ _hidl_cb(status);
+ return Void();
+ } else {
+ status.code = ThermalStatusCode::SUCCESS;
+ }
+ bool removed = false;
+ std::lock_guard<std::mutex> _lock(thermal_callback_mutex_);
+ callbacks_.erase(
+ std::remove_if(
+ callbacks_.begin(), callbacks_.end(),
+ [&](const CallbackSetting &c) {
+ if (interfacesEqual(c.callback, callback)) {
+ LOG(INFO)
+ << "a callback has been unregistered to ThermalHAL, isFilter: "
+ << c.is_filter_type << " Type: "
+ << android::hardware::thermal::V2_0::toString(c.type);
+ removed = true;
+ return true;
+ }
+ return false;
+ }),
+ callbacks_.end());
+ if (!removed) {
+ status.code = ThermalStatusCode::FAILURE;
+ status.debugMessage = "The callback was not registered before";
+ LOG(ERROR) << status.debugMessage;
+ }
+ _hidl_cb(status);
+ return Void();
+}
+
+void Thermal::sendThermalChangedCallback(const Temperature_2_0 &t) {
+ ATRACE_CALL();
+ std::lock_guard<std::mutex> _lock(thermal_callback_mutex_);
+ LOG(VERBOSE) << "Sending notification: "
+ << " Type: " << android::hardware::thermal::V2_0::toString(t.type)
+ << " Name: " << t.name << " CurrentValue: " << t.value << " ThrottlingStatus: "
+ << android::hardware::thermal::V2_0::toString(t.throttlingStatus);
+
+ callbacks_.erase(
+ std::remove_if(callbacks_.begin(), callbacks_.end(),
+ [&](const CallbackSetting &c) {
+ if (!c.is_filter_type || t.type == c.type) {
+ Return<void> ret = c.callback->notifyThrottling(t);
+ if (!ret.isOk()) {
+ LOG(ERROR) << "a Thermal callback is dead, removed from "
+ "callback list.";
+ return true;
+ }
+ return false;
+ }
+ return false;
+ }),
+ callbacks_.end());
+}
+
+void Thermal::dumpVirtualSensorInfo(std::ostringstream *dump_buf) {
+ *dump_buf << "VirtualSensorInfo:" << std::endl;
+ const auto &map = thermal_helper_.GetSensorInfoMap();
+ for (const auto &sensor_info_pair : map) {
+ if (sensor_info_pair.second.virtual_sensor_info != nullptr) {
+ *dump_buf << " Name: " << sensor_info_pair.first << std::endl;
+ *dump_buf << " LinkedSensorName: [";
+ for (size_t i = 0;
+ i < sensor_info_pair.second.virtual_sensor_info->linked_sensors.size(); i++) {
+ *dump_buf << sensor_info_pair.second.virtual_sensor_info->linked_sensors[i] << " ";
+ }
+ *dump_buf << "]" << std::endl;
+ *dump_buf << " LinkedSensorCoefficient: [";
+ for (size_t i = 0; i < sensor_info_pair.second.virtual_sensor_info->coefficients.size();
+ i++) {
+ *dump_buf << sensor_info_pair.second.virtual_sensor_info->coefficients[i] << " ";
+ }
+ *dump_buf << "]" << std::endl;
+ *dump_buf << " Offset: " << sensor_info_pair.second.virtual_sensor_info->offset
+ << std::endl;
+ *dump_buf << " Trigger Sensor: "
+ << (sensor_info_pair.second.virtual_sensor_info->trigger_sensor.empty()
+ ? "N/A"
+ : sensor_info_pair.second.virtual_sensor_info->trigger_sensor)
+ << std::endl;
+ *dump_buf << " Formula: ";
+ switch (sensor_info_pair.second.virtual_sensor_info->formula) {
+ case FormulaOption::COUNT_THRESHOLD:
+ *dump_buf << "COUNT_THRESHOLD";
+ break;
+ case FormulaOption::WEIGHTED_AVG:
+ *dump_buf << "WEIGHTED_AVG";
+ break;
+ case FormulaOption::MAXIMUM:
+ *dump_buf << "MAXIMUM";
+ break;
+ case FormulaOption::MINIMUM:
+ *dump_buf << "MINIMUM";
+ break;
+ default:
+ *dump_buf << "NONE";
+ break;
+ }
+
+ *dump_buf << std::endl;
+ }
+ }
+}
+
+void Thermal::dumpThrottlingInfo(std::ostringstream *dump_buf) {
+ *dump_buf << "Throttling Info:" << std::endl;
+ const auto &map = thermal_helper_.GetSensorInfoMap();
+ const auto &thermal_throttling_status_map = thermal_helper_.GetThermalThrottlingStatusMap();
+ for (const auto &name_info_pair : map) {
+ if (name_info_pair.second.throttling_info->binded_cdev_info_map.size()) {
+ *dump_buf << " Name: " << name_info_pair.first << std::endl;
+ if (thermal_throttling_status_map.at(name_info_pair.first)
+ .pid_power_budget_map.size()) {
+ *dump_buf << " PID Info:" << std::endl;
+ *dump_buf << " K_po: [";
+ for (size_t i = 0; i < kThrottlingSeverityCount; ++i) {
+ *dump_buf << name_info_pair.second.throttling_info->k_po[i] << " ";
+ }
+ *dump_buf << "]" << std::endl;
+ *dump_buf << " K_pu: [";
+ for (size_t i = 0; i < kThrottlingSeverityCount; ++i) {
+ *dump_buf << name_info_pair.second.throttling_info->k_pu[i] << " ";
+ }
+ *dump_buf << "]" << std::endl;
+ *dump_buf << " K_i: [";
+ for (size_t i = 0; i < kThrottlingSeverityCount; ++i) {
+ *dump_buf << name_info_pair.second.throttling_info->k_i[i] << " ";
+ }
+ *dump_buf << "]" << std::endl;
+ *dump_buf << " K_d: [";
+ for (size_t i = 0; i < kThrottlingSeverityCount; ++i) {
+ *dump_buf << name_info_pair.second.throttling_info->k_d[i] << " ";
+ }
+ *dump_buf << "]" << std::endl;
+ *dump_buf << " i_max: [";
+ for (size_t i = 0; i < kThrottlingSeverityCount; ++i) {
+ *dump_buf << name_info_pair.second.throttling_info->i_max[i] << " ";
+ }
+ *dump_buf << "]" << std::endl;
+ *dump_buf << " max_alloc_power: [";
+ for (size_t i = 0; i < kThrottlingSeverityCount; ++i) {
+ *dump_buf << name_info_pair.second.throttling_info->max_alloc_power[i] << " ";
+ }
+ *dump_buf << "]" << std::endl;
+ *dump_buf << " min_alloc_power: [";
+ for (size_t i = 0; i < kThrottlingSeverityCount; ++i) {
+ *dump_buf << name_info_pair.second.throttling_info->min_alloc_power[i] << " ";
+ }
+ *dump_buf << "]" << std::endl;
+ *dump_buf << " s_power: [";
+ for (size_t i = 0; i < kThrottlingSeverityCount; ++i) {
+ *dump_buf << name_info_pair.second.throttling_info->s_power[i] << " ";
+ }
+ *dump_buf << "]" << std::endl;
+ *dump_buf << " i_cutoff: [";
+ for (size_t i = 0; i < kThrottlingSeverityCount; ++i) {
+ *dump_buf << name_info_pair.second.throttling_info->i_cutoff[i] << " ";
+ }
+ *dump_buf << "]" << std::endl;
+ }
+ *dump_buf << " Binded CDEV Info:" << std::endl;
+ for (const auto &binded_cdev_info_pair :
+ name_info_pair.second.throttling_info->binded_cdev_info_map) {
+ *dump_buf << " Cooling device name: " << binded_cdev_info_pair.first << std::endl;
+ if (thermal_throttling_status_map.at(name_info_pair.first)
+ .pid_power_budget_map.size()) {
+ *dump_buf << " WeightForPID: [";
+ for (size_t i = 0; i < kThrottlingSeverityCount; ++i) {
+ *dump_buf << binded_cdev_info_pair.second.cdev_weight_for_pid[i] << " ";
+ }
+ *dump_buf << "]" << std::endl;
+ }
+ *dump_buf << " Ceiling: [";
+ for (size_t i = 0; i < kThrottlingSeverityCount; ++i) {
+ *dump_buf << binded_cdev_info_pair.second.cdev_ceiling[i] << " ";
+ }
+ *dump_buf << "]" << std::endl;
+ *dump_buf << " Hard limit: [";
+ for (size_t i = 0; i < kThrottlingSeverityCount; ++i) {
+ *dump_buf << binded_cdev_info_pair.second.limit_info[i] << " ";
+ }
+ *dump_buf << "]" << std::endl;
+
+ if (!binded_cdev_info_pair.second.power_rail.empty()) {
+ *dump_buf << " Binded power rail: "
+ << binded_cdev_info_pair.second.power_rail << std::endl;
+ *dump_buf << " Power threshold: [";
+ for (size_t i = 0; i < kThrottlingSeverityCount; ++i) {
+ *dump_buf << binded_cdev_info_pair.second.power_thresholds[i] << " ";
+ }
+ *dump_buf << "]" << std::endl;
+ *dump_buf << " Floor with PowerLink: [";
+ for (size_t i = 0; i < kThrottlingSeverityCount; ++i) {
+ *dump_buf << binded_cdev_info_pair.second.cdev_floor_with_power_link[i]
+ << " ";
+ }
+ *dump_buf << "]" << std::endl;
+ *dump_buf << " Release logic: ";
+ switch (binded_cdev_info_pair.second.release_logic) {
+ case ReleaseLogic::INCREASE:
+ *dump_buf << "INCREASE";
+ break;
+ case ReleaseLogic::DECREASE:
+ *dump_buf << "DECREASE";
+ break;
+ case ReleaseLogic::STEPWISE:
+ *dump_buf << "STEPWISE";
+ break;
+ case ReleaseLogic::RELEASE_TO_FLOOR:
+ *dump_buf << "RELEASE_TO_FLOOR";
+ break;
+ default:
+ *dump_buf << "NONE";
+ break;
+ }
+ *dump_buf << std::endl;
+ *dump_buf << " high_power_check: " << std::boolalpha
+ << binded_cdev_info_pair.second.high_power_check << std::endl;
+ *dump_buf << " throttling_with_power_link: " << std::boolalpha
+ << binded_cdev_info_pair.second.throttling_with_power_link
+ << std::endl;
+ }
+ }
+ }
+ }
+}
+
+void Thermal::dumpThrottlingRequestStatus(std::ostringstream *dump_buf) {
+ const auto &thermal_throttling_status_map = thermal_helper_.GetThermalThrottlingStatusMap();
+ if (!thermal_throttling_status_map.size()) {
+ return;
+ }
+ *dump_buf << "Throttling Request Status " << std::endl;
+ for (const auto &thermal_throttling_status_pair : thermal_throttling_status_map) {
+ *dump_buf << " Name: " << thermal_throttling_status_pair.first << std::endl;
+ if (thermal_throttling_status_pair.second.pid_power_budget_map.size()) {
+ *dump_buf << " power budget request state" << std::endl;
+ for (const auto &request_pair :
+ thermal_throttling_status_pair.second.pid_power_budget_map) {
+ *dump_buf << " " << request_pair.first << ": " << request_pair.second
+ << std::endl;
+ }
+ }
+ if (thermal_throttling_status_pair.second.pid_cdev_request_map.size()) {
+ *dump_buf << " pid cdev request state" << std::endl;
+ for (const auto &request_pair :
+ thermal_throttling_status_pair.second.pid_cdev_request_map) {
+ *dump_buf << " " << request_pair.first << ": " << request_pair.second
+ << std::endl;
+ }
+ }
+ if (thermal_throttling_status_pair.second.hardlimit_cdev_request_map.size()) {
+ *dump_buf << " hard limit cdev request state" << std::endl;
+ for (const auto &request_pair :
+ thermal_throttling_status_pair.second.hardlimit_cdev_request_map) {
+ *dump_buf << " " << request_pair.first << ": " << request_pair.second
+ << std::endl;
+ }
+ }
+ if (thermal_throttling_status_pair.second.throttling_release_map.size()) {
+ *dump_buf << " cdev release state" << std::endl;
+ for (const auto &request_pair :
+ thermal_throttling_status_pair.second.throttling_release_map) {
+ *dump_buf << " " << request_pair.first << ": " << request_pair.second
+ << std::endl;
+ }
+ }
+ if (thermal_throttling_status_pair.second.cdev_status_map.size()) {
+ *dump_buf << " cdev request state" << std::endl;
+ for (const auto &request_pair : thermal_throttling_status_pair.second.cdev_status_map) {
+ *dump_buf << " " << request_pair.first << ": " << request_pair.second
+ << std::endl;
+ }
+ }
+ }
+}
+
+void Thermal::dumpPowerRailInfo(std::ostringstream *dump_buf) {
+ const auto &power_rail_info_map = thermal_helper_.GetPowerRailInfoMap();
+ const auto &power_status_map = thermal_helper_.GetPowerStatusMap();
+
+ *dump_buf << "Power Rail Info " << std::endl;
+ for (const auto &power_rail_pair : power_rail_info_map) {
+ *dump_buf << " Power Rail: " << power_rail_pair.first << std::endl;
+ *dump_buf << " Power Sample Count: " << power_rail_pair.second.power_sample_count
+ << std::endl;
+ *dump_buf << " Power Sample Delay: " << power_rail_pair.second.power_sample_delay.count()
+ << std::endl;
+ if (power_status_map.count(power_rail_pair.first)) {
+ auto power_history = power_status_map.at(power_rail_pair.first).power_history;
+ *dump_buf << " Last Updated AVG Power: "
+ << power_status_map.at(power_rail_pair.first).last_updated_avg_power << " mW"
+ << std::endl;
+ if (power_rail_pair.second.virtual_power_rail_info != nullptr) {
+ *dump_buf << " Formula=";
+ switch (power_rail_pair.second.virtual_power_rail_info->formula) {
+ case FormulaOption::COUNT_THRESHOLD:
+ *dump_buf << "COUNT_THRESHOLD";
+ break;
+ case FormulaOption::WEIGHTED_AVG:
+ *dump_buf << "WEIGHTED_AVG";
+ break;
+ case FormulaOption::MAXIMUM:
+ *dump_buf << "MAXIMUM";
+ break;
+ case FormulaOption::MINIMUM:
+ *dump_buf << "MINIMUM";
+ break;
+ default:
+ *dump_buf << "NONE";
+ break;
+ }
+ *dump_buf << std::endl;
+ }
+ for (size_t i = 0; i < power_history.size(); ++i) {
+ if (power_rail_pair.second.virtual_power_rail_info != nullptr) {
+ *dump_buf
+ << " Linked power rail "
+ << power_rail_pair.second.virtual_power_rail_info->linked_power_rails[i]
+ << std::endl;
+ *dump_buf << " Coefficient="
+ << power_rail_pair.second.virtual_power_rail_info->coefficients[i]
+ << std::endl;
+ *dump_buf << " Power Samples: ";
+ } else {
+ *dump_buf << " Power Samples: ";
+ }
+ while (power_history[i].size() > 0) {
+ const auto power_sample = power_history[i].front();
+ power_history[i].pop();
+ *dump_buf << "(T=" << power_sample.duration
+ << ", uWs=" << power_sample.energy_counter << ") ";
+ }
+ *dump_buf << std::endl;
+ }
+ }
+ }
+}
+
+Return<void> Thermal::debug(const hidl_handle &handle, const hidl_vec<hidl_string> &) {
+ if (handle != nullptr && handle->numFds >= 1) {
+ int fd = handle->data[0];
+ std::ostringstream dump_buf;
+
+ if (!thermal_helper_.isInitializedOk()) {
+ dump_buf << "ThermalHAL not initialized properly." << std::endl;
+ } else {
+ {
+ hidl_vec<CpuUsage> cpu_usages;
+ dump_buf << "getCpuUsages:" << std::endl;
+ if (!thermal_helper_.fillCpuUsages(&cpu_usages)) {
+ dump_buf << "Failed to get CPU usages." << std::endl;
+ }
+
+ for (const auto &usage : cpu_usages) {
+ dump_buf << " Name: " << usage.name << " Active: " << usage.active
+ << " Total: " << usage.total << " IsOnline: " << usage.isOnline
+ << std::endl;
+ }
+ }
+ {
+ dump_buf << "getCachedTemperatures:" << std::endl;
+ boot_clock::time_point now = boot_clock::now();
+ const auto &sensor_status_map = thermal_helper_.GetSensorStatusMap();
+ for (const auto &sensor_status_pair : sensor_status_map) {
+ if ((sensor_status_pair.second.thermal_cached.timestamp) ==
+ boot_clock::time_point::min()) {
+ continue;
+ }
+ dump_buf << " Name: " << sensor_status_pair.first
+ << " CachedValue: " << sensor_status_pair.second.thermal_cached.temp
+ << " TimeToCache: "
+ << std::chrono::duration_cast<std::chrono::milliseconds>(
+ now - sensor_status_pair.second.thermal_cached.timestamp)
+ .count()
+ << "ms" << std::endl;
+ }
+ }
+ {
+ const auto &map = thermal_helper_.GetSensorInfoMap();
+ dump_buf << "getTemperatures:" << std::endl;
+ Temperature_1_0 temp_1_0;
+ for (const auto &name_info_pair : map) {
+ thermal_helper_.readTemperature(name_info_pair.first, &temp_1_0);
+ dump_buf << " Type: "
+ << android::hardware::thermal::V1_0::toString(temp_1_0.type)
+ << " Name: " << name_info_pair.first
+ << " CurrentValue: " << temp_1_0.currentValue
+ << " ThrottlingThreshold: " << temp_1_0.throttlingThreshold
+ << " ShutdownThreshold: " << temp_1_0.shutdownThreshold
+ << " VrThrottlingThreshold: " << temp_1_0.vrThrottlingThreshold
+ << std::endl;
+ }
+ dump_buf << "getCurrentTemperatures:" << std::endl;
+ Temperature_2_0 temp_2_0;
+ for (const auto &name_info_pair : map) {
+ thermal_helper_.readTemperature(name_info_pair.first, &temp_2_0, nullptr, true);
+ dump_buf << " Type: "
+ << android::hardware::thermal::V2_0::toString(temp_2_0.type)
+ << " Name: " << name_info_pair.first
+ << " CurrentValue: " << temp_2_0.value << " ThrottlingStatus: "
+ << android::hardware::thermal::V2_0::toString(
+ temp_2_0.throttlingStatus)
+ << std::endl;
+ }
+ dump_buf << "getTemperatureThresholds:" << std::endl;
+ for (const auto &name_info_pair : map) {
+ if (!name_info_pair.second.is_watch) {
+ continue;
+ }
+ dump_buf << " Type: "
+ << android::hardware::thermal::V2_0::toString(
+ name_info_pair.second.type)
+ << " Name: " << name_info_pair.first;
+ dump_buf << " hotThrottlingThreshold: [";
+ for (size_t i = 0; i < kThrottlingSeverityCount; ++i) {
+ dump_buf << name_info_pair.second.hot_thresholds[i] << " ";
+ }
+ dump_buf << "] coldThrottlingThreshold: [";
+ for (size_t i = 0; i < kThrottlingSeverityCount; ++i) {
+ dump_buf << name_info_pair.second.cold_thresholds[i] << " ";
+ }
+ dump_buf << "] vrThrottlingThreshold: " << name_info_pair.second.vr_threshold;
+ dump_buf << std::endl;
+ }
+ dump_buf << "getHysteresis:" << std::endl;
+ for (const auto &name_info_pair : map) {
+ if (!name_info_pair.second.is_watch) {
+ continue;
+ }
+ dump_buf << " Name: " << name_info_pair.first;
+ dump_buf << " hotHysteresis: [";
+ for (size_t i = 0; i < kThrottlingSeverityCount; ++i) {
+ dump_buf << name_info_pair.second.hot_hysteresis[i] << " ";
+ }
+ dump_buf << "] coldHysteresis: [";
+ for (size_t i = 0; i < kThrottlingSeverityCount; ++i) {
+ dump_buf << name_info_pair.second.cold_hysteresis[i] << " ";
+ }
+ dump_buf << "]" << std::endl;
+ }
+ }
+ {
+ dump_buf << "getCurrentCoolingDevices:" << std::endl;
+ hidl_vec<CoolingDevice_2_0> cooling_devices;
+ if (!thermal_helper_.fillCurrentCoolingDevices(false, CoolingType::CPU,
+ &cooling_devices)) {
+ dump_buf << "Failed to getCurrentCoolingDevices." << std::endl;
+ }
+
+ for (const auto &c : cooling_devices) {
+ dump_buf << " Type: " << android::hardware::thermal::V2_0::toString(c.type)
+ << " Name: " << c.name << " CurrentValue: " << c.value << std::endl;
+ }
+ }
+ {
+ dump_buf << "Callbacks: Total " << callbacks_.size() << std::endl;
+ for (const auto &c : callbacks_) {
+ dump_buf << " IsFilter: " << c.is_filter_type
+ << " Type: " << android::hardware::thermal::V2_0::toString(c.type)
+ << std::endl;
+ }
+ }
+ {
+ dump_buf << "SendCallback" << std::endl;
+ dump_buf << " Enabled List: ";
+ const auto &map = thermal_helper_.GetSensorInfoMap();
+ for (const auto &name_info_pair : map) {
+ if (name_info_pair.second.send_cb) {
+ dump_buf << name_info_pair.first << " ";
+ }
+ }
+ dump_buf << std::endl;
+ }
+ {
+ dump_buf << "SendPowerHint" << std::endl;
+ dump_buf << " Enabled List: ";
+ const auto &map = thermal_helper_.GetSensorInfoMap();
+ for (const auto &name_info_pair : map) {
+ if (name_info_pair.second.send_powerhint) {
+ dump_buf << name_info_pair.first << " ";
+ }
+ }
+ dump_buf << std::endl;
+ }
+ dumpVirtualSensorInfo(&dump_buf);
+ dumpThrottlingInfo(&dump_buf);
+ dumpThrottlingRequestStatus(&dump_buf);
+ dumpPowerRailInfo(&dump_buf);
+ {
+ dump_buf << "AIDL Power Hal exist: " << std::boolalpha
+ << thermal_helper_.isAidlPowerHalExist() << std::endl;
+ dump_buf << "AIDL Power Hal connected: " << std::boolalpha
+ << thermal_helper_.isPowerHalConnected() << std::endl;
+ dump_buf << "AIDL Power Hal Ext connected: " << std::boolalpha
+ << thermal_helper_.isPowerHalExtConnected() << std::endl;
+ }
+ }
+ std::string buf = dump_buf.str();
+ if (!android::base::WriteStringToFd(buf, fd)) {
+ PLOG(ERROR) << "Failed to dump state to fd";
+ }
+ fsync(fd);
+ }
+ return Void();
+}
+
+} // namespace implementation
+} // namespace V2_0
+} // namespace thermal
+} // namespace hardware
+} // namespace android
diff --git a/thermal/pid_1_0/Thermal.h b/thermal/pid_1_0/Thermal.h
new file mode 100644
index 0000000..66a4dd6
--- /dev/null
+++ b/thermal/pid_1_0/Thermal.h
@@ -0,0 +1,97 @@
+/*
+ * 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 <android/hardware/thermal/2.0/IThermal.h>
+#include <android/hardware/thermal/2.0/IThermalChangedCallback.h>
+#include <hidl/Status.h>
+
+#include <mutex>
+#include <thread>
+
+#include "pid_1_0/thermal-helper.h"
+
+namespace android {
+namespace hardware {
+namespace thermal {
+namespace V2_0 {
+namespace implementation {
+
+using ::android::sp;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+using ::android::hardware::thermal::V2_0::IThermal;
+using ::android::hardware::thermal::V2_0::IThermalChangedCallback;
+
+struct CallbackSetting {
+ CallbackSetting(sp<IThermalChangedCallback> callback, bool is_filter_type,
+ TemperatureType_2_0 type)
+ : callback(std::move(callback)), is_filter_type(is_filter_type), type(type) {}
+ sp<IThermalChangedCallback> callback;
+ bool is_filter_type;
+ TemperatureType_2_0 type;
+};
+
+class Thermal : public IThermal {
+ public:
+ Thermal();
+ ~Thermal() = default;
+
+ // Disallow copy and assign.
+ Thermal(const Thermal &) = delete;
+ void operator=(const Thermal &) = delete;
+
+ // Methods from ::android::hardware::thermal::V1_0::IThermal.
+ Return<void> getTemperatures(getTemperatures_cb _hidl_cb) override;
+ Return<void> getCpuUsages(getCpuUsages_cb _hidl_cb) override;
+ Return<void> getCoolingDevices(getCoolingDevices_cb _hidl_cb) override;
+
+ // Methods from ::android::hardware::thermal::V2_0::IThermal follow.
+ Return<void> getCurrentTemperatures(bool filterType, TemperatureType_2_0 type,
+ getCurrentTemperatures_cb _hidl_cb) override;
+ Return<void> getTemperatureThresholds(bool filterType, TemperatureType_2_0 type,
+ getTemperatureThresholds_cb _hidl_cb) override;
+ Return<void> registerThermalChangedCallback(
+ const sp<IThermalChangedCallback> &callback, bool filterType, TemperatureType_2_0 type,
+ registerThermalChangedCallback_cb _hidl_cb) override;
+ Return<void> unregisterThermalChangedCallback(
+ const sp<IThermalChangedCallback> &callback,
+ unregisterThermalChangedCallback_cb _hidl_cb) override;
+ Return<void> getCurrentCoolingDevices(bool filterType, CoolingType type,
+ getCurrentCoolingDevices_cb _hidl_cb) override;
+
+ // Methods from ::android::hidl::base::V1_0::IBase follow.
+ Return<void> debug(const hidl_handle &fd, const hidl_vec<hidl_string> &args) override;
+
+ // Helper function for calling callbacks
+ void sendThermalChangedCallback(const Temperature_2_0 &t);
+
+ private:
+ ThermalHelper thermal_helper_;
+ void dumpVirtualSensorInfo(std::ostringstream *dump_buf);
+ void dumpThrottlingInfo(std::ostringstream *dump_buf);
+ void dumpThrottlingRequestStatus(std::ostringstream *dump_buf);
+ void dumpPowerRailInfo(std::ostringstream *dump_buf);
+ std::mutex thermal_callback_mutex_;
+ std::vector<CallbackSetting> callbacks_;
+};
+
+} // namespace implementation
+} // namespace V2_0
+} // namespace thermal
+} // namespace hardware
+} // namespace android
diff --git a/thermal/pid_1_0/service.cpp b/thermal/pid_1_0/service.cpp
new file mode 100644
index 0000000..578b23d
--- /dev/null
+++ b/thermal/pid_1_0/service.cpp
@@ -0,0 +1,66 @@
+/*
+ * 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.
+ */
+#include <android-base/logging.h>
+#include <hidl/HidlTransportSupport.h>
+
+#include "Thermal.h"
+
+constexpr std::string_view kThermalLogTag("pixel-thermal");
+
+using ::android::OK;
+using ::android::status_t;
+
+// libhwbinder:
+using ::android::hardware::configureRpcThreadpool;
+using ::android::hardware::joinRpcThreadpool;
+
+// Generated HIDL files:
+using ::android::hardware::thermal::V2_0::IThermal;
+using ::android::hardware::thermal::V2_0::implementation::Thermal;
+
+static int shutdown() {
+ LOG(ERROR) << "Pixel Thermal HAL Service is shutting down.";
+ return 1;
+}
+
+int main(int /* argc */, char ** /* argv */) {
+ android::base::SetDefaultTag(kThermalLogTag.data());
+ status_t status;
+ android::sp<IThermal> service = nullptr;
+
+ LOG(INFO) << "Pixel Thermal HAL Service 2.0 starting...";
+
+ service = new Thermal();
+ if (service == nullptr) {
+ LOG(ERROR) << "Error creating an instance of Thermal HAL. Exiting...";
+ return shutdown();
+ }
+
+ configureRpcThreadpool(1, true /* callerWillJoin */);
+
+ android::hardware::setMinSchedulerPolicy(service, SCHED_NORMAL, -20);
+
+ status = service->registerAsService();
+ if (status != OK) {
+ LOG(ERROR) << "Could not register service for ThermalHAL (" << status << ")";
+ return shutdown();
+ }
+
+ LOG(INFO) << "Pixel Thermal HAL Service 2.0 started successfully.";
+ joinRpcThreadpool();
+ // We should not get past the joinRpcThreadpool().
+ return shutdown();
+}
diff --git a/thermal/pid_1_0/thermal-helper.cpp b/thermal/pid_1_0/thermal-helper.cpp
new file mode 100644
index 0000000..14aee56
--- /dev/null
+++ b/thermal/pid_1_0/thermal-helper.cpp
@@ -0,0 +1,1077 @@
+/*
+ * 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 ATRACE_TAG (ATRACE_TAG_THERMAL | ATRACE_TAG_HAL)
+
+#include "thermal-helper.h"
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <utils/Trace.h>
+
+#include <iterator>
+#include <set>
+#include <sstream>
+#include <thread>
+#include <vector>
+
+namespace android {
+namespace hardware {
+namespace thermal {
+namespace V2_0 {
+namespace implementation {
+
+constexpr std::string_view kCpuOnlineRoot("/sys/devices/system/cpu");
+constexpr std::string_view kThermalSensorsRoot("/sys/devices/virtual/thermal");
+constexpr std::string_view kCpuUsageFile("/proc/stat");
+constexpr std::string_view kCpuOnlineFileSuffix("online");
+constexpr std::string_view kCpuPresentFile("/sys/devices/system/cpu/present");
+constexpr std::string_view kSensorPrefix("thermal_zone");
+constexpr std::string_view kCoolingDevicePrefix("cooling_device");
+constexpr std::string_view kThermalNameFile("type");
+constexpr std::string_view kSensorPolicyFile("policy");
+constexpr std::string_view kSensorTempSuffix("temp");
+constexpr std::string_view kSensorTripPointTempZeroFile("trip_point_0_temp");
+constexpr std::string_view kSensorTripPointHystZeroFile("trip_point_0_hyst");
+constexpr std::string_view kUserSpaceSuffix("user_space");
+constexpr std::string_view kCoolingDeviceCurStateSuffix("cur_state");
+constexpr std::string_view kCoolingDeviceMaxStateSuffix("max_state");
+constexpr std::string_view kCoolingDeviceState2powerSuffix("state2power_table");
+constexpr std::string_view kConfigProperty("vendor.thermal.config");
+constexpr std::string_view kConfigDefaultFileName("thermal_info_config.json");
+constexpr std::string_view kThermalGenlProperty("persist.vendor.enable.thermal.genl");
+constexpr std::string_view kThermalDisabledProperty("vendor.disable.thermal.control");
+
+namespace {
+using android::base::StringPrintf;
+
+/*
+ * Pixel don't offline CPU, so std::thread::hardware_concurrency(); should work.
+ * However /sys/devices/system/cpu/present is preferred.
+ * The file is expected to contain single text line with two numbers %d-%d,
+ * which is a range of available cpu numbers, e.g. 0-7 would mean there
+ * are 8 cores number from 0 to 7.
+ * For Android systems this approach is safer than using cpufeatures, see bug
+ * b/36941727.
+ */
+static int getNumberOfCores() {
+ std::string file;
+ if (!android::base::ReadFileToString(kCpuPresentFile.data(), &file)) {
+ LOG(ERROR) << "Error reading CPU present file: " << kCpuPresentFile;
+ return 0;
+ }
+ std::vector<std::string> pieces = android::base::Split(file, "-");
+ if (pieces.size() != 2) {
+ LOG(ERROR) << "Error parsing CPU present file content: " << file;
+ return 0;
+ }
+ auto min_core = std::stoul(pieces[0]);
+ auto max_core = std::stoul(pieces[1]);
+ if (max_core < min_core) {
+ LOG(ERROR) << "Error parsing CPU present min and max: " << min_core << " - " << max_core;
+ return 0;
+ }
+ return static_cast<std::size_t>(max_core - min_core + 1);
+}
+const int kMaxCpus = getNumberOfCores();
+
+void parseCpuUsagesFileAndAssignUsages(hidl_vec<CpuUsage> *cpu_usages) {
+ std::string data;
+ if (!android::base::ReadFileToString(kCpuUsageFile.data(), &data)) {
+ LOG(ERROR) << "Error reading CPU usage file: " << kCpuUsageFile;
+ return;
+ }
+
+ std::istringstream stat_data(data);
+ std::string line;
+ while (std::getline(stat_data, line)) {
+ if (!line.find("cpu") && isdigit(line[3])) {
+ // Split the string using spaces.
+ std::vector<std::string> words = android::base::Split(line, " ");
+ std::string cpu_name = words[0];
+ int cpu_num = std::stoi(cpu_name.substr(3));
+
+ if (cpu_num < kMaxCpus) {
+ uint64_t user = std::stoull(words[1]);
+ uint64_t nice = std::stoull(words[2]);
+ uint64_t system = std::stoull(words[3]);
+ uint64_t idle = std::stoull(words[4]);
+
+ // Check if the CPU is online by reading the online file.
+ std::string cpu_online_path =
+ StringPrintf("%s/%s/%s", kCpuOnlineRoot.data(), cpu_name.c_str(),
+ kCpuOnlineFileSuffix.data());
+ std::string is_online;
+ if (!android::base::ReadFileToString(cpu_online_path, &is_online)) {
+ LOG(ERROR) << "Could not open CPU online file: " << cpu_online_path;
+ if (cpu_num != 0) {
+ return;
+ }
+ // Some architecture cannot offline cpu0, so assuming it is online
+ is_online = "1";
+ }
+ is_online = android::base::Trim(is_online);
+
+ (*cpu_usages)[cpu_num].active = user + nice + system;
+ (*cpu_usages)[cpu_num].total = user + nice + system + idle;
+ (*cpu_usages)[cpu_num].isOnline = (is_online == "1") ? true : false;
+ } else {
+ LOG(ERROR) << "Unexpected CPU number: " << words[0];
+ return;
+ }
+ }
+ }
+}
+
+std::unordered_map<std::string, std::string> parseThermalPathMap(std::string_view prefix) {
+ std::unordered_map<std::string, std::string> path_map;
+ std::unique_ptr<DIR, int (*)(DIR *)> dir(opendir(kThermalSensorsRoot.data()), closedir);
+ if (!dir) {
+ return path_map;
+ }
+
+ // std::filesystem is not available for vendor yet
+ // see discussion: aosp/894015
+ while (struct dirent *dp = readdir(dir.get())) {
+ if (dp->d_type != DT_DIR) {
+ continue;
+ }
+
+ if (!android::base::StartsWith(dp->d_name, prefix.data())) {
+ continue;
+ }
+
+ std::string path = android::base::StringPrintf("%s/%s/%s", kThermalSensorsRoot.data(),
+ dp->d_name, kThermalNameFile.data());
+ std::string name;
+ if (!android::base::ReadFileToString(path, &name)) {
+ PLOG(ERROR) << "Failed to read from " << path;
+ continue;
+ }
+
+ path_map.emplace(
+ android::base::Trim(name),
+ android::base::StringPrintf("%s/%s", kThermalSensorsRoot.data(), dp->d_name));
+ }
+
+ return path_map;
+}
+
+} // namespace
+
+/*
+ * Populate the sensor_name_to_file_map_ map by walking through the file tree,
+ * reading the type file and assigning the temp file path to the map. If we do
+ * not succeed, abort.
+ */
+ThermalHelper::ThermalHelper(const NotificationCallback &cb)
+ : thermal_watcher_(new ThermalWatcher(
+ std::bind(&ThermalHelper::thermalWatcherCallbackFunc, this, std::placeholders::_1))),
+ cb_(cb) {
+ const std::string config_path =
+ "/vendor/etc/" +
+ android::base::GetProperty(kConfigProperty.data(), kConfigDefaultFileName.data());
+ bool thermal_throttling_disabled =
+ android::base::GetBoolProperty(kThermalDisabledProperty.data(), false);
+
+ is_initialized_ = ParseCoolingDevice(config_path, &cooling_device_info_map_) &&
+ ParseSensorInfo(config_path, &sensor_info_map_);
+
+ if (thermal_throttling_disabled) {
+ return;
+ }
+
+ if (!is_initialized_) {
+ LOG(FATAL) << "Failed to parse thermal configs";
+ }
+
+ auto tz_map = parseThermalPathMap(kSensorPrefix.data());
+ auto cdev_map = parseThermalPathMap(kCoolingDevicePrefix.data());
+
+ is_initialized_ = initializeSensorMap(tz_map) && initializeCoolingDevices(cdev_map);
+
+ if (!is_initialized_) {
+ LOG(FATAL) << "ThermalHAL could not be initialized properly.";
+ }
+
+ if (!power_files_.registerPowerRailsToWatch(config_path)) {
+ LOG(FATAL) << "Failed to register power rails";
+ }
+
+ for (auto const &name_status_pair : sensor_info_map_) {
+ sensor_status_map_[name_status_pair.first] = {
+ .severity = ThrottlingSeverity::NONE,
+ .prev_hot_severity = ThrottlingSeverity::NONE,
+ .prev_cold_severity = ThrottlingSeverity::NONE,
+ .prev_hint_severity = ThrottlingSeverity::NONE,
+ .last_update_time = boot_clock::time_point::min(),
+ .thermal_cached = {NAN, boot_clock::time_point::min()},
+ };
+
+ if (name_status_pair.second.throttling_info != nullptr) {
+ if (!thermal_throttling_.registerThermalThrottling(
+ name_status_pair.first, name_status_pair.second.throttling_info,
+ cooling_device_info_map_)) {
+ LOG(FATAL) << name_status_pair.first << " failed to register thermal throttling";
+ }
+ }
+
+ // Update cooling device max state
+ for (auto &binded_cdev_pair :
+ name_status_pair.second.throttling_info->binded_cdev_info_map) {
+ const auto &cdev_info = cooling_device_info_map_.at(binded_cdev_pair.first);
+
+ for (auto &cdev_ceiling : binded_cdev_pair.second.cdev_ceiling) {
+ if (cdev_ceiling > cdev_info.max_state) {
+ if (cdev_ceiling != std::numeric_limits<int>::max()) {
+ LOG(ERROR) << "Sensor " << name_status_pair.first << "'s "
+ << binded_cdev_pair.first << " cdev_ceiling:" << cdev_ceiling
+ << " is higher than max state:" << cdev_info.max_state;
+ }
+ cdev_ceiling = cdev_info.max_state;
+ }
+ }
+ }
+
+ if (name_status_pair.second.virtual_sensor_info != nullptr &&
+ !name_status_pair.second.virtual_sensor_info->trigger_sensor.empty() &&
+ name_status_pair.second.is_watch) {
+ if (sensor_info_map_.count(
+ name_status_pair.second.virtual_sensor_info->trigger_sensor)) {
+ sensor_info_map_[name_status_pair.second.virtual_sensor_info->trigger_sensor]
+ .is_watch = true;
+ } else {
+ LOG(FATAL) << name_status_pair.first << "'s trigger sensor: "
+ << name_status_pair.second.virtual_sensor_info->trigger_sensor
+ << " is invalid";
+ }
+ }
+ }
+
+ const bool thermal_genl_enabled =
+ android::base::GetBoolProperty(kThermalGenlProperty.data(), false);
+
+ std::set<std::string> monitored_sensors;
+ initializeTrip(tz_map, &monitored_sensors, thermal_genl_enabled);
+
+ if (thermal_genl_enabled) {
+ thermal_watcher_->registerFilesToWatchNl(monitored_sensors);
+ } else {
+ thermal_watcher_->registerFilesToWatch(monitored_sensors);
+ }
+
+ // Need start watching after status map initialized
+ is_initialized_ = thermal_watcher_->startWatchingDeviceFiles();
+ if (!is_initialized_) {
+ LOG(FATAL) << "ThermalHAL could not start watching thread properly.";
+ }
+
+ if (!connectToPowerHal()) {
+ LOG(ERROR) << "Fail to connect to Power Hal";
+ } else {
+ updateSupportedPowerHints();
+ }
+}
+
+bool getThermalZoneTypeById(int tz_id, std::string *type) {
+ std::string tz_type;
+ std::string path =
+ android::base::StringPrintf("%s/%s%d/%s", kThermalSensorsRoot.data(),
+ kSensorPrefix.data(), tz_id, kThermalNameFile.data());
+ LOG(INFO) << "TZ Path: " << path;
+ if (!::android::base::ReadFileToString(path, &tz_type)) {
+ LOG(ERROR) << "Failed to read sensor: " << tz_type;
+ return false;
+ }
+
+ // Strip the newline.
+ *type = ::android::base::Trim(tz_type);
+ LOG(INFO) << "TZ type: " << *type;
+ return true;
+}
+
+bool ThermalHelper::readCoolingDevice(std::string_view cooling_device,
+ CoolingDevice_2_0 *out) const {
+ // Read the file. If the file can't be read temp will be empty string.
+ std::string data;
+
+ if (!cooling_devices_.readThermalFile(cooling_device, &data)) {
+ LOG(ERROR) << "readCoolingDevice: failed to read cooling_device: " << cooling_device;
+ return false;
+ }
+
+ const CdevInfo &cdev_info = cooling_device_info_map_.at(cooling_device.data());
+ const CoolingType &type = cdev_info.type;
+
+ out->type = type;
+ out->name = cooling_device.data();
+ out->value = std::stoi(data);
+
+ return true;
+}
+
+bool ThermalHelper::readTemperature(std::string_view sensor_name, Temperature_1_0 *out) {
+ // Return fail if the thermal sensor cannot be read.
+ float temp;
+ if (!readThermalSensor(sensor_name, &temp, false)) {
+ LOG(ERROR) << "readTemperature: failed to read sensor: " << sensor_name;
+ return false;
+ }
+
+ const SensorInfo &sensor_info = sensor_info_map_.at(sensor_name.data());
+ TemperatureType_1_0 type =
+ (static_cast<int>(sensor_info.type) > static_cast<int>(TemperatureType_1_0::SKIN))
+ ? TemperatureType_1_0::UNKNOWN
+ : static_cast<TemperatureType_1_0>(sensor_info.type);
+ out->type = type;
+ out->name = sensor_name.data();
+ out->currentValue = temp * sensor_info.multiplier;
+ out->throttlingThreshold =
+ sensor_info.hot_thresholds[static_cast<size_t>(ThrottlingSeverity::SEVERE)];
+ out->shutdownThreshold =
+ sensor_info.hot_thresholds[static_cast<size_t>(ThrottlingSeverity::SHUTDOWN)];
+ out->vrThrottlingThreshold = sensor_info.vr_threshold;
+
+ return true;
+}
+
+bool ThermalHelper::readTemperature(
+ std::string_view sensor_name, Temperature_2_0 *out,
+ std::pair<ThrottlingSeverity, ThrottlingSeverity> *throtting_status,
+ const bool force_sysfs) {
+ // Return fail if the thermal sensor cannot be read.
+ float temp;
+
+ if (!readThermalSensor(sensor_name, &temp, force_sysfs)) {
+ LOG(ERROR) << "readTemperature: failed to read sensor: " << sensor_name;
+ return false;
+ }
+
+ const auto &sensor_info = sensor_info_map_.at(sensor_name.data());
+ out->type = sensor_info.type;
+ out->name = sensor_name.data();
+ out->value = temp * sensor_info.multiplier;
+
+ std::pair<ThrottlingSeverity, ThrottlingSeverity> status =
+ std::make_pair(ThrottlingSeverity::NONE, ThrottlingSeverity::NONE);
+ // Only update status if the thermal sensor is being monitored
+ if (sensor_info.is_watch) {
+ ThrottlingSeverity prev_hot_severity, prev_cold_severity;
+ {
+ // reader lock, readTemperature will be called in Binder call and the watcher thread.
+ std::shared_lock<std::shared_mutex> _lock(sensor_status_map_mutex_);
+ prev_hot_severity = sensor_status_map_.at(sensor_name.data()).prev_hot_severity;
+ prev_cold_severity = sensor_status_map_.at(sensor_name.data()).prev_cold_severity;
+ }
+ status = getSeverityFromThresholds(sensor_info.hot_thresholds, sensor_info.cold_thresholds,
+ sensor_info.hot_hysteresis, sensor_info.cold_hysteresis,
+ prev_hot_severity, prev_cold_severity, out->value);
+ }
+ if (throtting_status) {
+ *throtting_status = status;
+ }
+
+ out->throttlingStatus = static_cast<size_t>(status.first) > static_cast<size_t>(status.second)
+ ? status.first
+ : status.second;
+
+ return true;
+}
+
+bool ThermalHelper::readTemperatureThreshold(std::string_view sensor_name,
+ TemperatureThreshold *out) const {
+ // Read the file. If the file can't be read temp will be empty string.
+ std::string temp;
+ std::string path;
+
+ if (!sensor_info_map_.count(sensor_name.data())) {
+ LOG(ERROR) << __func__ << ": sensor not found: " << sensor_name;
+ return false;
+ }
+
+ const auto &sensor_info = sensor_info_map_.at(sensor_name.data());
+
+ out->type = sensor_info.type;
+ out->name = sensor_name.data();
+ out->hotThrottlingThresholds = sensor_info.hot_thresholds;
+ out->coldThrottlingThresholds = sensor_info.cold_thresholds;
+ out->vrThrottlingThreshold = sensor_info.vr_threshold;
+ return true;
+}
+
+void ThermalHelper::updateCoolingDevices(const std::vector<std::string> &updated_cdev) {
+ int max_state;
+
+ const auto &thermal_throttling_status_map = thermal_throttling_.GetThermalThrottlingStatusMap();
+
+ for (const auto &target_cdev : updated_cdev) {
+ max_state = 0;
+ for (const auto &thermal_throttling_status_pair : thermal_throttling_status_map) {
+ if (!thermal_throttling_status_pair.second.cdev_status_map.count(target_cdev)) {
+ continue;
+ }
+ const auto state =
+ thermal_throttling_status_pair.second.cdev_status_map.at(target_cdev);
+ if (state > max_state) {
+ max_state = state;
+ }
+ }
+ if (cooling_devices_.writeCdevFile(target_cdev, std::to_string(max_state))) {
+ LOG(INFO) << "Successfully update cdev " << target_cdev << " sysfs to " << max_state;
+ }
+ }
+}
+
+std::pair<ThrottlingSeverity, ThrottlingSeverity> ThermalHelper::getSeverityFromThresholds(
+ const ThrottlingArray &hot_thresholds, const ThrottlingArray &cold_thresholds,
+ const ThrottlingArray &hot_hysteresis, const ThrottlingArray &cold_hysteresis,
+ ThrottlingSeverity prev_hot_severity, ThrottlingSeverity prev_cold_severity,
+ float value) const {
+ ThrottlingSeverity ret_hot = ThrottlingSeverity::NONE;
+ ThrottlingSeverity ret_hot_hysteresis = ThrottlingSeverity::NONE;
+ ThrottlingSeverity ret_cold = ThrottlingSeverity::NONE;
+ ThrottlingSeverity ret_cold_hysteresis = ThrottlingSeverity::NONE;
+
+ // Here we want to control the iteration from high to low, and hidl_enum_range doesn't support
+ // a reverse iterator yet.
+ for (size_t i = static_cast<size_t>(ThrottlingSeverity::SHUTDOWN);
+ i > static_cast<size_t>(ThrottlingSeverity::NONE); --i) {
+ if (!std::isnan(hot_thresholds[i]) && hot_thresholds[i] <= value &&
+ ret_hot == ThrottlingSeverity::NONE) {
+ ret_hot = static_cast<ThrottlingSeverity>(i);
+ }
+ if (!std::isnan(hot_thresholds[i]) && (hot_thresholds[i] - hot_hysteresis[i]) < value &&
+ ret_hot_hysteresis == ThrottlingSeverity::NONE) {
+ ret_hot_hysteresis = static_cast<ThrottlingSeverity>(i);
+ }
+ if (!std::isnan(cold_thresholds[i]) && cold_thresholds[i] >= value &&
+ ret_cold == ThrottlingSeverity::NONE) {
+ ret_cold = static_cast<ThrottlingSeverity>(i);
+ }
+ if (!std::isnan(cold_thresholds[i]) && (cold_thresholds[i] + cold_hysteresis[i]) > value &&
+ ret_cold_hysteresis == ThrottlingSeverity::NONE) {
+ ret_cold_hysteresis = static_cast<ThrottlingSeverity>(i);
+ }
+ }
+ if (static_cast<size_t>(ret_hot) < static_cast<size_t>(prev_hot_severity)) {
+ ret_hot = ret_hot_hysteresis;
+ }
+ if (static_cast<size_t>(ret_cold) < static_cast<size_t>(prev_cold_severity)) {
+ ret_cold = ret_cold_hysteresis;
+ }
+
+ return std::make_pair(ret_hot, ret_cold);
+}
+
+bool ThermalHelper::initializeSensorMap(
+ const std::unordered_map<std::string, std::string> &path_map) {
+ for (const auto &sensor_info_pair : sensor_info_map_) {
+ std::string_view sensor_name = sensor_info_pair.first;
+ if (sensor_info_pair.second.virtual_sensor_info != nullptr) {
+ continue;
+ }
+ if (!path_map.count(sensor_name.data())) {
+ LOG(ERROR) << "Could not find " << sensor_name << " in sysfs";
+ return false;
+ }
+
+ std::string path;
+ if (sensor_info_pair.second.temp_path.empty()) {
+ path = android::base::StringPrintf("%s/%s", path_map.at(sensor_name.data()).c_str(),
+ kSensorTempSuffix.data());
+ } else {
+ path = sensor_info_pair.second.temp_path;
+ }
+
+ if (!thermal_sensors_.addThermalFile(sensor_name, path)) {
+ LOG(ERROR) << "Could not add " << sensor_name << "to sensors map";
+ return false;
+ }
+ }
+ return true;
+}
+
+bool ThermalHelper::initializeCoolingDevices(
+ const std::unordered_map<std::string, std::string> &path_map) {
+ for (auto &cooling_device_info_pair : cooling_device_info_map_) {
+ std::string cooling_device_name = cooling_device_info_pair.first;
+ if (!path_map.count(cooling_device_name)) {
+ LOG(ERROR) << "Could not find " << cooling_device_name << " in sysfs";
+ return false;
+ }
+ // Add cooling device path for thermalHAL to get current state
+ std::string_view path = path_map.at(cooling_device_name);
+ std::string read_path;
+ if (!cooling_device_info_pair.second.read_path.empty()) {
+ read_path = cooling_device_info_pair.second.read_path.data();
+ } else {
+ read_path = android::base::StringPrintf("%s/%s", path.data(),
+ kCoolingDeviceCurStateSuffix.data());
+ }
+ if (!cooling_devices_.addThermalFile(cooling_device_name, read_path)) {
+ LOG(ERROR) << "Could not add " << cooling_device_name
+ << " read path to cooling device map";
+ return false;
+ }
+
+ std::string state2power_path = android::base::StringPrintf(
+ "%s/%s", path.data(), kCoolingDeviceState2powerSuffix.data());
+ std::string state2power_str;
+ if (android::base::ReadFileToString(state2power_path, &state2power_str)) {
+ LOG(INFO) << "Cooling device " << cooling_device_info_pair.first
+ << " use state2power read from sysfs";
+ cooling_device_info_pair.second.state2power.clear();
+
+ std::stringstream power(state2power_str);
+ unsigned int power_number;
+ int i = 0;
+ while (power >> power_number) {
+ cooling_device_info_pair.second.state2power.push_back(
+ static_cast<float>(power_number));
+ LOG(INFO) << "Cooling device " << cooling_device_info_pair.first << " state:" << i
+ << " power: " << power_number;
+ i++;
+ }
+ }
+
+ // Get max cooling device request state
+ std::string max_state;
+ std::string max_state_path = android::base::StringPrintf(
+ "%s/%s", path.data(), kCoolingDeviceMaxStateSuffix.data());
+ if (!android::base::ReadFileToString(max_state_path, &max_state)) {
+ LOG(ERROR) << cooling_device_info_pair.first
+ << " could not open max state file:" << max_state_path;
+ cooling_device_info_pair.second.max_state = std::numeric_limits<int>::max();
+ } else {
+ cooling_device_info_pair.second.max_state = std::stoi(android::base::Trim(max_state));
+ LOG(INFO) << "Cooling device " << cooling_device_info_pair.first
+ << " max state: " << cooling_device_info_pair.second.max_state
+ << " state2power number: "
+ << cooling_device_info_pair.second.state2power.size();
+ if (cooling_device_info_pair.second.state2power.size() > 0 &&
+ static_cast<int>(cooling_device_info_pair.second.state2power.size()) !=
+ (cooling_device_info_pair.second.max_state + 1)) {
+ LOG(ERROR) << "Invalid state2power number: "
+ << cooling_device_info_pair.second.state2power.size()
+ << ", number should be " << cooling_device_info_pair.second.max_state + 1
+ << " (max_state + 1)";
+ return false;
+ }
+ }
+
+ // Add cooling device path for thermalHAL to request state
+ cooling_device_name =
+ android::base::StringPrintf("%s_%s", cooling_device_name.c_str(), "w");
+ std::string write_path;
+ if (!cooling_device_info_pair.second.write_path.empty()) {
+ write_path = cooling_device_info_pair.second.write_path.data();
+ } else {
+ write_path = android::base::StringPrintf("%s/%s", path.data(),
+ kCoolingDeviceCurStateSuffix.data());
+ }
+
+ if (!cooling_devices_.addThermalFile(cooling_device_name, write_path)) {
+ LOG(ERROR) << "Could not add " << cooling_device_name
+ << " write path to cooling device map";
+ return false;
+ }
+ }
+ return true;
+}
+
+void ThermalHelper::setMinTimeout(SensorInfo *sensor_info) {
+ sensor_info->polling_delay = kMinPollIntervalMs;
+ sensor_info->passive_delay = kMinPollIntervalMs;
+}
+
+void ThermalHelper::initializeTrip(const std::unordered_map<std::string, std::string> &path_map,
+ std::set<std::string> *monitored_sensors,
+ bool thermal_genl_enabled) {
+ for (auto &sensor_info : sensor_info_map_) {
+ if (!sensor_info.second.is_watch || (sensor_info.second.virtual_sensor_info != nullptr)) {
+ continue;
+ }
+
+ bool trip_update = false;
+ std::string_view sensor_name = sensor_info.first;
+ std::string_view tz_path = path_map.at(sensor_name.data());
+ std::string tz_policy;
+ std::string path =
+ android::base::StringPrintf("%s/%s", (tz_path.data()), kSensorPolicyFile.data());
+
+ if (thermal_genl_enabled) {
+ trip_update = true;
+ } else {
+ // Check if thermal zone support uevent notify
+ if (!android::base::ReadFileToString(path, &tz_policy)) {
+ LOG(ERROR) << sensor_name << " could not open tz policy file:" << path;
+ } else {
+ tz_policy = android::base::Trim(tz_policy);
+ if (tz_policy != kUserSpaceSuffix) {
+ LOG(ERROR) << sensor_name << " does not support uevent notify";
+ } else {
+ trip_update = true;
+ }
+ }
+ }
+ if (trip_update) {
+ // Update thermal zone trip point
+ for (size_t i = 0; i < kThrottlingSeverityCount; ++i) {
+ if (!std::isnan(sensor_info.second.hot_thresholds[i]) &&
+ !std::isnan(sensor_info.second.hot_hysteresis[i])) {
+ // Update trip_point_0_temp threshold
+ std::string threshold = std::to_string(static_cast<int>(
+ sensor_info.second.hot_thresholds[i] / sensor_info.second.multiplier));
+ path = android::base::StringPrintf("%s/%s", (tz_path.data()),
+ kSensorTripPointTempZeroFile.data());
+ if (!android::base::WriteStringToFile(threshold, path)) {
+ LOG(ERROR) << "fail to update " << sensor_name << " trip point: " << path
+ << " to " << threshold;
+ trip_update = false;
+ break;
+ }
+ // Update trip_point_0_hyst threshold
+ threshold = std::to_string(static_cast<int>(
+ sensor_info.second.hot_hysteresis[i] / sensor_info.second.multiplier));
+ path = android::base::StringPrintf("%s/%s", (tz_path.data()),
+ kSensorTripPointHystZeroFile.data());
+ if (!android::base::WriteStringToFile(threshold, path)) {
+ LOG(ERROR) << "fail to update " << sensor_name << "trip hyst" << threshold
+ << path;
+ trip_update = false;
+ break;
+ }
+ break;
+ } else if (i == kThrottlingSeverityCount - 1) {
+ LOG(ERROR) << sensor_name << ":all thresholds are NAN";
+ trip_update = false;
+ break;
+ }
+ }
+ monitored_sensors->insert(sensor_info.first);
+ }
+
+ if (!trip_update) {
+ LOG(INFO) << "config Sensor: " << sensor_info.first
+ << " to default polling interval: " << kMinPollIntervalMs.count();
+ setMinTimeout(&sensor_info.second);
+ }
+ }
+}
+
+bool ThermalHelper::fillTemperatures(hidl_vec<Temperature_1_0> *temperatures) {
+ std::vector<Temperature_1_0> ret;
+ for (const auto &name_info_pair : sensor_info_map_) {
+ Temperature_1_0 temp;
+
+ if (name_info_pair.second.is_hidden) {
+ continue;
+ }
+
+ if (readTemperature(name_info_pair.first, &temp)) {
+ ret.emplace_back(std::move(temp));
+ } else {
+ LOG(ERROR) << __func__
+ << ": error reading temperature for sensor: " << name_info_pair.first;
+ return false;
+ }
+ }
+ *temperatures = ret;
+ return ret.size() > 0;
+}
+
+bool ThermalHelper::fillCurrentTemperatures(bool filterType, bool filterCallback,
+ TemperatureType_2_0 type,
+ hidl_vec<Temperature_2_0> *temperatures) {
+ std::vector<Temperature_2_0> ret;
+ for (const auto &name_info_pair : sensor_info_map_) {
+ Temperature_2_0 temp;
+ if (name_info_pair.second.is_hidden) {
+ continue;
+ }
+ if (filterType && name_info_pair.second.type != type) {
+ continue;
+ }
+ if (filterCallback && !name_info_pair.second.send_cb) {
+ continue;
+ }
+ if (readTemperature(name_info_pair.first, &temp, nullptr, false)) {
+ ret.emplace_back(std::move(temp));
+ } else {
+ LOG(ERROR) << __func__
+ << ": error reading temperature for sensor: " << name_info_pair.first;
+ }
+ }
+ *temperatures = ret;
+ return ret.size() > 0;
+}
+
+bool ThermalHelper::fillTemperatureThresholds(bool filterType, TemperatureType_2_0 type,
+ hidl_vec<TemperatureThreshold> *thresholds) const {
+ std::vector<TemperatureThreshold> ret;
+ for (const auto &name_info_pair : sensor_info_map_) {
+ TemperatureThreshold temp;
+ if (name_info_pair.second.is_hidden) {
+ continue;
+ }
+ if (filterType && name_info_pair.second.type != type) {
+ continue;
+ }
+ if (readTemperatureThreshold(name_info_pair.first, &temp)) {
+ ret.emplace_back(std::move(temp));
+ } else {
+ LOG(ERROR) << __func__ << ": error reading temperature threshold for sensor: "
+ << name_info_pair.first;
+ return false;
+ }
+ }
+ *thresholds = ret;
+ return ret.size() > 0;
+}
+
+bool ThermalHelper::fillCurrentCoolingDevices(bool filterType, CoolingType type,
+ hidl_vec<CoolingDevice_2_0> *cooling_devices) const {
+ std::vector<CoolingDevice_2_0> ret;
+ for (const auto &name_info_pair : cooling_device_info_map_) {
+ CoolingDevice_2_0 value;
+ if (filterType && name_info_pair.second.type != type) {
+ continue;
+ }
+ if (readCoolingDevice(name_info_pair.first, &value)) {
+ ret.emplace_back(std::move(value));
+ } else {
+ LOG(ERROR) << __func__ << ": error reading cooling device: " << name_info_pair.first;
+ return false;
+ }
+ }
+ *cooling_devices = ret;
+ return ret.size() > 0;
+}
+
+bool ThermalHelper::fillCpuUsages(hidl_vec<CpuUsage> *cpu_usages) const {
+ cpu_usages->resize(kMaxCpus);
+ for (int i = 0; i < kMaxCpus; i++) {
+ (*cpu_usages)[i].name = StringPrintf("cpu%d", i);
+ (*cpu_usages)[i].active = 0;
+ (*cpu_usages)[i].total = 0;
+ (*cpu_usages)[i].isOnline = false;
+ }
+ parseCpuUsagesFileAndAssignUsages(cpu_usages);
+ return true;
+}
+
+bool ThermalHelper::readThermalSensor(std::string_view sensor_name, float *temp,
+ const bool force_sysfs) {
+ float temp_val = 0.0;
+ std::string file_reading;
+ std::string log_buf;
+ boot_clock::time_point now = boot_clock::now();
+
+ ATRACE_NAME(StringPrintf("ThermalHelper::readThermalSensor - %s", sensor_name.data()).c_str());
+ if (!(sensor_info_map_.count(sensor_name.data()) &&
+ sensor_status_map_.count(sensor_name.data()))) {
+ return false;
+ }
+
+ const auto &sensor_info = sensor_info_map_.at(sensor_name.data());
+ auto &sensor_status = sensor_status_map_.at(sensor_name.data());
+
+ // Check if thermal data need to be read from buffer
+ if (!force_sysfs && (sensor_status.thermal_cached.timestamp != boot_clock::time_point::min()) &&
+ (std::chrono::duration_cast<std::chrono::milliseconds>(
+ now - sensor_status.thermal_cached.timestamp) < sensor_info.time_resolution) &&
+ !isnan(sensor_status.thermal_cached.temp)) {
+ *temp = sensor_status.thermal_cached.temp;
+ LOG(VERBOSE) << "read " << sensor_name.data() << " from buffer, value:" << *temp;
+ return true;
+ }
+
+ // Reading thermal sensor according to it's composition
+ if (sensor_info.virtual_sensor_info == nullptr) {
+ if (!thermal_sensors_.readThermalFile(sensor_name.data(), &file_reading)) {
+ return false;
+ }
+
+ if (file_reading.empty()) {
+ LOG(ERROR) << "failed to read sensor: " << sensor_name;
+ return false;
+ }
+ *temp = std::stof(::android::base::Trim(file_reading));
+ } else {
+ for (size_t i = 0; i < sensor_info.virtual_sensor_info->linked_sensors.size(); i++) {
+ float sensor_reading = 0.0;
+ if (!readThermalSensor(sensor_info.virtual_sensor_info->linked_sensors[i],
+ &sensor_reading, force_sysfs)) {
+ return false;
+ }
+ log_buf.append(StringPrintf("(%s: %0.2f)",
+ sensor_info.virtual_sensor_info->linked_sensors[i].c_str(),
+ sensor_reading));
+ if (std::isnan(sensor_info.virtual_sensor_info->coefficients[i])) {
+ return false;
+ }
+
+ float coefficient = sensor_info.virtual_sensor_info->coefficients[i];
+ switch (sensor_info.virtual_sensor_info->formula) {
+ case FormulaOption::COUNT_THRESHOLD:
+ if ((coefficient < 0 && sensor_reading < -coefficient) ||
+ (coefficient >= 0 && sensor_reading >= coefficient))
+ temp_val += 1;
+ break;
+ case FormulaOption::WEIGHTED_AVG:
+ temp_val += sensor_reading * coefficient;
+ break;
+ case FormulaOption::MAXIMUM:
+ if (i == 0)
+ temp_val = std::numeric_limits<float>::lowest();
+ if (sensor_reading * coefficient > temp_val)
+ temp_val = sensor_reading * coefficient;
+ break;
+ case FormulaOption::MINIMUM:
+ if (i == 0)
+ temp_val = std::numeric_limits<float>::max();
+ if (sensor_reading * coefficient < temp_val)
+ temp_val = sensor_reading * coefficient;
+ break;
+ default:
+ break;
+ }
+ }
+ LOG(VERBOSE) << sensor_name.data() << "'s sub sensors:" << log_buf;
+ *temp = (temp_val + sensor_info.virtual_sensor_info->offset);
+ }
+
+ {
+ std::unique_lock<std::shared_mutex> _lock(sensor_status_map_mutex_);
+ sensor_status.thermal_cached.temp = *temp;
+ sensor_status.thermal_cached.timestamp = now;
+ }
+
+ return true;
+}
+
+// This is called in the different thread context and will update sensor_status
+// uevent_sensors is the set of sensors which trigger uevent from thermal core driver.
+std::chrono::milliseconds ThermalHelper::thermalWatcherCallbackFunc(
+ const std::set<std::string> &uevent_sensors) {
+ std::vector<Temperature_2_0> temps;
+ std::vector<std::string> cooling_devices_to_update;
+ boot_clock::time_point now = boot_clock::now();
+ auto min_sleep_ms = std::chrono::milliseconds::max();
+ bool power_data_is_updated = false;
+
+ ATRACE_CALL();
+ for (auto &name_status_pair : sensor_status_map_) {
+ bool force_update = false;
+ bool force_sysfs = false;
+ Temperature_2_0 temp;
+ TemperatureThreshold threshold;
+ SensorStatus &sensor_status = name_status_pair.second;
+ const SensorInfo &sensor_info = sensor_info_map_.at(name_status_pair.first);
+
+ // Only handle the sensors in allow list
+ if (!sensor_info.is_watch) {
+ continue;
+ }
+
+ ATRACE_NAME(StringPrintf("ThermalHelper::thermalWatcherCallbackFunc - %s",
+ name_status_pair.first.data())
+ .c_str());
+
+ std::chrono::milliseconds time_elapsed_ms = std::chrono::milliseconds::zero();
+ auto sleep_ms = (sensor_status.severity != ThrottlingSeverity::NONE)
+ ? sensor_info.passive_delay
+ : sensor_info.polling_delay;
+
+ if (sensor_info.virtual_sensor_info != nullptr &&
+ !sensor_info.virtual_sensor_info->trigger_sensor.empty()) {
+ const auto trigger_sensor_status =
+ sensor_status_map_.at(sensor_info.virtual_sensor_info->trigger_sensor);
+ if (trigger_sensor_status.severity != ThrottlingSeverity::NONE) {
+ sleep_ms = sensor_info.passive_delay;
+ }
+ }
+ // Check if the sensor need to be updated
+ if (sensor_status.last_update_time == boot_clock::time_point::min()) {
+ force_update = true;
+ LOG(VERBOSE) << "Force update " << name_status_pair.first
+ << "'s temperature after booting";
+ } else {
+ time_elapsed_ms = std::chrono::duration_cast<std::chrono::milliseconds>(
+ now - sensor_status.last_update_time);
+ if (time_elapsed_ms > sleep_ms) {
+ // Update the sensor because sleep timeout
+ force_update = true;
+ } else if (uevent_sensors.size() &&
+ uevent_sensors.find((sensor_info.virtual_sensor_info != nullptr)
+ ? sensor_info.virtual_sensor_info->trigger_sensor
+ : name_status_pair.first) !=
+ uevent_sensors.end()) {
+ // Force update the sensor from sysfs
+ force_update = true;
+ force_sysfs = true;
+ }
+ }
+
+ LOG(VERBOSE) << "sensor " << name_status_pair.first
+ << ": time_elapsed=" << time_elapsed_ms.count()
+ << ", sleep_ms=" << sleep_ms.count() << ", force_update = " << force_update
+ << ", force_sysfs = " << force_sysfs;
+
+ if (!force_update) {
+ auto timeout_remaining = sleep_ms - time_elapsed_ms;
+ if (min_sleep_ms > timeout_remaining) {
+ min_sleep_ms = timeout_remaining;
+ }
+ LOG(VERBOSE) << "sensor " << name_status_pair.first
+ << ": timeout_remaining=" << timeout_remaining.count();
+ continue;
+ }
+
+ std::pair<ThrottlingSeverity, ThrottlingSeverity> throtting_status;
+ if (!readTemperature(name_status_pair.first, &temp, &throtting_status, force_sysfs)) {
+ LOG(ERROR) << __func__
+ << ": error reading temperature for sensor: " << name_status_pair.first;
+ continue;
+ }
+ if (!readTemperatureThreshold(name_status_pair.first, &threshold)) {
+ LOG(ERROR) << __func__ << ": error reading temperature threshold for sensor: "
+ << name_status_pair.first;
+ continue;
+ }
+
+ {
+ // writer lock
+ std::unique_lock<std::shared_mutex> _lock(sensor_status_map_mutex_);
+ if (throtting_status.first != sensor_status.prev_hot_severity) {
+ sensor_status.prev_hot_severity = throtting_status.first;
+ }
+ if (throtting_status.second != sensor_status.prev_cold_severity) {
+ sensor_status.prev_cold_severity = throtting_status.second;
+ }
+ if (temp.throttlingStatus != sensor_status.severity) {
+ temps.push_back(temp);
+ sensor_status.severity = temp.throttlingStatus;
+ sleep_ms = (sensor_status.severity != ThrottlingSeverity::NONE)
+ ? sensor_info.passive_delay
+ : sensor_info.polling_delay;
+ }
+ }
+
+ if (!power_data_is_updated) {
+ power_files_.refreshPowerStatus();
+ power_data_is_updated = true;
+ }
+
+ if (sensor_status.severity == ThrottlingSeverity::NONE) {
+ LOG(VERBOSE) << temp.name << ": " << temp.value;
+ thermal_throttling_.clearThrottlingData(name_status_pair.first, sensor_info);
+ } else {
+ LOG(INFO) << temp.name << ": " << temp.value;
+ // update thermal throttling request
+ thermal_throttling_.thermalThrottlingUpdate(
+ temp, sensor_info, sensor_status.severity, time_elapsed_ms,
+ power_files_.GetPowerStatusMap(), cooling_device_info_map_);
+ }
+
+ thermal_throttling_.computeCoolingDevicesRequest(name_status_pair.first, sensor_info,
+ sensor_status.severity,
+ &cooling_devices_to_update);
+ if (min_sleep_ms > sleep_ms) {
+ min_sleep_ms = sleep_ms;
+ }
+
+ LOG(VERBOSE) << "Sensor " << name_status_pair.first << ": sleep_ms=" << sleep_ms.count()
+ << ", min_sleep_ms voting result=" << min_sleep_ms.count();
+ sensor_status.last_update_time = now;
+ }
+
+ if (!cooling_devices_to_update.empty()) {
+ updateCoolingDevices(cooling_devices_to_update);
+ }
+
+ if (!temps.empty()) {
+ for (const auto &t : temps) {
+ if (sensor_info_map_.at(t.name).send_cb && cb_) {
+ cb_(t);
+ }
+
+ if (sensor_info_map_.at(t.name).send_powerhint && isAidlPowerHalExist()) {
+ sendPowerExtHint(t);
+ }
+ }
+ }
+
+ return min_sleep_ms;
+}
+
+bool ThermalHelper::connectToPowerHal() {
+ return power_hal_service_.connect();
+}
+
+void ThermalHelper::updateSupportedPowerHints() {
+ for (auto const &name_status_pair : sensor_info_map_) {
+ if (!(name_status_pair.second.send_powerhint)) {
+ continue;
+ }
+ ThrottlingSeverity current_severity = ThrottlingSeverity::NONE;
+ for (const auto &severity : hidl_enum_range<ThrottlingSeverity>()) {
+ if (severity == ThrottlingSeverity::NONE) {
+ supported_powerhint_map_[name_status_pair.first][ThrottlingSeverity::NONE] =
+ ThrottlingSeverity::NONE;
+ continue;
+ }
+
+ bool isSupported = false;
+ ndk::ScopedAStatus isSupportedResult;
+
+ if (power_hal_service_.isPowerHalExtConnected()) {
+ isSupported = power_hal_service_.isModeSupported(name_status_pair.first, severity);
+ }
+ if (isSupported)
+ current_severity = severity;
+ supported_powerhint_map_[name_status_pair.first][severity] = current_severity;
+ }
+ }
+}
+
+void ThermalHelper::sendPowerExtHint(const Temperature_2_0 &t) {
+ ATRACE_CALL();
+ std::lock_guard<std::shared_mutex> lock(sensor_status_map_mutex_);
+ ThrottlingSeverity prev_hint_severity;
+ prev_hint_severity = sensor_status_map_.at(t.name).prev_hint_severity;
+ ThrottlingSeverity current_hint_severity = supported_powerhint_map_[t.name][t.throttlingStatus];
+
+ if (prev_hint_severity == current_hint_severity)
+ return;
+
+ if (prev_hint_severity != ThrottlingSeverity::NONE) {
+ power_hal_service_.setMode(t.name, prev_hint_severity, false);
+ }
+
+ if (current_hint_severity != ThrottlingSeverity::NONE) {
+ power_hal_service_.setMode(t.name, current_hint_severity, true);
+ }
+
+ sensor_status_map_[t.name].prev_hint_severity = current_hint_severity;
+}
+} // namespace implementation
+} // namespace V2_0
+} // namespace thermal
+} // namespace hardware
+} // namespace android
diff --git a/thermal/pid_1_0/thermal-helper.h b/thermal/pid_1_0/thermal-helper.h
new file mode 100644
index 0000000..6b7cd9a
--- /dev/null
+++ b/thermal/pid_1_0/thermal-helper.h
@@ -0,0 +1,178 @@
+/*
+ * 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 <array>
+#include <chrono>
+#include <mutex>
+#include <shared_mutex>
+#include <string>
+#include <string_view>
+#include <thread>
+#include <unordered_map>
+#include <vector>
+
+#include "utils/power_files.h"
+#include "utils/powerhal_helper.h"
+#include "utils/thermal_files.h"
+#include "utils/thermal_info.h"
+#include "utils/thermal_throttling.h"
+#include "utils/thermal_watcher.h"
+
+namespace android {
+namespace hardware {
+namespace thermal {
+namespace V2_0 {
+namespace implementation {
+
+using ::android::hardware::hidl_vec;
+using ::android::hardware::thermal::V1_0::CpuUsage;
+using ::android::hardware::thermal::V2_0::CoolingType;
+using ::android::hardware::thermal::V2_0::IThermal;
+using CoolingDevice_1_0 = ::android::hardware::thermal::V1_0::CoolingDevice;
+using CoolingDevice_2_0 = ::android::hardware::thermal::V2_0::CoolingDevice;
+using Temperature_1_0 = ::android::hardware::thermal::V1_0::Temperature;
+using Temperature_2_0 = ::android::hardware::thermal::V2_0::Temperature;
+using TemperatureType_1_0 = ::android::hardware::thermal::V1_0::TemperatureType;
+using TemperatureType_2_0 = ::android::hardware::thermal::V2_0::TemperatureType;
+using ::android::hardware::thermal::V2_0::TemperatureThreshold;
+using ::android::hardware::thermal::V2_0::ThrottlingSeverity;
+
+using NotificationCallback = std::function<void(const Temperature_2_0 &t)>;
+using NotificationTime = std::chrono::time_point<std::chrono::steady_clock>;
+
+// Get thermal_zone type
+bool getThermalZoneTypeById(int tz_id, std::string *);
+
+struct ThermalSample {
+ float temp;
+ boot_clock::time_point timestamp;
+};
+
+struct SensorStatus {
+ ThrottlingSeverity severity;
+ ThrottlingSeverity prev_hot_severity;
+ ThrottlingSeverity prev_cold_severity;
+ ThrottlingSeverity prev_hint_severity;
+ boot_clock::time_point last_update_time;
+ ThermalSample thermal_cached;
+};
+
+class ThermalHelper {
+ public:
+ explicit ThermalHelper(const NotificationCallback &cb);
+ ~ThermalHelper() = default;
+
+ bool fillTemperatures(hidl_vec<Temperature_1_0> *temperatures);
+ bool fillCurrentTemperatures(bool filterType, bool filterCallback, TemperatureType_2_0 type,
+ hidl_vec<Temperature_2_0> *temperatures);
+ bool fillTemperatureThresholds(bool filterType, TemperatureType_2_0 type,
+ hidl_vec<TemperatureThreshold> *thresholds) const;
+ bool fillCurrentCoolingDevices(bool filterType, CoolingType type,
+ hidl_vec<CoolingDevice_2_0> *coolingdevices) const;
+ bool fillCpuUsages(hidl_vec<CpuUsage> *cpu_usages) const;
+
+ // Dissallow copy and assign.
+ ThermalHelper(const ThermalHelper &) = delete;
+ void operator=(const ThermalHelper &) = delete;
+
+ bool isInitializedOk() const { return is_initialized_; }
+
+ // Read the temperature of a single sensor.
+ bool readTemperature(std::string_view sensor_name, Temperature_1_0 *out);
+ bool readTemperature(
+ std::string_view sensor_name, Temperature_2_0 *out,
+ std::pair<ThrottlingSeverity, ThrottlingSeverity> *throtting_status = nullptr,
+ const bool force_sysfs = false);
+ bool readTemperatureThreshold(std::string_view sensor_name, TemperatureThreshold *out) const;
+ // Read the value of a single cooling device.
+ bool readCoolingDevice(std::string_view cooling_device, CoolingDevice_2_0 *out) const;
+ // Get SensorInfo Map
+ const std::unordered_map<std::string, SensorInfo> &GetSensorInfoMap() const {
+ return sensor_info_map_;
+ }
+ // Get CdevInfo Map
+ const std::unordered_map<std::string, CdevInfo> &GetCdevInfoMap() const {
+ return cooling_device_info_map_;
+ }
+ // Get SensorStatus Map
+ const std::unordered_map<std::string, SensorStatus> &GetSensorStatusMap() const {
+ std::shared_lock<std::shared_mutex> _lock(sensor_status_map_mutex_);
+ return sensor_status_map_;
+ }
+ // Get ThermalThrottling Map
+ const std::unordered_map<std::string, ThermalThrottlingStatus> &GetThermalThrottlingStatusMap()
+ const {
+ return thermal_throttling_.GetThermalThrottlingStatusMap();
+ }
+ // Get PowerRailInfo Map
+ const std::unordered_map<std::string, PowerRailInfo> &GetPowerRailInfoMap() const {
+ return power_files_.GetPowerRailInfoMap();
+ }
+
+ // Get PowerStatus Map
+ const std::unordered_map<std::string, PowerStatus> &GetPowerStatusMap() const {
+ return power_files_.GetPowerStatusMap();
+ }
+ void sendPowerExtHint(const Temperature_2_0 &t);
+ bool isAidlPowerHalExist() { return power_hal_service_.isAidlPowerHalExist(); }
+ bool isPowerHalConnected() { return power_hal_service_.isPowerHalConnected(); }
+ bool isPowerHalExtConnected() { return power_hal_service_.isPowerHalExtConnected(); }
+
+ private:
+ bool initializeSensorMap(const std::unordered_map<std::string, std::string> &path_map);
+ bool initializeCoolingDevices(const std::unordered_map<std::string, std::string> &path_map);
+ void setMinTimeout(SensorInfo *sensor_info);
+ void initializeTrip(const std::unordered_map<std::string, std::string> &path_map,
+ std::set<std::string> *monitored_sensors, bool thermal_genl_enabled);
+
+ // For thermal_watcher_'s polling thread, return the sleep interval
+ std::chrono::milliseconds thermalWatcherCallbackFunc(
+ const std::set<std::string> &uevent_sensors);
+ // Return hot and cold severity status as std::pair
+ std::pair<ThrottlingSeverity, ThrottlingSeverity> getSeverityFromThresholds(
+ const ThrottlingArray &hot_thresholds, const ThrottlingArray &cold_thresholds,
+ const ThrottlingArray &hot_hysteresis, const ThrottlingArray &cold_hysteresis,
+ ThrottlingSeverity prev_hot_severity, ThrottlingSeverity prev_cold_severity,
+ float value) const;
+ // Read temperature data according to thermal sensor's info
+ bool readThermalSensor(std::string_view sensor_name, float *temp, const bool force_sysfs);
+ bool connectToPowerHal();
+ void updateSupportedPowerHints();
+ void updateCoolingDevices(const std::vector<std::string> &cooling_devices_to_update);
+ sp<ThermalWatcher> thermal_watcher_;
+ PowerFiles power_files_;
+ ThermalFiles thermal_sensors_;
+ ThermalFiles cooling_devices_;
+ ThermalThrottling thermal_throttling_;
+ bool is_initialized_;
+ const NotificationCallback cb_;
+ std::unordered_map<std::string, CdevInfo> cooling_device_info_map_;
+ std::unordered_map<std::string, SensorInfo> sensor_info_map_;
+ std::unordered_map<std::string, std::map<ThrottlingSeverity, ThrottlingSeverity>>
+ supported_powerhint_map_;
+ PowerHalService power_hal_service_;
+
+ mutable std::shared_mutex sensor_status_map_mutex_;
+ std::unordered_map<std::string, SensorStatus> sensor_status_map_;
+};
+
+} // namespace implementation
+} // namespace V2_0
+} // namespace thermal
+} // namespace hardware
+} // namespace android
diff --git a/thermal/pid_1_0/utils/power_files.cpp b/thermal/pid_1_0/utils/power_files.cpp
new file mode 100644
index 0000000..3561e84
--- /dev/null
+++ b/thermal/pid_1_0/utils/power_files.cpp
@@ -0,0 +1,342 @@
+
+/*
+ * Copyright (C) 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 ATRACE_TAG (ATRACE_TAG_THERMAL | ATRACE_TAG_HAL)
+
+#include "power_files.h"
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <dirent.h>
+#include <utils/Trace.h>
+
+namespace android {
+namespace hardware {
+namespace thermal {
+namespace V2_0 {
+namespace implementation {
+
+constexpr std::string_view kDeviceType("iio:device");
+constexpr std::string_view kIioRootDir("/sys/bus/iio/devices");
+constexpr std::string_view kEnergyValueNode("energy_value");
+
+using android::base::ReadFileToString;
+using android::base::StringPrintf;
+
+bool PowerFiles::registerPowerRailsToWatch(std::string_view config_path) {
+ if (!ParsePowerRailInfo(config_path, &power_rail_info_map_)) {
+ LOG(ERROR) << "Failed to parse power rail info config";
+ return false;
+ }
+
+ if (!power_rail_info_map_.size()) {
+ LOG(INFO) << " No power rail info config found";
+ return true;
+ }
+
+ if (!findEnergySourceToWatch()) {
+ LOG(ERROR) << "Cannot find energy source";
+ return false;
+ }
+
+ if (!energy_info_map_.size() && !updateEnergyValues()) {
+ LOG(ERROR) << "Faield to update energy info";
+ return false;
+ }
+
+ for (const auto &power_rail_info_pair : power_rail_info_map_) {
+ std::vector<std::queue<PowerSample>> power_history;
+ if (!power_rail_info_pair.second.power_sample_count ||
+ power_rail_info_pair.second.power_sample_delay == std::chrono::milliseconds::max()) {
+ continue;
+ }
+
+ PowerSample power_sample = {
+ .energy_counter = 0,
+ .duration = 0,
+ };
+
+ if (power_rail_info_pair.second.virtual_power_rail_info != nullptr &&
+ power_rail_info_pair.second.virtual_power_rail_info->linked_power_rails.size()) {
+ for (size_t i = 0;
+ i < power_rail_info_pair.second.virtual_power_rail_info->linked_power_rails.size();
+ ++i) {
+ if (!energy_info_map_.count(power_rail_info_pair.second.virtual_power_rail_info
+ ->linked_power_rails[i])) {
+ LOG(ERROR) << " Could not find energy source "
+ << power_rail_info_pair.second.virtual_power_rail_info
+ ->linked_power_rails[i];
+ return false;
+ }
+ power_history.emplace_back(std::queue<PowerSample>());
+ for (int j = 0; j < power_rail_info_pair.second.power_sample_count; j++) {
+ power_history[i].emplace(power_sample);
+ }
+ }
+ } else {
+ if (energy_info_map_.count(power_rail_info_pair.first)) {
+ power_history.emplace_back(std::queue<PowerSample>());
+ for (int j = 0; j < power_rail_info_pair.second.power_sample_count; j++) {
+ power_history[0].emplace(power_sample);
+ }
+ } else {
+ LOG(ERROR) << "Could not find energy source " << power_rail_info_pair.first;
+ return false;
+ }
+ }
+
+ if (power_history.size()) {
+ power_status_map_[power_rail_info_pair.first] = {
+ .power_history = power_history,
+ .last_update_time = boot_clock::time_point::min(),
+ .last_updated_avg_power = NAN,
+ };
+ } else {
+ LOG(ERROR) << "power history size is zero";
+ return false;
+ }
+ LOG(INFO) << "Successfully to register power rail " << power_rail_info_pair.first;
+ }
+ return true;
+}
+
+bool PowerFiles::findEnergySourceToWatch(void) {
+ std::string devicePath;
+
+ if (energy_path_set_.size()) {
+ return true;
+ }
+
+ std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(kIioRootDir.data()), closedir);
+ if (!dir) {
+ PLOG(ERROR) << "Error opening directory" << kIioRootDir;
+ return false;
+ }
+
+ // Find any iio:devices that support energy_value
+ while (struct dirent *ent = readdir(dir.get())) {
+ std::string devTypeDir = ent->d_name;
+ if (devTypeDir.find(kDeviceType) != std::string::npos) {
+ devicePath = StringPrintf("%s/%s", kIioRootDir.data(), devTypeDir.data());
+ std::string deviceEnergyContent;
+
+ if (!ReadFileToString(StringPrintf("%s/%s", devicePath.data(), kEnergyValueNode.data()),
+ &deviceEnergyContent)) {
+ } else if (deviceEnergyContent.size()) {
+ energy_path_set_.emplace(
+ StringPrintf("%s/%s", devicePath.data(), kEnergyValueNode.data()));
+ }
+ }
+ }
+
+ if (!energy_path_set_.size()) {
+ return false;
+ }
+
+ return true;
+}
+
+bool PowerFiles::updateEnergyValues(void) {
+ std::string deviceEnergyContent;
+ std::string deviceEnergyContents;
+ std::string line;
+
+ ATRACE_CALL();
+ for (const auto &path : energy_path_set_) {
+ if (!android::base::ReadFileToString(path, &deviceEnergyContent)) {
+ LOG(ERROR) << "Failed to read energy content from " << path;
+ return false;
+ } else {
+ deviceEnergyContents.append(deviceEnergyContent);
+ }
+ }
+
+ std::istringstream energyData(deviceEnergyContents);
+
+ while (std::getline(energyData, line)) {
+ /* Read rail energy */
+ uint64_t energy_counter = 0;
+ uint64_t duration = 0;
+
+ /* Format example: CH3(T=358356)[S2M_VDD_CPUCL2], 761330 */
+ auto start_pos = line.find("T=");
+ auto end_pos = line.find(')');
+ if (start_pos != std::string::npos) {
+ duration =
+ strtoul(line.substr(start_pos + 2, end_pos - start_pos - 2).c_str(), NULL, 10);
+ } else {
+ continue;
+ }
+
+ start_pos = line.find(")[");
+ end_pos = line.find(']');
+ std::string railName;
+ if (start_pos != std::string::npos) {
+ railName = line.substr(start_pos + 2, end_pos - start_pos - 2);
+ } else {
+ continue;
+ }
+
+ start_pos = line.find("],");
+ if (start_pos != std::string::npos) {
+ energy_counter = strtoul(line.substr(start_pos + 2).c_str(), NULL, 10);
+ } else {
+ continue;
+ }
+
+ energy_info_map_[railName] = {
+ .energy_counter = energy_counter,
+ .duration = duration,
+ };
+ }
+
+ return true;
+}
+
+float PowerFiles::updateAveragePower(std::string_view power_rail,
+ std::queue<PowerSample> *power_history) {
+ float avg_power = NAN;
+
+ if (!energy_info_map_.count(power_rail.data())) {
+ LOG(ERROR) << " Could not find power rail " << power_rail.data();
+ return avg_power;
+ }
+ const auto last_sample = power_history->front();
+ const auto curr_sample = energy_info_map_.at(power_rail.data());
+ const auto duration = curr_sample.duration - last_sample.duration;
+ const auto deltaEnergy = curr_sample.energy_counter - last_sample.energy_counter;
+
+ if (!last_sample.duration) {
+ LOG(VERBOSE) << "Power rail " << power_rail.data()
+ << ": all power samples have not been collected yet";
+ } else if (duration <= 0 || deltaEnergy < 0) {
+ LOG(ERROR) << "Power rail " << power_rail.data() << " is invalid: duration = " << duration
+ << ", deltaEnergy = " << deltaEnergy;
+
+ return avg_power;
+ } else {
+ avg_power = static_cast<float>(deltaEnergy) / static_cast<float>(duration);
+ LOG(VERBOSE) << "Power rail " << power_rail.data() << ", avg power = " << avg_power
+ << ", duration = " << duration << ", deltaEnergy = " << deltaEnergy;
+ }
+
+ power_history->pop();
+ power_history->push(curr_sample);
+
+ return avg_power;
+}
+
+float PowerFiles::updatePowerRail(std::string_view power_rail) {
+ float avg_power = NAN;
+
+ if (!power_rail_info_map_.count(power_rail.data())) {
+ return avg_power;
+ }
+
+ if (!power_status_map_.count(power_rail.data())) {
+ return avg_power;
+ }
+
+ const auto &power_rail_info = power_rail_info_map_.at(power_rail.data());
+ auto &power_status = power_status_map_.at(power_rail.data());
+
+ boot_clock::time_point now = boot_clock::now();
+ auto time_elapsed_ms = std::chrono::duration_cast<std::chrono::milliseconds>(
+ now - power_status.last_update_time);
+
+ if (power_status.last_update_time != boot_clock::time_point::min() &&
+ time_elapsed_ms < power_rail_info.power_sample_delay) {
+ return power_status.last_updated_avg_power;
+ }
+
+ if (!energy_info_map_.size() && !updateEnergyValues()) {
+ LOG(ERROR) << "Failed to update energy values";
+ return avg_power;
+ }
+
+ if (power_rail_info.virtual_power_rail_info == nullptr) {
+ avg_power = updateAveragePower(power_rail, &power_status.power_history[0]);
+ } else {
+ const auto offset = power_rail_info.virtual_power_rail_info->offset;
+ float avg_power_val = 0.0;
+ for (size_t i = 0; i < power_rail_info.virtual_power_rail_info->linked_power_rails.size();
+ i++) {
+ float coefficient = power_rail_info.virtual_power_rail_info->coefficients[i];
+ float avg_power_number = updateAveragePower(
+ power_rail_info.virtual_power_rail_info->linked_power_rails[i],
+ &power_status.power_history[i]);
+
+ switch (power_rail_info.virtual_power_rail_info->formula) {
+ case FormulaOption::COUNT_THRESHOLD:
+ if ((coefficient < 0 && avg_power_number < -coefficient) ||
+ (coefficient >= 0 && avg_power_number >= coefficient))
+ avg_power_val += 1;
+ break;
+ case FormulaOption::WEIGHTED_AVG:
+ avg_power_val += avg_power_number * coefficient;
+ break;
+ case FormulaOption::MAXIMUM:
+ if (i == 0)
+ avg_power_val = std::numeric_limits<float>::lowest();
+ if (avg_power_number * coefficient > avg_power_val)
+ avg_power_val = avg_power_number * coefficient;
+ break;
+ case FormulaOption::MINIMUM:
+ if (i == 0)
+ avg_power_val = std::numeric_limits<float>::max();
+ if (avg_power_number * coefficient < avg_power_val)
+ avg_power_val = avg_power_number * coefficient;
+ break;
+ default:
+ break;
+ }
+ }
+ if (avg_power_val >= 0) {
+ avg_power_val = avg_power_val + offset;
+ }
+
+ avg_power = avg_power_val;
+ }
+
+ if (avg_power < 0) {
+ avg_power = NAN;
+ }
+
+ power_status.last_updated_avg_power = avg_power;
+ power_status.last_update_time = now;
+ return avg_power;
+}
+
+bool PowerFiles::refreshPowerStatus(void) {
+ if (!updateEnergyValues()) {
+ LOG(ERROR) << "Failed to update energy values";
+ return false;
+ }
+
+ for (const auto &power_status_pair : power_status_map_) {
+ updatePowerRail(power_status_pair.first);
+ }
+ return true;
+}
+
+} // namespace implementation
+} // namespace V2_0
+} // namespace thermal
+} // namespace hardware
+} // namespace android
diff --git a/thermal/pid_1_0/utils/power_files.h b/thermal/pid_1_0/utils/power_files.h
new file mode 100644
index 0000000..65c6758
--- /dev/null
+++ b/thermal/pid_1_0/utils/power_files.h
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 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 <android-base/chrono_utils.h>
+
+#include <chrono>
+#include <queue>
+#include <shared_mutex>
+#include <string>
+#include <unordered_map>
+#include <unordered_set>
+
+#include "thermal_info.h"
+
+namespace android {
+namespace hardware {
+namespace thermal {
+namespace V2_0 {
+namespace implementation {
+
+using android::base::boot_clock;
+
+struct PowerSample {
+ uint64_t energy_counter;
+ uint64_t duration;
+};
+
+struct PowerStatus {
+ boot_clock::time_point last_update_time;
+ // A vector to record the queues of power sample history.
+ std::vector<std::queue<PowerSample>> power_history;
+ float last_updated_avg_power;
+};
+
+// A helper class for monitoring power rails.
+class PowerFiles {
+ public:
+ PowerFiles() = default;
+ ~PowerFiles() = default;
+ // Disallow copy and assign.
+ PowerFiles(const PowerFiles &) = delete;
+ void operator=(const PowerFiles &) = delete;
+ bool registerPowerRailsToWatch(std::string_view config_path);
+ // Update the power data from ODPM sysfs
+ bool refreshPowerStatus(void);
+ // Get power status map
+ const std::unordered_map<std::string, PowerStatus> &GetPowerStatusMap() const {
+ std::shared_lock<std::shared_mutex> _lock(power_status_map_mutex_);
+ return power_status_map_;
+ }
+ // Get power rail info map
+ const std::unordered_map<std::string, PowerRailInfo> &GetPowerRailInfoMap() const {
+ return power_rail_info_map_;
+ }
+
+ private:
+ // Update energy value to energy_info_map_, return false if the value is failed to update.
+ bool updateEnergyValues(void);
+ // Compute the average power for physical power rail.
+ float updateAveragePower(std::string_view power_rail, std::queue<PowerSample> *power_history);
+ // Update the power data for the target power rail.
+ float updatePowerRail(std::string_view power_rail);
+ // Find the energy source path, return false if no energy source found.
+ bool findEnergySourceToWatch(void);
+ // The map to record the energy counter for each power rail.
+ std::unordered_map<std::string, PowerSample> energy_info_map_;
+ // The map to record the power data for each thermal sensor.
+ std::unordered_map<std::string, PowerStatus> power_status_map_;
+ mutable std::shared_mutex power_status_map_mutex_;
+ // The map to record the power rail information from thermal config
+ std::unordered_map<std::string, PowerRailInfo> power_rail_info_map_;
+ // The set to store the energy source paths
+ std::unordered_set<std::string> energy_path_set_;
+};
+
+} // namespace implementation
+} // namespace V2_0
+} // namespace thermal
+} // namespace hardware
+} // namespace android
diff --git a/thermal/pid_1_0/utils/powerhal_helper.cpp b/thermal/pid_1_0/utils/powerhal_helper.cpp
new file mode 100644
index 0000000..515fa2d
--- /dev/null
+++ b/thermal/pid_1_0/utils/powerhal_helper.cpp
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "powerhal_helper.h"
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <android/binder_manager.h>
+#include <hidl/HidlTransportSupport.h>
+
+#include <iterator>
+#include <set>
+#include <sstream>
+#include <thread>
+#include <vector>
+
+#include "thermal_info.h"
+#include "thermal_throttling.h"
+
+namespace android {
+namespace hardware {
+namespace thermal {
+namespace V2_0 {
+namespace implementation {
+
+using android::base::StringPrintf;
+
+PowerHalService::PowerHalService()
+ : power_hal_aidl_exist_(true), power_hal_aidl_(nullptr), power_hal_ext_aidl_(nullptr) {
+ connect();
+}
+
+bool PowerHalService::connect() {
+ std::lock_guard<std::mutex> lock(lock_);
+ if (!power_hal_aidl_exist_)
+ return false;
+
+ if (power_hal_aidl_ != nullptr)
+ return true;
+
+ const std::string kInstance = std::string(IPower::descriptor) + "/default";
+ ndk::SpAIBinder power_binder = ndk::SpAIBinder(AServiceManager_getService(kInstance.c_str()));
+ ndk::SpAIBinder ext_power_binder;
+
+ if (power_binder.get() == nullptr) {
+ LOG(ERROR) << "Cannot get Power Hal Binder";
+ power_hal_aidl_exist_ = false;
+ return false;
+ }
+
+ power_hal_aidl_ = IPower::fromBinder(power_binder);
+
+ if (power_hal_aidl_ == nullptr) {
+ power_hal_aidl_exist_ = false;
+ LOG(ERROR) << "Cannot get Power Hal AIDL" << kInstance.c_str();
+ return false;
+ }
+
+ if (STATUS_OK != AIBinder_getExtension(power_binder.get(), ext_power_binder.getR()) ||
+ ext_power_binder.get() == nullptr) {
+ LOG(ERROR) << "Cannot get Power Hal Extension Binder";
+ power_hal_aidl_exist_ = false;
+ return false;
+ }
+
+ power_hal_ext_aidl_ = IPowerExt::fromBinder(ext_power_binder);
+ if (power_hal_ext_aidl_ == nullptr) {
+ LOG(ERROR) << "Cannot get Power Hal Extension AIDL";
+ power_hal_aidl_exist_ = false;
+ }
+
+ return true;
+}
+
+bool PowerHalService::isModeSupported(const std::string &type, const ThrottlingSeverity &t) {
+ bool isSupported = false;
+ if (!isPowerHalConnected()) {
+ return false;
+ }
+ std::string power_hint = StringPrintf("THERMAL_%s_%s", type.c_str(), toString(t).c_str());
+ lock_.lock();
+ if (!power_hal_ext_aidl_->isModeSupported(power_hint, &isSupported).isOk()) {
+ LOG(ERROR) << "Fail to check supported mode, Hint: " << power_hint;
+ power_hal_aidl_exist_ = false;
+ power_hal_ext_aidl_ = nullptr;
+ power_hal_aidl_ = nullptr;
+ lock_.unlock();
+ return false;
+ }
+ lock_.unlock();
+ return isSupported;
+}
+
+void PowerHalService::setMode(const std::string &type, const ThrottlingSeverity &t,
+ const bool &enable) {
+ if (!isPowerHalConnected()) {
+ return;
+ }
+
+ std::string power_hint = StringPrintf("THERMAL_%s_%s", type.c_str(), toString(t).c_str());
+ LOG(INFO) << "Send Hint " << power_hint << " Enable: " << std::boolalpha << enable;
+ lock_.lock();
+ if (!power_hal_ext_aidl_->setMode(power_hint, enable).isOk()) {
+ LOG(ERROR) << "Fail to set mode, Hint: " << power_hint;
+ power_hal_aidl_exist_ = false;
+ power_hal_ext_aidl_ = nullptr;
+ power_hal_aidl_ = nullptr;
+ lock_.unlock();
+ return;
+ }
+ lock_.unlock();
+}
+
+} // namespace implementation
+} // namespace V2_0
+} // namespace thermal
+} // namespace hardware
+} // namespace android
diff --git a/thermal/pid_1_0/utils/powerhal_helper.h b/thermal/pid_1_0/utils/powerhal_helper.h
new file mode 100644
index 0000000..761e315
--- /dev/null
+++ b/thermal/pid_1_0/utils/powerhal_helper.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <aidl/android/hardware/power/IPower.h>
+#include <aidl/google/hardware/power/extension/pixel/IPowerExt.h>
+#include <android/hardware/thermal/2.0/IThermal.h>
+
+#include <queue>
+#include <shared_mutex>
+#include <string>
+#include <unordered_map>
+#include <unordered_set>
+
+namespace android {
+namespace hardware {
+namespace thermal {
+namespace V2_0 {
+namespace implementation {
+
+using ::aidl::android::hardware::power::IPower;
+using ::aidl::google::hardware::power::extension::pixel::IPowerExt;
+
+using ::android::hardware::hidl_vec;
+using ::android::hardware::thermal::V2_0::IThermal;
+using Temperature_2_0 = ::android::hardware::thermal::V2_0::Temperature;
+using ::android::hardware::thermal::V2_0::TemperatureThreshold;
+using ::android::hardware::thermal::V2_0::ThrottlingSeverity;
+using CdevRequestStatus = std::unordered_map<std::string, int>;
+
+class PowerHalService {
+ public:
+ PowerHalService();
+ ~PowerHalService() = default;
+ bool connect();
+ bool isAidlPowerHalExist() { return power_hal_aidl_exist_; }
+ bool isModeSupported(const std::string &type, const ThrottlingSeverity &t);
+ bool isPowerHalConnected() { return power_hal_aidl_ != nullptr; }
+ bool isPowerHalExtConnected() { return power_hal_ext_aidl_ != nullptr; }
+ void setMode(const std::string &type, const ThrottlingSeverity &t, const bool &enable);
+
+ private:
+ bool power_hal_aidl_exist_;
+ std::shared_ptr<IPower> power_hal_aidl_;
+ std::shared_ptr<IPowerExt> power_hal_ext_aidl_;
+ std::mutex lock_;
+};
+
+} // namespace implementation
+} // namespace V2_0
+} // namespace thermal
+} // namespace hardware
+} // namespace android
diff --git a/thermal/pid_1_0/utils/thermal_files.cpp b/thermal/pid_1_0/utils/thermal_files.cpp
new file mode 100644
index 0000000..ff10d8b
--- /dev/null
+++ b/thermal/pid_1_0/utils/thermal_files.cpp
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 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 ATRACE_TAG (ATRACE_TAG_THERMAL | ATRACE_TAG_HAL)
+
+#include "thermal_files.h"
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <utils/Trace.h>
+
+#include <algorithm>
+#include <string_view>
+
+namespace android {
+namespace hardware {
+namespace thermal {
+namespace V2_0 {
+namespace implementation {
+
+using android::base::StringPrintf;
+
+std::string ThermalFiles::getThermalFilePath(std::string_view thermal_name) const {
+ auto sensor_itr = thermal_name_to_path_map_.find(thermal_name.data());
+ if (sensor_itr == thermal_name_to_path_map_.end()) {
+ return "";
+ }
+ return sensor_itr->second;
+}
+
+bool ThermalFiles::addThermalFile(std::string_view thermal_name, std::string_view path) {
+ return thermal_name_to_path_map_.emplace(thermal_name, path).second;
+}
+
+bool ThermalFiles::readThermalFile(std::string_view thermal_name, std::string *data) const {
+ std::string sensor_reading;
+ std::string file_path = getThermalFilePath(std::string_view(thermal_name));
+ *data = "";
+
+ ATRACE_NAME(StringPrintf("ThermalFiles::readThermalFile - %s", thermal_name.data()).c_str());
+ if (file_path.empty()) {
+ PLOG(WARNING) << "Failed to find " << thermal_name << "'s path";
+ return false;
+ }
+
+ if (!::android::base::ReadFileToString(file_path, &sensor_reading)) {
+ PLOG(WARNING) << "Failed to read sensor: " << thermal_name;
+ return false;
+ }
+
+ // Strip the newline.
+ *data = ::android::base::Trim(sensor_reading);
+ return true;
+}
+
+bool ThermalFiles::writeCdevFile(std::string_view cdev_name, std::string_view data) {
+ std::string file_path =
+ getThermalFilePath(android::base::StringPrintf("%s_%s", cdev_name.data(), "w"));
+
+ ATRACE_NAME(StringPrintf("ThermalFiles::writeCdevFile - %s", cdev_name.data()).c_str());
+ if (!android::base::WriteStringToFile(data.data(), file_path)) {
+ PLOG(WARNING) << "Failed to write cdev: " << cdev_name << " to " << data.data();
+ return false;
+ }
+
+ return true;
+}
+
+} // namespace implementation
+} // namespace V2_0
+} // namespace thermal
+} // namespace hardware
+} // namespace android
diff --git a/thermal/pid_1_0/utils/thermal_files.h b/thermal/pid_1_0/utils/thermal_files.h
new file mode 100644
index 0000000..59afba6
--- /dev/null
+++ b/thermal/pid_1_0/utils/thermal_files.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 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 <string>
+#include <unordered_map>
+
+namespace android {
+namespace hardware {
+namespace thermal {
+namespace V2_0 {
+namespace implementation {
+
+class ThermalFiles {
+ public:
+ ThermalFiles() = default;
+ ~ThermalFiles() = default;
+ ThermalFiles(const ThermalFiles &) = delete;
+ void operator=(const ThermalFiles &) = delete;
+
+ std::string getThermalFilePath(std::string_view thermal_name) const;
+ // Returns true if add was successful, false otherwise.
+ bool addThermalFile(std::string_view thermal_name, std::string_view path);
+ // If thermal_name is not found in the thermal names to path map, this will set
+ // data to empty and return false. If the thermal_name is found and its content
+ // is read, this function will fill in data accordingly then return true.
+ bool readThermalFile(std::string_view thermal_name, std::string *data) const;
+ bool writeCdevFile(std::string_view thermal_name, std::string_view data);
+ size_t getNumThermalFiles() const { return thermal_name_to_path_map_.size(); }
+
+ private:
+ std::unordered_map<std::string, std::string> thermal_name_to_path_map_;
+};
+
+} // namespace implementation
+} // namespace V2_0
+} // namespace thermal
+} // namespace hardware
+} // namespace android
diff --git a/thermal/pid_1_0/utils/thermal_info.cpp b/thermal/pid_1_0/utils/thermal_info.cpp
new file mode 100644
index 0000000..7ed63c3
--- /dev/null
+++ b/thermal/pid_1_0/utils/thermal_info.cpp
@@ -0,0 +1,1021 @@
+/*
+ * 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.
+ */
+#include "thermal_info.h"
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <android-base/strings.h>
+#include <json/reader.h>
+#include <json/value.h>
+
+#include <cmath>
+#include <unordered_set>
+
+namespace android {
+namespace hardware {
+namespace thermal {
+namespace V2_0 {
+namespace implementation {
+
+constexpr std::string_view kPowerLinkDisabledProperty("vendor.disable.thermal.powerlink");
+
+using ::android::hardware::hidl_enum_range;
+using ::android::hardware::thermal::V2_0::toString;
+using TemperatureType_2_0 = ::android::hardware::thermal::V2_0::TemperatureType;
+
+namespace {
+
+template <typename T>
+// Return false when failed parsing
+bool getTypeFromString(std::string_view str, T *out) {
+ auto types = hidl_enum_range<T>();
+ for (const auto &type : types) {
+ if (toString(type) == str) {
+ *out = type;
+ return true;
+ }
+ }
+ return false;
+}
+
+float getFloatFromValue(const Json::Value &value) {
+ if (value.isString()) {
+ return std::stof(value.asString());
+ } else {
+ return value.asFloat();
+ }
+}
+
+int getIntFromValue(const Json::Value &value) {
+ if (value.isString()) {
+ return (value.asString() == "max") ? std::numeric_limits<int>::max()
+ : std::stoul(value.asString());
+ } else {
+ return value.asInt();
+ }
+}
+
+bool getIntFromJsonValues(const Json::Value &values, CdevArray *out, bool inc_check,
+ bool dec_check) {
+ CdevArray ret;
+
+ if (inc_check && dec_check) {
+ LOG(ERROR) << "Cannot enable inc_check and dec_check at the same time";
+ return false;
+ }
+
+ if (values.size() != kThrottlingSeverityCount) {
+ LOG(ERROR) << "Values size is invalid";
+ return false;
+ } else {
+ int last;
+ for (Json::Value::ArrayIndex i = 0; i < kThrottlingSeverityCount; ++i) {
+ ret[i] = getIntFromValue(values[i]);
+ if (inc_check && ret[i] < last) {
+ LOG(FATAL) << "Invalid array[" << i << "]" << ret[i] << " min=" << last;
+ return false;
+ }
+ if (dec_check && ret[i] > last) {
+ LOG(FATAL) << "Invalid array[" << i << "]" << ret[i] << " max=" << last;
+ return false;
+ }
+ last = ret[i];
+ LOG(INFO) << "[" << i << "]: " << ret[i];
+ }
+ }
+
+ *out = ret;
+ return true;
+}
+
+bool getFloatFromJsonValues(const Json::Value &values, ThrottlingArray *out, bool inc_check,
+ bool dec_check) {
+ ThrottlingArray ret;
+
+ if (inc_check && dec_check) {
+ LOG(ERROR) << "Cannot enable inc_check and dec_check at the same time";
+ return false;
+ }
+
+ if (values.size() != kThrottlingSeverityCount) {
+ LOG(ERROR) << "Values size is invalid";
+ return false;
+ } else {
+ float last = std::nanf("");
+ for (Json::Value::ArrayIndex i = 0; i < kThrottlingSeverityCount; ++i) {
+ ret[i] = getFloatFromValue(values[i]);
+ if (inc_check && !std::isnan(last) && !std::isnan(ret[i]) && ret[i] < last) {
+ LOG(FATAL) << "Invalid array[" << i << "]" << ret[i] << " min=" << last;
+ return false;
+ }
+ if (dec_check && !std::isnan(last) && !std::isnan(ret[i]) && ret[i] > last) {
+ LOG(FATAL) << "Invalid array[" << i << "]" << ret[i] << " max=" << last;
+ return false;
+ }
+ last = std::isnan(ret[i]) ? last : ret[i];
+ LOG(INFO) << "[" << i << "]: " << ret[i];
+ }
+ }
+
+ *out = ret;
+ return true;
+}
+} // namespace
+
+bool ParseSensorInfo(std::string_view config_path,
+ std::unordered_map<std::string, SensorInfo> *sensors_parsed) {
+ std::string json_doc;
+ if (!android::base::ReadFileToString(config_path.data(), &json_doc)) {
+ LOG(ERROR) << "Failed to read JSON config from " << config_path;
+ return false;
+ }
+ Json::Value root;
+ Json::CharReaderBuilder builder;
+ std::unique_ptr<Json::CharReader> reader(builder.newCharReader());
+ std::string errorMessage;
+
+ if (!reader->parse(&*json_doc.begin(), &*json_doc.end(), &root, &errorMessage)) {
+ LOG(ERROR) << "Failed to parse JSON config: " << errorMessage;
+ return false;
+ }
+
+ Json::Value sensors = root["Sensors"];
+ std::size_t total_parsed = 0;
+ std::unordered_set<std::string> sensors_name_parsed;
+
+ for (Json::Value::ArrayIndex i = 0; i < sensors.size(); ++i) {
+ const std::string &name = sensors[i]["Name"].asString();
+ LOG(INFO) << "Sensor[" << i << "]'s Name: " << name;
+ if (name.empty()) {
+ LOG(ERROR) << "Failed to read "
+ << "Sensor[" << i << "]'s Name";
+ sensors_parsed->clear();
+ return false;
+ }
+
+ auto result = sensors_name_parsed.insert(name);
+ if (!result.second) {
+ LOG(ERROR) << "Duplicate Sensor[" << i << "]'s Name";
+ sensors_parsed->clear();
+ return false;
+ }
+
+ std::string sensor_type_str = sensors[i]["Type"].asString();
+ LOG(INFO) << "Sensor[" << name << "]'s Type: " << sensor_type_str;
+ TemperatureType_2_0 sensor_type;
+
+ if (!getTypeFromString(sensor_type_str, &sensor_type)) {
+ LOG(ERROR) << "Invalid "
+ << "Sensor[" << name << "]'s Type: " << sensor_type_str;
+ sensors_parsed->clear();
+ return false;
+ }
+
+ bool send_cb = false;
+ if (sensors[i]["Monitor"].empty() || !sensors[i]["Monitor"].isBool()) {
+ LOG(INFO) << "Failed to read Sensor[" << name << "]'s Monitor, set to 'false'";
+ } else if (sensors[i]["Monitor"].asBool()) {
+ send_cb = true;
+ }
+ LOG(INFO) << "Sensor[" << name << "]'s SendCallback: " << std::boolalpha << send_cb
+ << std::noboolalpha;
+
+ bool send_powerhint = false;
+ if (sensors[i]["SendPowerHint"].empty() || !sensors[i]["SendPowerHint"].isBool()) {
+ LOG(INFO) << "Failed to read Sensor[" << name << "]'s SendPowerHint, set to 'false'";
+ } else if (sensors[i]["SendPowerHint"].asBool()) {
+ send_powerhint = true;
+ }
+ LOG(INFO) << "Sensor[" << name << "]'s SendPowerHint: " << std::boolalpha << send_powerhint
+ << std::noboolalpha;
+
+ bool is_hidden = false;
+ if (sensors[i]["Hidden"].empty() || !sensors[i]["Hidden"].isBool()) {
+ LOG(INFO) << "Failed to read Sensor[" << name << "]'s Hidden, set to 'false'";
+ } else if (sensors[i]["Hidden"].asBool()) {
+ is_hidden = true;
+ }
+ LOG(INFO) << "Sensor[" << name << "]'s Hidden: " << std::boolalpha << is_hidden
+ << std::noboolalpha;
+
+ std::array<float, kThrottlingSeverityCount> hot_thresholds;
+ hot_thresholds.fill(NAN);
+ std::array<float, kThrottlingSeverityCount> cold_thresholds;
+ cold_thresholds.fill(NAN);
+ std::array<float, kThrottlingSeverityCount> hot_hysteresis;
+ hot_hysteresis.fill(0.0);
+ std::array<float, kThrottlingSeverityCount> cold_hysteresis;
+ cold_hysteresis.fill(0.0);
+ std::vector<std::string> linked_sensors;
+ std::vector<float> coefficients;
+ float offset = 0;
+ std::string trigger_sensor;
+
+ FormulaOption formula = FormulaOption::COUNT_THRESHOLD;
+ bool is_virtual_sensor = false;
+ if (sensors[i]["VirtualSensor"].empty() || !sensors[i]["VirtualSensor"].isBool()) {
+ LOG(INFO) << "Failed to read Sensor[" << name << "]'s VirtualSensor, set to 'false'";
+ } else {
+ is_virtual_sensor = sensors[i]["VirtualSensor"].asBool();
+ }
+ Json::Value values = sensors[i]["HotThreshold"];
+ if (!values.size()) {
+ LOG(INFO) << "Sensor[" << name << "]'s HotThreshold, default all to NAN";
+ } else if (values.size() != kThrottlingSeverityCount) {
+ LOG(ERROR) << "Invalid "
+ << "Sensor[" << name << "]'s HotThreshold count:" << values.size();
+ sensors_parsed->clear();
+ return false;
+ } else {
+ float min = std::numeric_limits<float>::min();
+ for (Json::Value::ArrayIndex j = 0; j < kThrottlingSeverityCount; ++j) {
+ hot_thresholds[j] = getFloatFromValue(values[j]);
+ if (!std::isnan(hot_thresholds[j])) {
+ if (hot_thresholds[j] < min) {
+ LOG(ERROR) << "Invalid "
+ << "Sensor[" << name << "]'s HotThreshold[j" << j
+ << "]: " << hot_thresholds[j] << " < " << min;
+ sensors_parsed->clear();
+ return false;
+ }
+ min = hot_thresholds[j];
+ }
+ LOG(INFO) << "Sensor[" << name << "]'s HotThreshold[" << j
+ << "]: " << hot_thresholds[j];
+ }
+ }
+
+ values = sensors[i]["HotHysteresis"];
+ if (!values.size()) {
+ LOG(INFO) << "Sensor[" << name << "]'s HotHysteresis, default all to 0.0";
+ } else if (values.size() != kThrottlingSeverityCount) {
+ LOG(ERROR) << "Invalid "
+ << "Sensor[" << name << "]'s HotHysteresis, count:" << values.size();
+ sensors_parsed->clear();
+ return false;
+ } else {
+ for (Json::Value::ArrayIndex j = 0; j < kThrottlingSeverityCount; ++j) {
+ hot_hysteresis[j] = getFloatFromValue(values[j]);
+ if (std::isnan(hot_hysteresis[j])) {
+ LOG(ERROR) << "Invalid "
+ << "Sensor[" << name << "]'s HotHysteresis: " << hot_hysteresis[j];
+ sensors_parsed->clear();
+ return false;
+ }
+ LOG(INFO) << "Sensor[" << name << "]'s HotHysteresis[" << j
+ << "]: " << hot_hysteresis[j];
+ }
+ }
+
+ for (Json::Value::ArrayIndex j = 0; j < (kThrottlingSeverityCount - 1); ++j) {
+ if (std::isnan(hot_thresholds[j])) {
+ continue;
+ }
+ for (auto k = j + 1; k < kThrottlingSeverityCount; ++k) {
+ if (std::isnan(hot_thresholds[k])) {
+ continue;
+ } else if (hot_thresholds[j] > (hot_thresholds[k] - hot_hysteresis[k])) {
+ LOG(ERROR) << "Sensor[" << name << "]'s hot threshold " << j
+ << " is overlapped";
+ sensors_parsed->clear();
+ return false;
+ } else {
+ break;
+ }
+ }
+ }
+
+ values = sensors[i]["ColdThreshold"];
+ if (!values.size()) {
+ LOG(INFO) << "Sensor[" << name << "]'s ColdThreshold, default all to NAN";
+ } else if (values.size() != kThrottlingSeverityCount) {
+ LOG(ERROR) << "Invalid "
+ << "Sensor[" << name << "]'s ColdThreshold count:" << values.size();
+ sensors_parsed->clear();
+ return false;
+ } else {
+ float max = std::numeric_limits<float>::max();
+ for (Json::Value::ArrayIndex j = 0; j < kThrottlingSeverityCount; ++j) {
+ cold_thresholds[j] = getFloatFromValue(values[j]);
+ if (!std::isnan(cold_thresholds[j])) {
+ if (cold_thresholds[j] > max) {
+ LOG(ERROR) << "Invalid "
+ << "Sensor[" << name << "]'s ColdThreshold[j" << j
+ << "]: " << cold_thresholds[j] << " > " << max;
+ sensors_parsed->clear();
+ return false;
+ }
+ max = cold_thresholds[j];
+ }
+ LOG(INFO) << "Sensor[" << name << "]'s ColdThreshold[" << j
+ << "]: " << cold_thresholds[j];
+ }
+ }
+
+ values = sensors[i]["ColdHysteresis"];
+ if (!values.size()) {
+ LOG(INFO) << "Sensor[" << name << "]'s ColdHysteresis, default all to 0.0";
+ } else if (values.size() != kThrottlingSeverityCount) {
+ LOG(ERROR) << "Invalid "
+ << "Sensor[" << name << "]'s ColdHysteresis count:" << values.size();
+ sensors_parsed->clear();
+ return false;
+ } else {
+ for (Json::Value::ArrayIndex j = 0; j < kThrottlingSeverityCount; ++j) {
+ cold_hysteresis[j] = getFloatFromValue(values[j]);
+ if (std::isnan(cold_hysteresis[j])) {
+ LOG(ERROR) << "Invalid "
+ << "Sensor[" << name << "]'s ColdHysteresis: " << cold_hysteresis[j];
+ sensors_parsed->clear();
+ return false;
+ }
+ LOG(INFO) << "Sensor[" << name << "]'s ColdHysteresis[" << j
+ << "]: " << cold_hysteresis[j];
+ }
+ }
+
+ for (Json::Value::ArrayIndex j = 0; j < (kThrottlingSeverityCount - 1); ++j) {
+ if (std::isnan(cold_thresholds[j])) {
+ continue;
+ }
+ for (auto k = j + 1; k < kThrottlingSeverityCount; ++k) {
+ if (std::isnan(cold_thresholds[k])) {
+ continue;
+ } else if (cold_thresholds[j] < (cold_thresholds[k] + cold_hysteresis[k])) {
+ LOG(ERROR) << "Sensor[" << name << "]'s cold threshold " << j
+ << " is overlapped";
+ sensors_parsed->clear();
+ return false;
+ } else {
+ break;
+ }
+ }
+ }
+
+ if (is_virtual_sensor) {
+ values = sensors[i]["Combination"];
+ if (values.size()) {
+ linked_sensors.reserve(values.size());
+ for (Json::Value::ArrayIndex j = 0; j < values.size(); ++j) {
+ linked_sensors.emplace_back(values[j].asString());
+ LOG(INFO) << "Sensor[" << name << "]'s combination[" << j
+ << "]: " << linked_sensors[j];
+ }
+ } else {
+ LOG(ERROR) << "Sensor[" << name << "] has no combination setting";
+ sensors_parsed->clear();
+ return false;
+ }
+
+ values = sensors[i]["Coefficient"];
+ if (values.size()) {
+ coefficients.reserve(values.size());
+ for (Json::Value::ArrayIndex j = 0; j < values.size(); ++j) {
+ coefficients.emplace_back(getFloatFromValue(values[j]));
+ LOG(INFO) << "Sensor[" << name << "]'s coefficient[" << j
+ << "]: " << coefficients[j];
+ }
+ } else {
+ LOG(ERROR) << "Sensor[" << name << "] has no coefficient setting";
+ sensors_parsed->clear();
+ return false;
+ }
+
+ if (linked_sensors.size() != coefficients.size()) {
+ LOG(ERROR) << "Sensor[" << name
+ << "]'s combination size is not matched with coefficient size";
+ sensors_parsed->clear();
+ return false;
+ }
+
+ if (!sensors[i]["Offset"].empty()) {
+ offset = sensors[i]["Offset"].asFloat();
+ }
+
+ trigger_sensor = sensors[i]["TriggerSensor"].asString();
+ if (sensors[i]["Formula"].asString().compare("COUNT_THRESHOLD") == 0) {
+ formula = FormulaOption::COUNT_THRESHOLD;
+ } else if (sensors[i]["Formula"].asString().compare("WEIGHTED_AVG") == 0) {
+ formula = FormulaOption::WEIGHTED_AVG;
+ } else if (sensors[i]["Formula"].asString().compare("MAXIMUM") == 0) {
+ formula = FormulaOption::MAXIMUM;
+ } else if (sensors[i]["Formula"].asString().compare("MINIMUM") == 0) {
+ formula = FormulaOption::MINIMUM;
+ } else {
+ LOG(ERROR) << "Sensor[" << name << "]'s Formula is invalid";
+ sensors_parsed->clear();
+ return false;
+ }
+ }
+
+ std::string temp_path;
+ if (!sensors[i]["TempPath"].empty()) {
+ temp_path = sensors[i]["TempPath"].asString();
+ LOG(INFO) << "Sensor[" << name << "]'s TempPath: " << temp_path;
+ }
+
+ float vr_threshold = NAN;
+ if (!sensors[i]["VrThreshold"].empty()) {
+ vr_threshold = getFloatFromValue(sensors[i]["VrThreshold"]);
+ LOG(INFO) << "Sensor[" << name << "]'s VrThreshold: " << vr_threshold;
+ }
+ float multiplier = sensors[i]["Multiplier"].asFloat();
+ LOG(INFO) << "Sensor[" << name << "]'s Multiplier: " << multiplier;
+
+ std::chrono::milliseconds polling_delay = kUeventPollTimeoutMs;
+ if (!sensors[i]["PollingDelay"].empty()) {
+ const auto value = getIntFromValue(sensors[i]["PollingDelay"]);
+ polling_delay = (value > 0) ? std::chrono::milliseconds(value)
+ : std::chrono::milliseconds::max();
+ }
+ LOG(INFO) << "Sensor[" << name << "]'s Polling delay: " << polling_delay.count();
+
+ std::chrono::milliseconds passive_delay = kMinPollIntervalMs;
+ if (!sensors[i]["PassiveDelay"].empty()) {
+ const auto value = getIntFromValue(sensors[i]["PassiveDelay"]);
+ passive_delay = (value > 0) ? std::chrono::milliseconds(value)
+ : std::chrono::milliseconds::max();
+ }
+ LOG(INFO) << "Sensor[" << name << "]'s Passive delay: " << passive_delay.count();
+
+ std::chrono::milliseconds time_resolution;
+ if (sensors[i]["TimeResolution"].empty()) {
+ time_resolution = kMinPollIntervalMs;
+ } else {
+ time_resolution =
+ std::chrono::milliseconds(getIntFromValue(sensors[i]["TimeResolution"]));
+ }
+ LOG(INFO) << "Sensor[" << name << "]'s Time resolution: " << time_resolution.count();
+
+ bool support_pid = false;
+ std::array<float, kThrottlingSeverityCount> k_po;
+ k_po.fill(0.0);
+ std::array<float, kThrottlingSeverityCount> k_pu;
+ k_pu.fill(0.0);
+ std::array<float, kThrottlingSeverityCount> k_i;
+ k_i.fill(0.0);
+ std::array<float, kThrottlingSeverityCount> k_d;
+ k_d.fill(0.0);
+ std::array<float, kThrottlingSeverityCount> i_max;
+ i_max.fill(NAN);
+ std::array<float, kThrottlingSeverityCount> max_alloc_power;
+ max_alloc_power.fill(NAN);
+ std::array<float, kThrottlingSeverityCount> min_alloc_power;
+ min_alloc_power.fill(NAN);
+ std::array<float, kThrottlingSeverityCount> s_power;
+ s_power.fill(NAN);
+ std::array<float, kThrottlingSeverityCount> i_cutoff;
+ i_cutoff.fill(NAN);
+ float err_integral_default = 0.0;
+
+ // Parse PID parameters
+ if (!sensors[i]["PIDInfo"].empty()) {
+ LOG(INFO) << "Start to parse"
+ << " Sensor[" << name << "]'s K_Po";
+ if (sensors[i]["PIDInfo"]["K_Po"].empty() ||
+ !getFloatFromJsonValues(sensors[i]["PIDInfo"]["K_Po"], &k_po, false, false)) {
+ LOG(ERROR) << "Sensor[" << name << "]: Failed to parse K_Po";
+ sensors_parsed->clear();
+ return false;
+ }
+ LOG(INFO) << "Start to parse"
+ << " Sensor[" << name << "]'s K_Pu";
+ if (sensors[i]["PIDInfo"]["K_Pu"].empty() ||
+ !getFloatFromJsonValues(sensors[i]["PIDInfo"]["K_Pu"], &k_pu, false, false)) {
+ LOG(ERROR) << "Sensor[" << name << "]: Failed to parse K_Pu";
+ sensors_parsed->clear();
+ return false;
+ }
+ LOG(INFO) << "Start to parse"
+ << " Sensor[" << name << "]'s K_I";
+ if (sensors[i]["PIDInfo"]["K_I"].empty() ||
+ !getFloatFromJsonValues(sensors[i]["PIDInfo"]["K_I"], &k_i, false, false)) {
+ LOG(ERROR) << "Sensor[" << name << "]: Failed to parse K_I";
+ sensors_parsed->clear();
+ return false;
+ }
+ LOG(INFO) << "Start to parse"
+ << " Sensor[" << name << "]'s K_D";
+ if (sensors[i]["PIDInfo"]["K_D"].empty() ||
+ !getFloatFromJsonValues(sensors[i]["PIDInfo"]["K_D"], &k_d, false, false)) {
+ LOG(ERROR) << "Sensor[" << name << "]: Failed to parse K_D";
+ sensors_parsed->clear();
+ return false;
+ }
+ LOG(INFO) << "Start to parse"
+ << " Sensor[" << name << "]'s I_Max";
+ if (sensors[i]["PIDInfo"]["I_Max"].empty() ||
+ !getFloatFromJsonValues(sensors[i]["PIDInfo"]["I_Max"], &i_max, false, false)) {
+ LOG(ERROR) << "Sensor[" << name << "]: Failed to parse I_Max";
+ sensors_parsed->clear();
+ return false;
+ }
+ LOG(INFO) << "Start to parse"
+ << " Sensor[" << name << "]'s MaxAllocPower";
+ if (sensors[i]["PIDInfo"]["MaxAllocPower"].empty() ||
+ !getFloatFromJsonValues(sensors[i]["PIDInfo"]["MaxAllocPower"], &max_alloc_power,
+ false, true)) {
+ LOG(ERROR) << "Sensor[" << name << "]: Failed to parse MaxAllocPower";
+ sensors_parsed->clear();
+ return false;
+ }
+ LOG(INFO) << "Start to parse"
+ << " Sensor[" << name << "]'s MinAllocPower";
+ if (sensors[i]["PIDInfo"]["MinAllocPower"].empty() ||
+ !getFloatFromJsonValues(sensors[i]["PIDInfo"]["MinAllocPower"], &min_alloc_power,
+ false, true)) {
+ LOG(ERROR) << "Sensor[" << name << "]: Failed to parse MinAllocPower";
+ sensors_parsed->clear();
+ return false;
+ }
+ LOG(INFO) << "Start to parse"
+ << " Sensor[" << name << "]'s S_Power";
+ if (sensors[i]["PIDInfo"]["S_Power"].empty() ||
+ !getFloatFromJsonValues(sensors[i]["PIDInfo"]["S_Power"], &s_power, false, true)) {
+ LOG(ERROR) << "Sensor[" << name << "]: Failed to parse S_Power";
+ sensors_parsed->clear();
+ return false;
+ }
+ LOG(INFO) << "Start to parse"
+ << " Sensor[" << name << "]'s I_Cutoff";
+ if (sensors[i]["PIDInfo"]["I_Cutoff"].empty() ||
+ !getFloatFromJsonValues(sensors[i]["PIDInfo"]["I_Cutoff"], &i_cutoff, false,
+ false)) {
+ LOG(ERROR) << "Sensor[" << name << "]: Failed to parse I_Cutoff";
+ sensors_parsed->clear();
+ return false;
+ }
+ LOG(INFO) << "Start to parse"
+ << " Sensor[" << name << "]'s E_Integral_Default";
+ err_integral_default = getFloatFromValue(sensors[i]["PIDInfo"]["E_Integral_Default"]);
+ LOG(INFO) << "Sensor[" << name << "]'s E_Integral_Default: " << err_integral_default;
+ // Confirm we have at least one valid PID combination
+ bool valid_pid_combination = false;
+ for (Json::Value::ArrayIndex j = 0; j < kThrottlingSeverityCount; ++j) {
+ if (!std::isnan(s_power[j])) {
+ if (std::isnan(k_po[j]) || std::isnan(k_pu[j]) || std::isnan(k_i[j]) ||
+ std::isnan(k_d[j]) || std::isnan(i_max[j]) ||
+ std::isnan(max_alloc_power[j]) || std::isnan(min_alloc_power[j]) ||
+ std::isnan(i_cutoff[j])) {
+ valid_pid_combination = false;
+ break;
+ } else {
+ valid_pid_combination = true;
+ }
+ }
+ }
+ if (!valid_pid_combination) {
+ LOG(ERROR) << "Sensor[" << name << "]: Invalid PID parameters combinations";
+ sensors_parsed->clear();
+ return false;
+ } else {
+ support_pid = true;
+ }
+ }
+
+ // Parse binded cooling device
+ bool support_hard_limit = false;
+ std::unordered_map<std::string, BindedCdevInfo> binded_cdev_info_map;
+ values = sensors[i]["BindedCdevInfo"];
+ for (Json::Value::ArrayIndex j = 0; j < values.size(); ++j) {
+ Json::Value sub_values;
+ const std::string &cdev_name = values[j]["CdevRequest"].asString();
+ ThrottlingArray cdev_weight_for_pid;
+ cdev_weight_for_pid.fill(NAN);
+ CdevArray cdev_ceiling;
+ cdev_ceiling.fill(std::numeric_limits<int>::max());
+ int max_release_step = std::numeric_limits<int>::max();
+ int max_throttle_step = std::numeric_limits<int>::max();
+ if (support_pid) {
+ if (!values[j]["CdevWeightForPID"].empty()) {
+ LOG(INFO) << "Sensor[" << name << "]: Star to parse " << cdev_name
+ << "'s CdevWeightForPID";
+ if (!getFloatFromJsonValues(values[j]["CdevWeightForPID"], &cdev_weight_for_pid,
+ false, false)) {
+ LOG(ERROR) << "Failed to parse CdevWeightForPID";
+ sensors_parsed->clear();
+ return false;
+ }
+ }
+ if (!values[j]["CdevCeiling"].empty()) {
+ LOG(INFO) << "Sensor[" << name
+ << "]: Start to parse CdevCeiling: " << cdev_name;
+ if (!getIntFromJsonValues(values[j]["CdevCeiling"], &cdev_ceiling, false,
+ false)) {
+ LOG(ERROR) << "Failed to parse CdevCeiling";
+ sensors_parsed->clear();
+ return false;
+ }
+ }
+ if (!values[j]["MaxReleaseStep"].empty()) {
+ max_release_step = getIntFromValue(values[j]["MaxReleaseStep"]);
+ if (max_release_step < 0) {
+ LOG(ERROR) << "Sensor[" << name << "]'s " << cdev_name
+ << " MaxReleaseStep: " << max_release_step;
+ sensors_parsed->clear();
+ return false;
+ } else {
+ LOG(INFO) << "Sensor[" << name << "]'s " << cdev_name
+ << " MaxReleaseStep: " << max_release_step;
+ }
+ }
+ if (!values[j]["MaxThrottleStep"].empty()) {
+ max_throttle_step = getIntFromValue(values[j]["MaxThrottleStep"]);
+ if (max_throttle_step < 0) {
+ LOG(ERROR) << "Sensor[" << name << "]'s " << cdev_name
+ << " MaxThrottleStep: " << max_throttle_step;
+ sensors_parsed->clear();
+ return false;
+ } else {
+ LOG(INFO) << "Sensor[" << name << "]'s " << cdev_name
+ << " MaxThrottleStep: " << max_throttle_step;
+ }
+ }
+ }
+ CdevArray limit_info;
+ limit_info.fill(0);
+ ThrottlingArray power_thresholds;
+ power_thresholds.fill(NAN);
+
+ ReleaseLogic release_logic = ReleaseLogic::NONE;
+
+ sub_values = values[j]["LimitInfo"];
+ if (sub_values.size()) {
+ LOG(INFO) << "Sensor[" << name << "]: Start to parse LimitInfo: " << cdev_name;
+ if (!getIntFromJsonValues(sub_values, &limit_info, false, false)) {
+ LOG(ERROR) << "Failed to parse LimitInfo";
+ sensors_parsed->clear();
+ return false;
+ }
+ support_hard_limit = true;
+ }
+
+ // Parse linked power info
+ std::string power_rail;
+ bool high_power_check = false;
+ bool throttling_with_power_link = false;
+ CdevArray cdev_floor_with_power_link;
+ cdev_floor_with_power_link.fill(0);
+
+ const bool power_link_disabled =
+ android::base::GetBoolProperty(kPowerLinkDisabledProperty.data(), false);
+ if (!power_link_disabled) {
+ power_rail = values[j]["BindedPowerRail"].asString();
+
+ if (values[j]["HighPowerCheck"].asBool()) {
+ high_power_check = true;
+ }
+ LOG(INFO) << "Highpowercheck: " << std::boolalpha << high_power_check;
+
+ if (values[j]["ThrottlingWithPowerLink"].asBool()) {
+ throttling_with_power_link = true;
+ }
+ LOG(INFO) << "ThrottlingwithPowerLink: " << std::boolalpha
+ << throttling_with_power_link;
+
+ sub_values = values[j]["CdevFloorWithPowerLink"];
+ if (sub_values.size()) {
+ LOG(INFO) << "Sensor[" << name << "]: Start to parse " << cdev_name
+ << "'s CdevFloorWithPowerLink";
+ if (!getIntFromJsonValues(sub_values, &cdev_floor_with_power_link, false,
+ false)) {
+ LOG(ERROR) << "Failed to parse CdevFloor";
+ sensors_parsed->clear();
+ return false;
+ }
+ }
+ sub_values = values[j]["PowerThreshold"];
+ if (sub_values.size()) {
+ LOG(INFO) << "Sensor[" << name << "]: Start to parse " << cdev_name
+ << "'s PowerThreshold";
+ if (!getFloatFromJsonValues(sub_values, &power_thresholds, false, false)) {
+ LOG(ERROR) << "Failed to parse power thresholds";
+ sensors_parsed->clear();
+ return false;
+ }
+ if (values[j]["ReleaseLogic"].asString() == "INCREASE") {
+ release_logic = ReleaseLogic::INCREASE;
+ LOG(INFO) << "Release logic: INCREASE";
+ } else if (values[j]["ReleaseLogic"].asString() == "DECREASE") {
+ release_logic = ReleaseLogic::DECREASE;
+ LOG(INFO) << "Release logic: DECREASE";
+ } else if (values[j]["ReleaseLogic"].asString() == "STEPWISE") {
+ release_logic = ReleaseLogic::STEPWISE;
+ LOG(INFO) << "Release logic: STEPWISE";
+ } else if (values[j]["ReleaseLogic"].asString() == "RELEASE_TO_FLOOR") {
+ release_logic = ReleaseLogic::RELEASE_TO_FLOOR;
+ LOG(INFO) << "Release logic: RELEASE_TO_FLOOR";
+ } else {
+ LOG(ERROR) << "Release logic is invalid";
+ sensors_parsed->clear();
+ return false;
+ }
+ }
+ }
+
+ binded_cdev_info_map[cdev_name] = {
+ .limit_info = limit_info,
+ .power_thresholds = power_thresholds,
+ .release_logic = release_logic,
+ .high_power_check = high_power_check,
+ .throttling_with_power_link = throttling_with_power_link,
+ .cdev_weight_for_pid = cdev_weight_for_pid,
+ .cdev_ceiling = cdev_ceiling,
+ .max_release_step = max_release_step,
+ .max_throttle_step = max_throttle_step,
+ .cdev_floor_with_power_link = cdev_floor_with_power_link,
+ .power_rail = power_rail,
+ };
+ }
+
+ if (is_hidden && send_cb) {
+ LOG(ERROR) << "is_hidden and send_cb cannot be enabled together";
+ sensors_parsed->clear();
+ return false;
+ }
+
+ bool is_watch = (send_cb | send_powerhint | support_pid | support_hard_limit);
+ LOG(INFO) << "Sensor[" << name << "]'s is_watch: " << std::boolalpha << is_watch;
+
+ std::unique_ptr<VirtualSensorInfo> virtual_sensor_info;
+ if (is_virtual_sensor) {
+ virtual_sensor_info.reset(new VirtualSensorInfo{linked_sensors, coefficients, offset,
+ trigger_sensor, formula});
+ }
+
+ std::shared_ptr<ThrottlingInfo> throttling_info(
+ new ThrottlingInfo{k_po, k_pu, k_i, k_d, i_max, max_alloc_power, min_alloc_power,
+ s_power, i_cutoff, err_integral_default, binded_cdev_info_map});
+
+ (*sensors_parsed)[name] = {
+ .type = sensor_type,
+ .hot_thresholds = hot_thresholds,
+ .cold_thresholds = cold_thresholds,
+ .hot_hysteresis = hot_hysteresis,
+ .cold_hysteresis = cold_hysteresis,
+ .temp_path = temp_path,
+ .vr_threshold = vr_threshold,
+ .multiplier = multiplier,
+ .polling_delay = polling_delay,
+ .passive_delay = passive_delay,
+ .time_resolution = time_resolution,
+ .send_cb = send_cb,
+ .send_powerhint = send_powerhint,
+ .is_watch = is_watch,
+ .is_hidden = is_hidden,
+ .virtual_sensor_info = std::move(virtual_sensor_info),
+ .throttling_info = std::move(throttling_info),
+ };
+
+ ++total_parsed;
+ }
+ LOG(INFO) << total_parsed << " Sensors parsed successfully";
+ return true;
+}
+
+bool ParseCoolingDevice(std::string_view config_path,
+ std::unordered_map<std::string, CdevInfo> *cooling_devices_parsed) {
+ std::string json_doc;
+ if (!android::base::ReadFileToString(config_path.data(), &json_doc)) {
+ LOG(ERROR) << "Failed to read JSON config from " << config_path;
+ return false;
+ }
+ Json::Value root;
+ Json::CharReaderBuilder builder;
+ std::unique_ptr<Json::CharReader> reader(builder.newCharReader());
+ std::string errorMessage;
+
+ if (!reader->parse(&*json_doc.begin(), &*json_doc.end(), &root, &errorMessage)) {
+ LOG(ERROR) << "Failed to parse JSON config: " << errorMessage;
+ return false;
+ }
+
+ Json::Value cooling_devices = root["CoolingDevices"];
+ std::size_t total_parsed = 0;
+ std::unordered_set<std::string> cooling_devices_name_parsed;
+
+ for (Json::Value::ArrayIndex i = 0; i < cooling_devices.size(); ++i) {
+ const std::string &name = cooling_devices[i]["Name"].asString();
+ LOG(INFO) << "CoolingDevice[" << i << "]'s Name: " << name;
+ if (name.empty()) {
+ LOG(ERROR) << "Failed to read "
+ << "CoolingDevice[" << i << "]'s Name";
+ cooling_devices_parsed->clear();
+ return false;
+ }
+
+ auto result = cooling_devices_name_parsed.insert(name.data());
+ if (!result.second) {
+ LOG(ERROR) << "Duplicate CoolingDevice[" << i << "]'s Name";
+ cooling_devices_parsed->clear();
+ return false;
+ }
+
+ std::string cooling_device_type_str = cooling_devices[i]["Type"].asString();
+ LOG(INFO) << "CoolingDevice[" << name << "]'s Type: " << cooling_device_type_str;
+ CoolingType cooling_device_type;
+
+ if (!getTypeFromString(cooling_device_type_str, &cooling_device_type)) {
+ LOG(ERROR) << "Invalid "
+ << "CoolingDevice[" << name << "]'s Type: " << cooling_device_type_str;
+ cooling_devices_parsed->clear();
+ return false;
+ }
+
+ const std::string &read_path = cooling_devices[i]["ReadPath"].asString();
+ LOG(INFO) << "Cdev Read Path: " << (read_path.empty() ? "default" : read_path);
+
+ const std::string &write_path = cooling_devices[i]["WritePath"].asString();
+ LOG(INFO) << "Cdev Write Path: " << (write_path.empty() ? "default" : write_path);
+
+ std::vector<float> state2power;
+ Json::Value values = cooling_devices[i]["State2Power"];
+ if (values.size()) {
+ state2power.reserve(values.size());
+ for (Json::Value::ArrayIndex j = 0; j < values.size(); ++j) {
+ state2power.emplace_back(getFloatFromValue(values[j]));
+ LOG(INFO) << "Cooling device[" << name << "]'s Power2State[" << j
+ << "]: " << state2power[j];
+ }
+ } else {
+ LOG(INFO) << "CoolingDevice[" << i << "]'s Name: " << name
+ << " does not support State2Power";
+ }
+
+ const std::string &power_rail = cooling_devices[i]["PowerRail"].asString();
+ LOG(INFO) << "Cooling device power rail : " << power_rail;
+
+ (*cooling_devices_parsed)[name] = {
+ .type = cooling_device_type,
+ .read_path = read_path,
+ .write_path = write_path,
+ .state2power = state2power,
+ };
+ ++total_parsed;
+ }
+ LOG(INFO) << total_parsed << " CoolingDevices parsed successfully";
+ return true;
+}
+
+bool ParsePowerRailInfo(std::string_view config_path,
+ std::unordered_map<std::string, PowerRailInfo> *power_rails_parsed) {
+ std::string json_doc;
+ if (!android::base::ReadFileToString(config_path.data(), &json_doc)) {
+ LOG(ERROR) << "Failed to read JSON config from " << config_path;
+ return false;
+ }
+ Json::Value root;
+ Json::CharReaderBuilder builder;
+ std::unique_ptr<Json::CharReader> reader(builder.newCharReader());
+ std::string errorMessage;
+
+ if (!reader->parse(&*json_doc.begin(), &*json_doc.end(), &root, &errorMessage)) {
+ LOG(ERROR) << "Failed to parse JSON config: " << errorMessage;
+ return false;
+ }
+
+ Json::Value power_rails = root["PowerRails"];
+ std::size_t total_parsed = 0;
+ std::unordered_set<std::string> power_rails_name_parsed;
+
+ for (Json::Value::ArrayIndex i = 0; i < power_rails.size(); ++i) {
+ const std::string &name = power_rails[i]["Name"].asString();
+ LOG(INFO) << "PowerRail[" << i << "]'s Name: " << name;
+ if (name.empty()) {
+ LOG(ERROR) << "Failed to read "
+ << "PowerRail[" << i << "]'s Name";
+ power_rails_parsed->clear();
+ return false;
+ }
+
+ std::string rail;
+ if (power_rails[i]["Rail"].empty()) {
+ rail = name;
+ } else {
+ rail = power_rails[i]["Rail"].asString();
+ }
+ LOG(INFO) << "PowerRail[" << i << "]'s Rail: " << rail;
+
+ std::vector<std::string> linked_power_rails;
+ std::vector<float> coefficients;
+ float offset = 0;
+ FormulaOption formula = FormulaOption::COUNT_THRESHOLD;
+ bool is_virtual_power_rail = false;
+ Json::Value values;
+ int power_sample_count = 0;
+ std::chrono::milliseconds power_sample_delay;
+
+ if (!power_rails[i]["VirtualRails"].empty() && power_rails[i]["VirtualRails"].isBool()) {
+ is_virtual_power_rail = power_rails[i]["VirtualRails"].asBool();
+ LOG(INFO) << "PowerRails[" << name << "]'s VirtualRail, set to 'true'";
+ }
+
+ if (is_virtual_power_rail) {
+ values = power_rails[i]["Combination"];
+ if (values.size()) {
+ linked_power_rails.reserve(values.size());
+ for (Json::Value::ArrayIndex j = 0; j < values.size(); ++j) {
+ linked_power_rails.emplace_back(values[j].asString());
+ LOG(INFO) << "PowerRail[" << name << "]'s combination[" << j
+ << "]: " << linked_power_rails[j];
+ }
+ } else {
+ LOG(ERROR) << "PowerRails[" << name << "] has no combination for VirtualRail";
+ power_rails_parsed->clear();
+ return false;
+ }
+
+ values = power_rails[i]["Coefficient"];
+ if (values.size()) {
+ coefficients.reserve(values.size());
+ for (Json::Value::ArrayIndex j = 0; j < values.size(); ++j) {
+ coefficients.emplace_back(getFloatFromValue(values[j]));
+ LOG(INFO) << "PowerRail[" << name << "]'s coefficient[" << j
+ << "]: " << coefficients[j];
+ }
+ } else {
+ LOG(ERROR) << "PowerRails[" << name << "] has no coefficient for VirtualRail";
+ power_rails_parsed->clear();
+ return false;
+ }
+
+ if (linked_power_rails.size() != coefficients.size()) {
+ LOG(ERROR) << "PowerRails[" << name
+ << "]'s combination size is not matched with coefficient size";
+ power_rails_parsed->clear();
+ return false;
+ }
+
+ if (!power_rails[i]["Offset"].empty()) {
+ offset = power_rails[i]["Offset"].asFloat();
+ }
+
+ if (linked_power_rails.size() != coefficients.size()) {
+ LOG(ERROR) << "PowerRails[" << name
+ << "]'s combination size is not matched with coefficient size";
+ power_rails_parsed->clear();
+ return false;
+ }
+
+ if (power_rails[i]["Formula"].asString().compare("COUNT_THRESHOLD") == 0) {
+ formula = FormulaOption::COUNT_THRESHOLD;
+ } else if (power_rails[i]["Formula"].asString().compare("WEIGHTED_AVG") == 0) {
+ formula = FormulaOption::WEIGHTED_AVG;
+ } else if (power_rails[i]["Formula"].asString().compare("MAXIMUM") == 0) {
+ formula = FormulaOption::MAXIMUM;
+ } else if (power_rails[i]["Formula"].asString().compare("MINIMUM") == 0) {
+ formula = FormulaOption::MINIMUM;
+ } else {
+ LOG(ERROR) << "PowerRails[" << name << "]'s Formula is invalid";
+ power_rails_parsed->clear();
+ return false;
+ }
+ }
+
+ std::unique_ptr<VirtualPowerRailInfo> virtual_power_rail_info;
+ if (is_virtual_power_rail) {
+ virtual_power_rail_info.reset(
+ new VirtualPowerRailInfo{linked_power_rails, coefficients, offset, formula});
+ }
+
+ power_sample_count = power_rails[i]["PowerSampleCount"].asInt();
+ LOG(INFO) << "Power sample Count: " << power_sample_count;
+
+ if (!power_rails[i]["PowerSampleDelay"]) {
+ power_sample_delay = std::chrono::milliseconds::max();
+ } else {
+ power_sample_delay =
+ std::chrono::milliseconds(getIntFromValue(power_rails[i]["PowerSampleDelay"]));
+ }
+
+ (*power_rails_parsed)[name] = {
+ .rail = rail,
+ .power_sample_count = power_sample_count,
+ .power_sample_delay = power_sample_delay,
+ .virtual_power_rail_info = std::move(virtual_power_rail_info),
+ };
+ ++total_parsed;
+ }
+ LOG(INFO) << total_parsed << " PowerRails parsed successfully";
+ return true;
+}
+
+} // namespace implementation
+} // namespace V2_0
+} // namespace thermal
+} // namespace hardware
+} // namespace android
diff --git a/thermal/pid_1_0/utils/thermal_info.h b/thermal/pid_1_0/utils/thermal_info.h
new file mode 100644
index 0000000..84d0402
--- /dev/null
+++ b/thermal/pid_1_0/utils/thermal_info.h
@@ -0,0 +1,148 @@
+/*
+ * 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 <android/hardware/thermal/2.0/IThermal.h>
+
+#include <string>
+#include <unordered_map>
+
+namespace android {
+namespace hardware {
+namespace thermal {
+namespace V2_0 {
+namespace implementation {
+
+using ::android::hardware::hidl_enum_range;
+using CoolingType_2_0 = ::android::hardware::thermal::V2_0::CoolingType;
+using TemperatureType_2_0 = ::android::hardware::thermal::V2_0::TemperatureType;
+using ::android::hardware::thermal::V2_0::ThrottlingSeverity;
+constexpr size_t kThrottlingSeverityCount = std::distance(
+ hidl_enum_range<ThrottlingSeverity>().begin(), hidl_enum_range<ThrottlingSeverity>().end());
+using ThrottlingArray = std::array<float, static_cast<size_t>(kThrottlingSeverityCount)>;
+using CdevArray = std::array<int, static_cast<size_t>(kThrottlingSeverityCount)>;
+constexpr std::chrono::milliseconds kMinPollIntervalMs = std::chrono::milliseconds(2000);
+constexpr std::chrono::milliseconds kUeventPollTimeoutMs = std::chrono::milliseconds(300000);
+
+enum FormulaOption : uint32_t {
+ COUNT_THRESHOLD = 0,
+ WEIGHTED_AVG,
+ MAXIMUM,
+ MINIMUM,
+};
+
+struct VirtualSensorInfo {
+ std::vector<std::string> linked_sensors;
+ std::vector<float> coefficients;
+ float offset;
+ std::string trigger_sensor;
+ FormulaOption formula;
+};
+
+struct VirtualPowerRailInfo {
+ std::vector<std::string> linked_power_rails;
+ std::vector<float> coefficients;
+ float offset;
+ FormulaOption formula;
+};
+
+// The method when the ODPM power is lower than threshold
+enum ReleaseLogic : uint32_t {
+ INCREASE = 0, // Increase throttling by step
+ DECREASE, // Decrease throttling by step
+ STEPWISE, // Support both increase and decrease logix
+ RELEASE_TO_FLOOR, // Release throttling to floor directly
+ NONE,
+};
+
+struct BindedCdevInfo {
+ CdevArray limit_info;
+ ThrottlingArray power_thresholds;
+ ReleaseLogic release_logic;
+ ThrottlingArray cdev_weight_for_pid;
+ CdevArray cdev_ceiling;
+ int max_release_step;
+ int max_throttle_step;
+ CdevArray cdev_floor_with_power_link;
+ std::string power_rail;
+ // The flag for activate release logic when power is higher than power threshold
+ bool high_power_check;
+ // The flag for only triggering throttling until all power samples are collected
+ bool throttling_with_power_link;
+};
+
+struct ThrottlingInfo {
+ ThrottlingArray k_po;
+ ThrottlingArray k_pu;
+ ThrottlingArray k_i;
+ ThrottlingArray k_d;
+ ThrottlingArray i_max;
+ ThrottlingArray max_alloc_power;
+ ThrottlingArray min_alloc_power;
+ ThrottlingArray s_power;
+ ThrottlingArray i_cutoff;
+ float err_integral_default;
+ std::unordered_map<std::string, BindedCdevInfo> binded_cdev_info_map;
+};
+
+struct SensorInfo {
+ TemperatureType_2_0 type;
+ ThrottlingArray hot_thresholds;
+ ThrottlingArray cold_thresholds;
+ ThrottlingArray hot_hysteresis;
+ ThrottlingArray cold_hysteresis;
+ std::string temp_path;
+ float vr_threshold;
+ float multiplier;
+ std::chrono::milliseconds polling_delay;
+ std::chrono::milliseconds passive_delay;
+ std::chrono::milliseconds time_resolution;
+ bool send_cb;
+ bool send_powerhint;
+ bool is_watch;
+ bool is_hidden;
+ std::unique_ptr<VirtualSensorInfo> virtual_sensor_info;
+ std::shared_ptr<ThrottlingInfo> throttling_info;
+};
+
+struct CdevInfo {
+ CoolingType_2_0 type;
+ std::string read_path;
+ std::string write_path;
+ std::vector<float> state2power;
+ int max_state;
+};
+
+struct PowerRailInfo {
+ std::string rail;
+ int power_sample_count;
+ std::chrono::milliseconds power_sample_delay;
+ std::unique_ptr<VirtualPowerRailInfo> virtual_power_rail_info;
+};
+
+bool ParseSensorInfo(std::string_view config_path,
+ std::unordered_map<std::string, SensorInfo> *sensors_parsed);
+bool ParseCoolingDevice(std::string_view config_path,
+ std::unordered_map<std::string, CdevInfo> *cooling_device_parsed);
+bool ParsePowerRailInfo(std::string_view config_path,
+ std::unordered_map<std::string, PowerRailInfo> *power_rail_parsed);
+
+} // namespace implementation
+} // namespace V2_0
+} // namespace thermal
+} // namespace hardware
+} // namespace android
diff --git a/thermal/pid_1_0/utils/thermal_throttling.cpp b/thermal/pid_1_0/utils/thermal_throttling.cpp
index f158b6c..53075ce 100644
--- a/thermal/pid_1_0/utils/thermal_throttling.cpp
+++ b/thermal/pid_1_0/utils/thermal_throttling.cpp
@@ -29,9 +29,6 @@
#include <thread>
#include <vector>
-#include "../../utils/power_files.h"
-#include "../../utils/thermal_info.h"
-
namespace android {
namespace hardware {
namespace thermal {
diff --git a/thermal/pid_1_0/utils/thermal_throttling.h b/thermal/pid_1_0/utils/thermal_throttling.h
index e94f25d..6314646 100644
--- a/thermal/pid_1_0/utils/thermal_throttling.h
+++ b/thermal/pid_1_0/utils/thermal_throttling.h
@@ -24,8 +24,8 @@
#include <unordered_map>
#include <unordered_set>
-#include "../../utils/power_files.h"
-#include "../../utils/thermal_info.h"
+#include "power_files.h"
+#include "thermal_info.h"
namespace android {
namespace hardware {
diff --git a/thermal/pid_1_0/utils/thermal_watcher.cpp b/thermal/pid_1_0/utils/thermal_watcher.cpp
new file mode 100644
index 0000000..7ff1d89
--- /dev/null
+++ b/thermal/pid_1_0/utils/thermal_watcher.cpp
@@ -0,0 +1,535 @@
+/*
+ * 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 ATRACE_TAG (ATRACE_TAG_THERMAL | ATRACE_TAG_HAL)
+
+#include "thermal_watcher.h"
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <cutils/uevent.h>
+#include <dirent.h>
+#include <linux/netlink.h>
+#include <linux/thermal.h>
+#include <sys/inotify.h>
+#include <sys/resource.h>
+#include <sys/types.h>
+#include <utils/Trace.h>
+
+#include <chrono>
+#include <fstream>
+
+#include "../thermal-helper.h"
+
+namespace android {
+namespace hardware {
+namespace thermal {
+namespace V2_0 {
+namespace implementation {
+
+namespace {
+
+static int nlErrorHandle(struct sockaddr_nl *nla, struct nlmsgerr *err, void *arg) {
+ int *ret = reinterpret_cast<int *>(arg);
+ *ret = err->error;
+ LOG(ERROR) << __func__ << "nl_groups: " << nla->nl_groups << ", nl_pid: " << nla->nl_pid;
+
+ return NL_STOP;
+}
+
+static int nlFinishHandle(struct nl_msg *msg, void *arg) {
+ int *ret = reinterpret_cast<int *>(arg);
+ *ret = 1;
+ struct nlmsghdr *nlh = nlmsg_hdr(msg);
+
+ LOG(VERBOSE) << __func__ << ": nlmsg type: " << nlh->nlmsg_type;
+
+ return NL_OK;
+}
+
+static int nlAckHandle(struct nl_msg *msg, void *arg) {
+ int *ret = reinterpret_cast<int *>(arg);
+ *ret = 1;
+ struct nlmsghdr *nlh = nlmsg_hdr(msg);
+
+ LOG(VERBOSE) << __func__ << ": nlmsg type: " << nlh->nlmsg_type;
+
+ return NL_OK;
+}
+
+static int nlSeqCheckHandle(struct nl_msg *msg, void *arg) {
+ int *ret = reinterpret_cast<int *>(arg);
+ *ret = 1;
+ struct nlmsghdr *nlh = nlmsg_hdr(msg);
+
+ LOG(VERBOSE) << __func__ << ": nlmsg type: " << nlh->nlmsg_type;
+
+ return NL_OK;
+}
+
+struct HandlerArgs {
+ const char *group;
+ int id;
+};
+
+static int nlSendMsg(struct nl_sock *sock, struct nl_msg *msg,
+ int (*rx_handler)(struct nl_msg *, void *), void *data) {
+ int err, done = 0;
+
+ std::unique_ptr<nl_cb, decltype(&nl_cb_put)> cb(nl_cb_alloc(NL_CB_DEFAULT), nl_cb_put);
+
+ err = nl_send_auto_complete(sock, msg);
+ if (err < 0)
+ return err;
+
+ err = 0;
+ nl_cb_err(cb.get(), NL_CB_CUSTOM, nlErrorHandle, &err);
+ nl_cb_set(cb.get(), NL_CB_FINISH, NL_CB_CUSTOM, nlFinishHandle, &done);
+ nl_cb_set(cb.get(), NL_CB_ACK, NL_CB_CUSTOM, nlAckHandle, &done);
+
+ if (rx_handler != NULL)
+ nl_cb_set(cb.get(), NL_CB_VALID, NL_CB_CUSTOM, rx_handler, data);
+
+ while (err == 0 && done == 0) nl_recvmsgs(sock, cb.get());
+
+ return err;
+}
+
+static int nlFamilyHandle(struct nl_msg *msg, void *arg) {
+ struct HandlerArgs *grp = reinterpret_cast<struct HandlerArgs *>(arg);
+ struct nlattr *tb[CTRL_ATTR_MAX + 1];
+ struct genlmsghdr *gnlh = (struct genlmsghdr *)nlmsg_data(nlmsg_hdr(msg));
+ struct nlattr *mcgrp;
+ int rem_mcgrp;
+
+ nla_parse(tb, CTRL_ATTR_MAX, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), NULL);
+
+ if (!tb[CTRL_ATTR_MCAST_GROUPS]) {
+ LOG(ERROR) << __func__ << "Multicast group not found";
+ return -1;
+ }
+
+ nla_for_each_nested(mcgrp, tb[CTRL_ATTR_MCAST_GROUPS], rem_mcgrp) {
+ struct nlattr *tb_mcgrp[CTRL_ATTR_MCAST_GRP_MAX + 1];
+
+ nla_parse(tb_mcgrp, CTRL_ATTR_MCAST_GRP_MAX, reinterpret_cast<nlattr *>(nla_data(mcgrp)),
+ nla_len(mcgrp), NULL);
+
+ if (!tb_mcgrp[CTRL_ATTR_MCAST_GRP_NAME] || !tb_mcgrp[CTRL_ATTR_MCAST_GRP_ID])
+ continue;
+
+ if (strncmp(reinterpret_cast<char *>(nla_data(tb_mcgrp[CTRL_ATTR_MCAST_GRP_NAME])),
+ grp->group, nla_len(tb_mcgrp[CTRL_ATTR_MCAST_GRP_NAME])) != 0)
+ continue;
+
+ grp->id = nla_get_u32(tb_mcgrp[CTRL_ATTR_MCAST_GRP_ID]);
+
+ break;
+ }
+
+ return 0;
+}
+
+static int nlGetMulticastId(struct nl_sock *sock, const char *family, const char *group) {
+ int err = 0, ctrlid;
+ struct HandlerArgs grp = {
+ .group = group,
+ .id = -ENOENT,
+ };
+
+ std::unique_ptr<nl_msg, decltype(&nlmsg_free)> msg(nlmsg_alloc(), nlmsg_free);
+
+ ctrlid = genl_ctrl_resolve(sock, "nlctrl");
+
+ genlmsg_put(msg.get(), 0, 0, ctrlid, 0, 0, CTRL_CMD_GETFAMILY, 0);
+
+ nla_put_string(msg.get(), CTRL_ATTR_FAMILY_NAME, family);
+
+ err = nlSendMsg(sock, msg.get(), nlFamilyHandle, &grp);
+ if (err)
+ return err;
+
+ err = grp.id;
+ LOG(INFO) << group << " multicast_id: " << grp.id;
+
+ return err;
+}
+
+static bool socketAddMembership(struct nl_sock *sock, const char *group) {
+ int mcid = nlGetMulticastId(sock, THERMAL_GENL_FAMILY_NAME, group);
+ if (mcid < 0) {
+ LOG(ERROR) << "Failed to get multicast id: " << group;
+ return false;
+ }
+
+ if (nl_socket_add_membership(sock, mcid)) {
+ LOG(ERROR) << "Failed to add netlink socket membership: " << group;
+ return false;
+ }
+
+ LOG(INFO) << "Added netlink socket membership: " << group;
+ return true;
+}
+
+static int handleEvent(struct nl_msg *n, void *arg) {
+ struct nlmsghdr *nlh = nlmsg_hdr(n);
+ struct genlmsghdr *glh = genlmsg_hdr(nlh);
+ struct nlattr *attrs[THERMAL_GENL_ATTR_MAX + 1];
+ int *tz_id = reinterpret_cast<int *>(arg);
+
+ genlmsg_parse(nlh, 0, attrs, THERMAL_GENL_ATTR_MAX, NULL);
+
+ if (glh->cmd == THERMAL_GENL_EVENT_TZ_TRIP_UP) {
+ LOG(INFO) << "THERMAL_GENL_EVENT_TZ_TRIP_UP";
+ if (attrs[THERMAL_GENL_ATTR_TZ_ID]) {
+ LOG(INFO) << "Thermal zone id: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]);
+ *tz_id = nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]);
+ }
+ if (attrs[THERMAL_GENL_ATTR_TZ_TRIP_ID])
+ LOG(INFO) << "Thermal zone trip id: "
+ << nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TRIP_ID]);
+ }
+
+ if (glh->cmd == THERMAL_GENL_EVENT_TZ_TRIP_DOWN) {
+ LOG(INFO) << "THERMAL_GENL_EVENT_TZ_TRIP_DOWN";
+ if (attrs[THERMAL_GENL_ATTR_TZ_ID]) {
+ LOG(INFO) << "Thermal zone id: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]);
+ *tz_id = nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]);
+ }
+ if (attrs[THERMAL_GENL_ATTR_TZ_TRIP_ID])
+ LOG(INFO) << "Thermal zone trip id: "
+ << nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TRIP_ID]);
+ }
+
+ if (glh->cmd == THERMAL_GENL_EVENT_TZ_GOV_CHANGE) {
+ LOG(INFO) << "THERMAL_GENL_EVENT_TZ_GOV_CHANGE";
+ if (attrs[THERMAL_GENL_ATTR_TZ_ID]) {
+ LOG(INFO) << "Thermal zone id: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]);
+ *tz_id = nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]);
+ }
+ if (attrs[THERMAL_GENL_ATTR_GOV_NAME])
+ LOG(INFO) << "Governor name: " << nla_get_string(attrs[THERMAL_GENL_ATTR_GOV_NAME]);
+ }
+
+ if (glh->cmd == THERMAL_GENL_EVENT_TZ_CREATE) {
+ LOG(INFO) << "THERMAL_GENL_EVENT_TZ_CREATE";
+ if (attrs[THERMAL_GENL_ATTR_TZ_ID]) {
+ LOG(INFO) << "Thermal zone id: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]);
+ *tz_id = nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]);
+ }
+ if (attrs[THERMAL_GENL_ATTR_TZ_NAME])
+ LOG(INFO) << "Thermal zone name: " << nla_get_string(attrs[THERMAL_GENL_ATTR_TZ_NAME]);
+ }
+
+ if (glh->cmd == THERMAL_GENL_EVENT_TZ_DELETE) {
+ LOG(INFO) << "THERMAL_GENL_EVENT_TZ_DELETE";
+ if (attrs[THERMAL_GENL_ATTR_TZ_ID]) {
+ LOG(INFO) << "Thermal zone id: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]);
+ *tz_id = nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]);
+ }
+ }
+
+ if (glh->cmd == THERMAL_GENL_EVENT_TZ_DISABLE) {
+ LOG(INFO) << "THERMAL_GENL_EVENT_TZ_DISABLE";
+ if (attrs[THERMAL_GENL_ATTR_TZ_ID]) {
+ LOG(INFO) << "Thermal zone id: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]);
+ *tz_id = nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]);
+ }
+ }
+
+ if (glh->cmd == THERMAL_GENL_EVENT_TZ_ENABLE) {
+ LOG(INFO) << "THERMAL_GENL_EVENT_TZ_ENABLE";
+ if (attrs[THERMAL_GENL_ATTR_TZ_ID]) {
+ LOG(INFO) << "Thermal zone id: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]);
+ *tz_id = nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]);
+ }
+ }
+
+ if (glh->cmd == THERMAL_GENL_EVENT_TZ_TRIP_CHANGE) {
+ LOG(INFO) << "THERMAL_GENL_EVENT_TZ_TRIP_CHANGE";
+ if (attrs[THERMAL_GENL_ATTR_TZ_ID]) {
+ LOG(INFO) << "Thermal zone id: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]);
+ *tz_id = nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]);
+ }
+ if (attrs[THERMAL_GENL_ATTR_TZ_TRIP_ID])
+ LOG(INFO) << "Trip id:: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TRIP_ID]);
+ if (attrs[THERMAL_GENL_ATTR_TZ_TRIP_TYPE])
+ LOG(INFO) << "Trip type: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TRIP_TYPE]);
+ if (attrs[THERMAL_GENL_ATTR_TZ_TRIP_TEMP])
+ LOG(INFO) << "Trip temp: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TRIP_TEMP]);
+ if (attrs[THERMAL_GENL_ATTR_TZ_TRIP_HYST])
+ LOG(INFO) << "Trip hyst: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TRIP_HYST]);
+ }
+
+ if (glh->cmd == THERMAL_GENL_EVENT_TZ_TRIP_ADD) {
+ LOG(INFO) << "THERMAL_GENL_EVENT_TZ_TRIP_ADD";
+ if (attrs[THERMAL_GENL_ATTR_TZ_ID])
+ LOG(INFO) << "Thermal zone id: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]);
+ if (attrs[THERMAL_GENL_ATTR_TZ_TRIP_ID])
+ LOG(INFO) << "Trip id:: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TRIP_ID]);
+ if (attrs[THERMAL_GENL_ATTR_TZ_TRIP_TYPE])
+ LOG(INFO) << "Trip type: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TRIP_TYPE]);
+ if (attrs[THERMAL_GENL_ATTR_TZ_TRIP_TEMP])
+ LOG(INFO) << "Trip temp: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TRIP_TEMP]);
+ if (attrs[THERMAL_GENL_ATTR_TZ_TRIP_HYST])
+ LOG(INFO) << "Trip hyst: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TRIP_HYST]);
+ }
+
+ if (glh->cmd == THERMAL_GENL_EVENT_TZ_TRIP_DELETE) {
+ LOG(INFO) << "THERMAL_GENL_EVENT_TZ_TRIP_DELETE";
+ if (attrs[THERMAL_GENL_ATTR_TZ_ID]) {
+ LOG(INFO) << "Thermal zone id: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]);
+ *tz_id = nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]);
+ }
+ if (attrs[THERMAL_GENL_ATTR_TZ_TRIP_ID])
+ LOG(INFO) << "Trip id:: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TRIP_ID]);
+ }
+
+ if (glh->cmd == THERMAL_GENL_EVENT_CDEV_STATE_UPDATE) {
+ LOG(INFO) << "THERMAL_GENL_EVENT_CDEV_STATE_UPDATE";
+ if (attrs[THERMAL_GENL_ATTR_CDEV_ID])
+ LOG(INFO) << "Cooling device id: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_CDEV_ID]);
+ if (attrs[THERMAL_GENL_ATTR_CDEV_CUR_STATE])
+ LOG(INFO) << "Cooling device current state: "
+ << nla_get_u32(attrs[THERMAL_GENL_ATTR_CDEV_CUR_STATE]);
+ }
+
+ if (glh->cmd == THERMAL_GENL_EVENT_CDEV_ADD) {
+ LOG(INFO) << "THERMAL_GENL_EVENT_CDEV_ADD";
+ if (attrs[THERMAL_GENL_ATTR_CDEV_NAME])
+ LOG(INFO) << "Cooling device name: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_CDEV_NAME]);
+ if (attrs[THERMAL_GENL_ATTR_CDEV_ID])
+ LOG(INFO) << "Cooling device id: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_CDEV_ID]);
+ if (attrs[THERMAL_GENL_ATTR_CDEV_MAX_STATE])
+ LOG(INFO) << "Cooling device max state: "
+ << nla_get_u32(attrs[THERMAL_GENL_ATTR_CDEV_MAX_STATE]);
+ }
+
+ if (glh->cmd == THERMAL_GENL_EVENT_CDEV_DELETE) {
+ LOG(INFO) << "THERMAL_GENL_EVENT_CDEV_DELETE";
+ if (attrs[THERMAL_GENL_ATTR_CDEV_ID])
+ LOG(INFO) << "Cooling device id: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_CDEV_ID]);
+ }
+
+ if (glh->cmd == THERMAL_GENL_SAMPLING_TEMP) {
+ LOG(INFO) << "THERMAL_GENL_SAMPLING_TEMP";
+ if (attrs[THERMAL_GENL_ATTR_TZ_ID]) {
+ LOG(INFO) << "Thermal zone id: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]);
+ *tz_id = nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]);
+ }
+ if (attrs[THERMAL_GENL_ATTR_TZ_TEMP])
+ LOG(INFO) << "Thermal zone temp: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TEMP]);
+ }
+
+ return 0;
+}
+
+} // namespace
+
+void ThermalWatcher::registerFilesToWatch(const std::set<std::string> &sensors_to_watch) {
+ LOG(INFO) << "Uevent register file to watch...";
+ monitored_sensors_.insert(sensors_to_watch.begin(), sensors_to_watch.end());
+
+ uevent_fd_.reset((TEMP_FAILURE_RETRY(uevent_open_socket(64 * 1024, true))));
+ if (uevent_fd_.get() < 0) {
+ LOG(ERROR) << "failed to open uevent socket";
+ return;
+ }
+
+ fcntl(uevent_fd_, F_SETFL, O_NONBLOCK);
+
+ looper_->addFd(uevent_fd_.get(), 0, Looper::EVENT_INPUT, nullptr, nullptr);
+ sleep_ms_ = std::chrono::milliseconds(0);
+ last_update_time_ = boot_clock::now();
+}
+
+void ThermalWatcher::registerFilesToWatchNl(const std::set<std::string> &sensors_to_watch) {
+ LOG(INFO) << "Thermal genl register file to watch...";
+ monitored_sensors_.insert(sensors_to_watch.begin(), sensors_to_watch.end());
+
+ sk_thermal = nl_socket_alloc();
+ if (!sk_thermal) {
+ LOG(ERROR) << "nl_socket_alloc failed";
+ return;
+ }
+
+ if (genl_connect(sk_thermal)) {
+ LOG(ERROR) << "genl_connect failed: sk_thermal";
+ return;
+ }
+
+ thermal_genl_fd_.reset(nl_socket_get_fd(sk_thermal));
+ if (thermal_genl_fd_.get() < 0) {
+ LOG(ERROR) << "Failed to create thermal netlink socket";
+ return;
+ }
+
+ if (!socketAddMembership(sk_thermal, THERMAL_GENL_EVENT_GROUP_NAME)) {
+ return;
+ }
+
+ /*
+ * Currently, only the update_temperature() will send thermal genl samlping events
+ * from kernel. To avoid thermal-hal busy because samlping events are sent
+ * too frequently, ignore thermal genl samlping events until we figure out how to use it.
+ *
+ if (!socketAddMembership(sk_thermal, THERMAL_GENL_SAMPLING_GROUP_NAME)) {
+ return;
+ }
+ */
+
+ fcntl(thermal_genl_fd_, F_SETFL, O_NONBLOCK);
+ looper_->addFd(thermal_genl_fd_.get(), 0, Looper::EVENT_INPUT, nullptr, nullptr);
+ sleep_ms_ = std::chrono::milliseconds(0);
+ last_update_time_ = boot_clock::now();
+}
+
+bool ThermalWatcher::startWatchingDeviceFiles() {
+ if (cb_) {
+ auto ret = this->run("FileWatcherThread", PRIORITY_HIGHEST);
+ if (ret != NO_ERROR) {
+ LOG(ERROR) << "ThermalWatcherThread start fail";
+ return false;
+ } else {
+ LOG(INFO) << "ThermalWatcherThread started";
+ return true;
+ }
+ }
+ return false;
+}
+void ThermalWatcher::parseUevent(std::set<std::string> *sensors_set) {
+ bool thermal_event = false;
+ constexpr int kUeventMsgLen = 2048;
+ char msg[kUeventMsgLen + 2];
+ char *cp;
+
+ while (true) {
+ int n = uevent_kernel_multicast_recv(uevent_fd_.get(), msg, kUeventMsgLen);
+ if (n <= 0) {
+ if (errno != EAGAIN && errno != EWOULDBLOCK) {
+ LOG(ERROR) << "Error reading from Uevent Fd";
+ }
+ break;
+ }
+
+ if (n >= kUeventMsgLen) {
+ LOG(ERROR) << "Uevent overflowed buffer, discarding";
+ continue;
+ }
+
+ msg[n] = '\0';
+ msg[n + 1] = '\0';
+
+ cp = msg;
+ while (*cp) {
+ std::string uevent = cp;
+ auto findSubSystemThermal = uevent.find("SUBSYSTEM=thermal");
+ if (!thermal_event) {
+ if (!uevent.find("SUBSYSTEM=")) {
+ if (findSubSystemThermal != std::string::npos) {
+ thermal_event = true;
+ } else {
+ break;
+ }
+ }
+ } else {
+ auto start_pos = uevent.find("NAME=");
+ if (start_pos != std::string::npos) {
+ start_pos += 5;
+ std::string name = uevent.substr(start_pos);
+ if (std::find(monitored_sensors_.begin(), monitored_sensors_.end(), name) !=
+ monitored_sensors_.end()) {
+ sensors_set->insert(name);
+ }
+ break;
+ }
+ }
+ while (*cp++) {
+ }
+ }
+ }
+}
+
+// TODO(b/175367921): Consider for potentially adding more type of event in the function
+// instead of just add the sensors to the list.
+void ThermalWatcher::parseGenlink(std::set<std::string> *sensors_set) {
+ int err = 0, done = 0, tz_id = -1;
+
+ std::unique_ptr<nl_cb, decltype(&nl_cb_put)> cb(nl_cb_alloc(NL_CB_DEFAULT), nl_cb_put);
+
+ nl_cb_err(cb.get(), NL_CB_CUSTOM, nlErrorHandle, &err);
+ nl_cb_set(cb.get(), NL_CB_FINISH, NL_CB_CUSTOM, nlFinishHandle, &done);
+ nl_cb_set(cb.get(), NL_CB_ACK, NL_CB_CUSTOM, nlAckHandle, &done);
+ nl_cb_set(cb.get(), NL_CB_SEQ_CHECK, NL_CB_CUSTOM, nlSeqCheckHandle, &done);
+ nl_cb_set(cb.get(), NL_CB_VALID, NL_CB_CUSTOM, handleEvent, &tz_id);
+
+ while (!done && !err) {
+ nl_recvmsgs(sk_thermal, cb.get());
+
+ if (tz_id < 0) {
+ break;
+ }
+
+ std::string name;
+ if (getThermalZoneTypeById(tz_id, &name) &&
+ std::find(monitored_sensors_.begin(), monitored_sensors_.end(), name) !=
+ monitored_sensors_.end()) {
+ sensors_set->insert(name);
+ }
+ }
+}
+
+void ThermalWatcher::wake() {
+ looper_->wake();
+}
+
+bool ThermalWatcher::threadLoop() {
+ LOG(VERBOSE) << "ThermalWatcher polling...";
+
+ int fd;
+ std::set<std::string> sensors;
+
+ auto time_elapsed_ms = std::chrono::duration_cast<std::chrono::milliseconds>(boot_clock::now() -
+ last_update_time_);
+
+ if (time_elapsed_ms < sleep_ms_ &&
+ looper_->pollOnce(sleep_ms_.count(), &fd, nullptr, nullptr) >= 0) {
+ ATRACE_NAME("ThermalWatcher::threadLoop - receive event");
+ if (fd != uevent_fd_.get() && fd != thermal_genl_fd_.get()) {
+ return true;
+ } else if (fd == thermal_genl_fd_.get()) {
+ parseGenlink(&sensors);
+ } else if (fd == uevent_fd_.get()) {
+ parseUevent(&sensors);
+ }
+ // Ignore cb_ if uevent is not from monitored sensors
+ if (sensors.size() == 0) {
+ return true;
+ }
+ }
+
+ sleep_ms_ = cb_(sensors);
+ last_update_time_ = boot_clock::now();
+ return true;
+}
+
+} // namespace implementation
+} // namespace V2_0
+} // namespace thermal
+} // namespace hardware
+} // namespace android
diff --git a/thermal/pid_1_0/utils/thermal_watcher.h b/thermal/pid_1_0/utils/thermal_watcher.h
new file mode 100644
index 0000000..a7e3820
--- /dev/null
+++ b/thermal/pid_1_0/utils/thermal_watcher.h
@@ -0,0 +1,114 @@
+/*
+ * 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 <android-base/chrono_utils.h>
+#include <android-base/unique_fd.h>
+#include <linux/genetlink.h>
+#include <netlink/genl/ctrl.h>
+#include <netlink/genl/genl.h>
+#include <utils/Looper.h>
+#include <utils/Thread.h>
+
+#include <chrono>
+#include <condition_variable>
+#include <future>
+#include <list>
+#include <mutex>
+#include <set>
+#include <string>
+#include <thread>
+#include <unordered_map>
+#include <vector>
+
+namespace android {
+namespace hardware {
+namespace thermal {
+namespace V2_0 {
+namespace implementation {
+
+using android::base::boot_clock;
+using android::base::unique_fd;
+using WatcherCallback = std::function<std::chrono::milliseconds(const std::set<std::string> &name)>;
+
+// A helper class for monitoring thermal files changes.
+class ThermalWatcher : public ::android::Thread {
+ public:
+ explicit ThermalWatcher(const WatcherCallback &cb)
+ : Thread(false), cb_(cb), looper_(new Looper(true)) {}
+ ~ThermalWatcher() = default;
+
+ // Disallow copy and assign.
+ ThermalWatcher(const ThermalWatcher &) = delete;
+ void operator=(const ThermalWatcher &) = delete;
+
+ // Start the thread and return true if it succeeds.
+ bool startWatchingDeviceFiles();
+ // Give the file watcher a list of files to start watching. This helper
+ // class will by default wait for modifications to the file with a looper.
+ // This should be called before starting watcher thread.
+ // For monitoring uevents.
+ void registerFilesToWatch(const std::set<std::string> &sensors_to_watch);
+ // For monitoring thermal genl events.
+ void registerFilesToWatchNl(const std::set<std::string> &sensors_to_watch);
+ // Wake up the looper thus the worker thread, immediately. This can be called
+ // in any thread.
+ void wake();
+
+ private:
+ // The work done by the watcher thread. This will use inotify to check for
+ // modifications to the files to watch. If any modification is seen this
+ // will callback the registered function with the new data read from the
+ // modified file.
+ bool threadLoop() override;
+
+ // Parse uevent message
+ void parseUevent(std::set<std::string> *sensor_name);
+
+ // Parse thermal netlink message
+ void parseGenlink(std::set<std::string> *sensor_name);
+
+ // Maps watcher filer descriptor to watched file path.
+ std::unordered_map<int, std::string> watch_to_file_path_map_;
+
+ // The callback function. Called whenever thermal uevent is seen.
+ // The function passed in should expect a string in the form (type).
+ // Where type is the name of the thermal zone that trigger a uevent notification.
+ // Callback will return thermal trigger status for next polling decision.
+ const WatcherCallback cb_;
+
+ sp<Looper> looper_;
+
+ // For uevent socket registration.
+ android::base::unique_fd uevent_fd_;
+ // For thermal genl socket registration.
+ android::base::unique_fd thermal_genl_fd_;
+ // Sensor list which monitor flag is enabled.
+ std::set<std::string> monitored_sensors_;
+ // Sleep interval voting result
+ std::chrono::milliseconds sleep_ms_;
+ // Timestamp for last thermal update
+ boot_clock::time_point last_update_time_;
+ // For thermal genl socket object.
+ struct nl_sock *sk_thermal;
+};
+
+} // namespace implementation
+} // namespace V2_0
+} // namespace thermal
+} // namespace hardware
+} // namespace android
diff --git a/thermal/thermal-helper.cpp b/thermal/thermal-helper.cpp
index 9b23560..8bc6741 100644
--- a/thermal/thermal-helper.cpp
+++ b/thermal/thermal-helper.cpp
@@ -232,16 +232,17 @@
}
// Update cooling device max state
- for (auto &binded_cdev_pair :
+ for (auto &binded_cdev_info_pair :
name_status_pair.second.throttling_info->binded_cdev_info_map) {
- const auto &cdev_info = cooling_device_info_map_.at(binded_cdev_pair.first);
+ const auto &cdev_info = cooling_device_info_map_.at(binded_cdev_info_pair.first);
- for (auto &cdev_ceiling : binded_cdev_pair.second.cdev_ceiling) {
+ for (auto &cdev_ceiling : binded_cdev_info_pair.second.cdev_ceiling) {
if (cdev_ceiling > cdev_info.max_state) {
if (cdev_ceiling != std::numeric_limits<int>::max()) {
- LOG(ERROR) << "Sensor " << name_status_pair.first << "'s "
- << binded_cdev_pair.first << " cdev_ceiling:" << cdev_ceiling
- << " is higher than max state:" << cdev_info.max_state;
+ LOG(ERROR)
+ << "Sensor " << name_status_pair.first << "'s "
+ << binded_cdev_info_pair.first << " cdev_ceiling:" << cdev_ceiling
+ << " is higher than max state:" << cdev_info.max_state;
}
cdev_ceiling = cdev_info.max_state;
}
@@ -433,6 +434,8 @@
}
if (cooling_devices_.writeCdevFile(target_cdev, std::to_string(max_state))) {
LOG(INFO) << "Successfully update cdev " << target_cdev << " sysfs to " << max_state;
+ } else {
+ LOG(ERROR) << "Failed to update cdev " << target_cdev << " sysfs to " << max_state;
}
}
}
diff --git a/thermal/utils/thermal_info.cpp b/thermal/utils/thermal_info.cpp
index 7ed63c3..f01d11b 100644
--- a/thermal/utils/thermal_info.cpp
+++ b/thermal/utils/thermal_info.cpp
@@ -480,7 +480,8 @@
s_power.fill(NAN);
std::array<float, kThrottlingSeverityCount> i_cutoff;
i_cutoff.fill(NAN);
- float err_integral_default = 0.0;
+ float i_default = 0.0;
+ int tran_cycle = 0;
// Parse PID parameters
if (!sensors[i]["PIDInfo"].empty()) {
@@ -560,9 +561,15 @@
return false;
}
LOG(INFO) << "Start to parse"
- << " Sensor[" << name << "]'s E_Integral_Default";
- err_integral_default = getFloatFromValue(sensors[i]["PIDInfo"]["E_Integral_Default"]);
- LOG(INFO) << "Sensor[" << name << "]'s E_Integral_Default: " << err_integral_default;
+ << " Sensor[" << name << "]'s I_Default";
+ i_default = getFloatFromValue(sensors[i]["PIDInfo"]["I_Default"]);
+ LOG(INFO) << "Sensor[" << name << "]'s I_Default: " << i_default;
+
+ LOG(INFO) << "Start to parse"
+ << " Sensor[" << name << "]'s TranCycle";
+ tran_cycle = getFloatFromValue(sensors[i]["PIDInfo"]["TranCycle"]);
+ LOG(INFO) << "Sensor[" << name << "]'s TranCycle: " << tran_cycle;
+
// Confirm we have at least one valid PID combination
bool valid_pid_combination = false;
for (Json::Value::ArrayIndex j = 0; j < kThrottlingSeverityCount; ++j) {
@@ -742,6 +749,31 @@
};
}
+ std::unordered_map<std::string, ThrottlingArray> excluded_power_info_map;
+ values = sensors[i]["ExcludedPowerInfo"];
+ for (Json::Value::ArrayIndex j = 0; j < values.size(); ++j) {
+ Json::Value sub_values;
+ const std::string &power_rail = values[j]["PowerRail"].asString();
+ if (power_rail.empty()) {
+ LOG(ERROR) << "Sensor[" << name << "] failed to parse excluded PowerRail";
+ sensors_parsed->clear();
+ return false;
+ }
+ ThrottlingArray power_weight;
+ power_weight.fill(1);
+ if (!values[j]["PowerWeight"].empty()) {
+ LOG(INFO) << "Sensor[" << name << "]: Start to parse " << power_rail
+ << "'s PowerWeight";
+ if (!getFloatFromJsonValues(values[j]["PowerWeight"], &power_weight, false,
+ false)) {
+ LOG(ERROR) << "Failed to parse PowerWeight";
+ sensors_parsed->clear();
+ return false;
+ }
+ }
+ excluded_power_info_map[power_rail] = power_weight;
+ }
+
if (is_hidden && send_cb) {
LOG(ERROR) << "is_hidden and send_cb cannot be enabled together";
sensors_parsed->clear();
@@ -757,9 +789,9 @@
trigger_sensor, formula});
}
- std::shared_ptr<ThrottlingInfo> throttling_info(
- new ThrottlingInfo{k_po, k_pu, k_i, k_d, i_max, max_alloc_power, min_alloc_power,
- s_power, i_cutoff, err_integral_default, binded_cdev_info_map});
+ std::shared_ptr<ThrottlingInfo> throttling_info(new ThrottlingInfo{
+ k_po, k_pu, k_i, k_d, i_max, max_alloc_power, min_alloc_power, s_power, i_cutoff,
+ i_default, tran_cycle, excluded_power_info_map, binded_cdev_info_map});
(*sensors_parsed)[name] = {
.type = sensor_type,
diff --git a/thermal/utils/thermal_info.h b/thermal/utils/thermal_info.h
index 84d0402..e56b358 100644
--- a/thermal/utils/thermal_info.h
+++ b/thermal/utils/thermal_info.h
@@ -95,7 +95,9 @@
ThrottlingArray min_alloc_power;
ThrottlingArray s_power;
ThrottlingArray i_cutoff;
- float err_integral_default;
+ float i_default;
+ int tran_cycle;
+ std::unordered_map<std::string, ThrottlingArray> excluded_power_info_map;
std::unordered_map<std::string, BindedCdevInfo> binded_cdev_info_map;
};
diff --git a/thermal/utils/thermal_throttling.cpp b/thermal/utils/thermal_throttling.cpp
index be1c39e..6f2d120 100644
--- a/thermal/utils/thermal_throttling.cpp
+++ b/thermal/utils/thermal_throttling.cpp
@@ -37,6 +37,7 @@
namespace thermal {
namespace V2_0 {
namespace implementation {
+using android::base::StringPrintf;
// To find the next PID target state according to the current thermal severity
size_t getTargetStateOfPID(const SensorInfo &sensor_info, const ThrottlingSeverity curr_severity) {
@@ -83,9 +84,14 @@
throttling_release_pair.second = 0;
}
- thermal_throttling_status_map_[sensor_name.data()].err_integral =
- sensor_info.throttling_info->err_integral_default;
thermal_throttling_status_map_[sensor_name.data()].prev_err = NAN;
+ thermal_throttling_status_map_[sensor_name.data()].i_budget =
+ sensor_info.throttling_info->i_default;
+ thermal_throttling_status_map_[sensor_name.data()].prev_target =
+ static_cast<size_t>(ThrottlingSeverity::NONE);
+ thermal_throttling_status_map_[sensor_name.data()].prev_power_budget = NAN;
+ thermal_throttling_status_map_[sensor_name.data()].tran_cycle = 0;
+
return;
}
@@ -101,9 +107,13 @@
LOG(ERROR) << "Sensor " << sensor_name.data() << " has no throttling info";
return false;
}
- thermal_throttling_status_map_[sensor_name.data()].err_integral =
- throttling_info->err_integral_default;
+
thermal_throttling_status_map_[sensor_name.data()].prev_err = NAN;
+ thermal_throttling_status_map_[sensor_name.data()].i_budget = throttling_info->i_default;
+ thermal_throttling_status_map_[sensor_name.data()].prev_target =
+ static_cast<size_t>(ThrottlingSeverity::NONE);
+ thermal_throttling_status_map_[sensor_name.data()].prev_power_budget = NAN;
+ thermal_throttling_status_map_[sensor_name.data()].tran_cycle = 0;
for (auto &binded_cdev_pair : throttling_info->binded_cdev_info_map) {
if (!cooling_device_info_map.count(binded_cdev_pair.first)) {
@@ -153,38 +163,49 @@
const SensorInfo &sensor_info,
std::chrono::milliseconds time_elapsed_ms,
ThrottlingSeverity curr_severity) {
- float p = 0, i = 0, d = 0;
+ float p = 0, d = 0;
float power_budget = std::numeric_limits<float>::max();
+ bool target_changed = false;
+ float budget_transient = 0.0;
+ auto &throttling_status = thermal_throttling_status_map_.at(temp.name);
if (curr_severity == ThrottlingSeverity::NONE) {
return power_budget;
}
const auto target_state = getTargetStateOfPID(sensor_info, curr_severity);
+ if (throttling_status.prev_target != static_cast<size_t>(ThrottlingSeverity::NONE) &&
+ target_state != throttling_status.prev_target &&
+ sensor_info.throttling_info->tran_cycle > 0) {
+ throttling_status.tran_cycle = sensor_info.throttling_info->tran_cycle - 1;
+ target_changed = true;
+ }
+ throttling_status.prev_target = target_state;
// Compute PID
float err = sensor_info.hot_thresholds[target_state] - temp.value;
p = err * (err < 0 ? sensor_info.throttling_info->k_po[target_state]
: sensor_info.throttling_info->k_pu[target_state]);
- i = thermal_throttling_status_map_[temp.name].err_integral *
- sensor_info.throttling_info->k_i[target_state];
+
if (err < sensor_info.throttling_info->i_cutoff[target_state]) {
- float i_next = i + err * sensor_info.throttling_info->k_i[target_state];
- if (abs(i_next) < sensor_info.throttling_info->i_max[target_state]) {
- i = i_next;
- thermal_throttling_status_map_[temp.name].err_integral += err;
- }
+ throttling_status.i_budget += err * sensor_info.throttling_info->k_i[target_state];
}
- if (!std::isnan(thermal_throttling_status_map_[temp.name].prev_err) &&
+ if (fabsf(throttling_status.i_budget) > sensor_info.throttling_info->i_max[target_state]) {
+ throttling_status.i_budget = sensor_info.throttling_info->i_max[target_state] *
+ (throttling_status.i_budget > 0 ? 1 : -1);
+ }
+
+ if (!std::isnan(throttling_status.prev_err) &&
time_elapsed_ms != std::chrono::milliseconds::zero()) {
- d = sensor_info.throttling_info->k_d[target_state] *
- (err - thermal_throttling_status_map_[temp.name].prev_err) / time_elapsed_ms.count();
+ d = sensor_info.throttling_info->k_d[target_state] * (err - throttling_status.prev_err) /
+ time_elapsed_ms.count();
}
- thermal_throttling_status_map_[temp.name].prev_err = err;
+ throttling_status.prev_err = err;
// Calculate power budget
- power_budget = sensor_info.throttling_info->s_power[target_state] + p + i + d;
+ power_budget =
+ sensor_info.throttling_info->s_power[target_state] + p + throttling_status.i_budget + d;
if (power_budget < sensor_info.throttling_info->min_alloc_power[target_state]) {
power_budget = sensor_info.throttling_info->min_alloc_power[target_state];
}
@@ -192,15 +213,51 @@
power_budget = sensor_info.throttling_info->max_alloc_power[target_state];
}
+ if (target_changed) {
+ throttling_status.budget_transient = throttling_status.prev_power_budget - power_budget;
+ }
+
+ if (throttling_status.tran_cycle) {
+ budget_transient = throttling_status.budget_transient *
+ ((static_cast<float>(throttling_status.tran_cycle) /
+ static_cast<float>(sensor_info.throttling_info->tran_cycle)));
+ power_budget += budget_transient;
+ throttling_status.tran_cycle--;
+ }
+
LOG(INFO) << temp.name << " power_budget=" << power_budget << " err=" << err
- << " err_integral=" << thermal_throttling_status_map_[temp.name].err_integral
<< " s_power=" << sensor_info.throttling_info->s_power[target_state]
- << " time_elapsed_ms=" << time_elapsed_ms.count() << " p=" << p << " i=" << i
- << " d=" << d << " control target=" << target_state;
+ << " time_elapsed_ms=" << time_elapsed_ms.count() << " p=" << p
+ << " i=" << throttling_status.i_budget << " d=" << d
+ << " budget transient=" << budget_transient << " control target=" << target_state;
+
+ throttling_status.prev_power_budget = power_budget;
return power_budget;
}
+float ThermalThrottling::computeExcludedPower(
+ const SensorInfo &sensor_info, const ThrottlingSeverity curr_severity,
+ const std::unordered_map<std::string, PowerStatus> &power_status_map,
+ std::string *log_buf) {
+ float excluded_power = 0.0;
+
+ for (const auto &excluded_power_info_pair :
+ sensor_info.throttling_info->excluded_power_info_map) {
+ const auto last_updated_avg_power =
+ power_status_map.at(excluded_power_info_pair.first).last_updated_avg_power;
+ if (!std::isnan(last_updated_avg_power)) {
+ excluded_power += last_updated_avg_power *
+ excluded_power_info_pair.second[static_cast<size_t>(curr_severity)];
+ log_buf->append(StringPrintf(
+ "(%s: %0.2f mW, cdev_weight: %f)", excluded_power_info_pair.first.c_str(),
+ last_updated_avg_power,
+ excluded_power_info_pair.second[static_cast<size_t>(curr_severity)]));
+ }
+ }
+ return excluded_power;
+}
+
// Allocate power budget to binded cooling devices base on the real ODPM power data
bool ThermalThrottling::allocatePowerToCdev(
const Temperature_2_0 &temp, const SensorInfo &sensor_info,
@@ -214,10 +271,23 @@
bool low_power_device_check = true;
bool is_budget_allocated = false;
bool power_data_invalid = false;
- auto total_power_budget = updatePowerBudget(temp, sensor_info, time_elapsed_ms, curr_severity);
std::set<std::string> allocated_cdev;
+ std::string log_buf;
std::unique_lock<std::shared_mutex> _lock(thermal_throttling_status_map_mutex_);
+ auto total_power_budget = updatePowerBudget(temp, sensor_info, time_elapsed_ms, curr_severity);
+
+ if (sensor_info.throttling_info->excluded_power_info_map.size()) {
+ std::string log_buf;
+ total_power_budget -=
+ computeExcludedPower(sensor_info, curr_severity, power_status_map, &log_buf);
+ total_power_budget = std::max(total_power_budget, 0.0f);
+ if (!log_buf.empty()) {
+ LOG(INFO) << temp.name << " power budget=" << total_power_budget << " after " << log_buf
+ << " is excluded";
+ }
+ }
+
// Compute total cdev weight
for (const auto &binded_cdev_info_pair : sensor_info.throttling_info->binded_cdev_info_map) {
const auto cdev_weight = binded_cdev_info_pair.second
@@ -237,8 +307,6 @@
binded_cdev_info_pair.second
.cdev_weight_for_pid[static_cast<size_t>(curr_severity)];
- const CdevInfo &cdev_info = cooling_device_info_map.at(binded_cdev_info_pair.first);
-
if (allocated_cdev.count(binded_cdev_info_pair.first)) {
continue;
}
@@ -278,12 +346,24 @@
allocated_power += last_updated_avg_power;
allocated_weight += cdev_weight;
allocated_cdev.insert(binded_cdev_info_pair.first);
+ log_buf.append(StringPrintf("(%s: %0.2f mW)",
+ binded_cdev_info_pair.second.power_rail.c_str(),
+ last_updated_avg_power));
+
+ LOG(VERBOSE) << temp.name << " binded " << binded_cdev_info_pair.first
+ << " has been already at min state 0";
}
} else {
+ const CdevInfo &cdev_info = cooling_device_info_map.at(binded_cdev_info_pair.first);
+ log_buf.append(StringPrintf("(%s: %0.2f mW)",
+ binded_cdev_info_pair.second.power_rail.c_str(),
+ last_updated_avg_power));
// Ignore the power distribution if the CDEV has no space to reduce power
if ((cdev_power_adjustment < 0 &&
thermal_throttling_status_map_[temp.name].pid_cdev_request_map.at(
binded_cdev_info_pair.first) == cdev_info.max_state)) {
+ LOG(VERBOSE) << temp.name << " binded " << binded_cdev_info_pair.first
+ << " has been already at max state " << cdev_info.max_state;
continue;
}
@@ -356,7 +436,9 @@
is_budget_allocated = true;
}
}
-
+ if (log_buf.size()) {
+ LOG(INFO) << temp.name << " binded power rails: " << log_buf;
+ }
return true;
}
diff --git a/thermal/utils/thermal_throttling.h b/thermal/utils/thermal_throttling.h
index a1c9f6c..d373dd3 100644
--- a/thermal/utils/thermal_throttling.h
+++ b/thermal/utils/thermal_throttling.h
@@ -45,8 +45,12 @@
std::unordered_map<std::string, int> hardlimit_cdev_request_map;
std::unordered_map<std::string, int> throttling_release_map;
std::unordered_map<std::string, int> cdev_status_map;
- float err_integral;
float prev_err;
+ float i_budget;
+ float prev_target;
+ float prev_power_budget;
+ float budget_transient;
+ int tran_cycle;
};
// Return the control temp target of PID algorithm
@@ -93,6 +97,13 @@
float updatePowerBudget(const Temperature_2_0 &temp, const SensorInfo &sensor_info,
std::chrono::milliseconds time_elapsed_ms,
ThrottlingSeverity curr_severity);
+
+ // PID algo - return the power number from excluded power rail list
+ float computeExcludedPower(const SensorInfo &sensor_info,
+ const ThrottlingSeverity curr_severity,
+ const std::unordered_map<std::string, PowerStatus> &power_status_map,
+ std::string *log_buf);
+
// PID algo - allocate the power to target CDEV according to the ODPM
bool allocatePowerToCdev(
const Temperature_2_0 &temp, const SensorInfo &sensor_info,
diff --git a/vibrator/cs40l26/Hardware.h b/vibrator/cs40l26/Hardware.h
index c0bea8b..f0d7038 100644
--- a/vibrator/cs40l26/Hardware.h
+++ b/vibrator/cs40l26/Hardware.h
@@ -35,6 +35,7 @@
open("default/owt_free_space", &mOwtFreeSpace);
open("default/f0_comp_enable", &mF0CompEnable);
open("default/redc_comp_enable", &mRedcCompEnable);
+ open("default/delay_before_stop_playback_us", &mMinOnOffInterval);
}
bool setF0(std::string value) override { return set(value, &mF0); }
@@ -49,6 +50,7 @@
bool getOwtFreeSpace(uint32_t *value) override { return get(value, &mOwtFreeSpace); }
bool setF0CompEnable(bool value) override { return set(value, &mF0CompEnable); }
bool setRedcCompEnable(bool value) override { return set(value, &mRedcCompEnable); }
+ bool setMinOnOffInterval(uint32_t value) override { return set(value, &mMinOnOffInterval); }
void debug(int fd) override { HwApiBase::debug(fd); }
private:
@@ -61,6 +63,7 @@
std::ifstream mOwtFreeSpace;
std::ofstream mF0CompEnable;
std::ofstream mRedcCompEnable;
+ std::ofstream mMinOnOffInterval;
};
class HwCal : public Vibrator::HwCal, private HwCalBase {
diff --git a/vibrator/cs40l26/Vibrator.cpp b/vibrator/cs40l26/Vibrator.cpp
index f21ccbc..587f0fe 100644
--- a/vibrator/cs40l26/Vibrator.cpp
+++ b/vibrator/cs40l26/Vibrator.cpp
@@ -52,6 +52,7 @@
static constexpr uint8_t VOLTAGE_SCALE_MAX = 100;
static constexpr int8_t MAX_COLD_START_LATENCY_MS = 6; // I2C Transaction + DSP Return-From-Standby
+static constexpr uint32_t MIN_ON_OFF_INTERVAL_US = 8500; // SVC initialization time
static constexpr int8_t MAX_PAUSE_TIMING_ERROR_MS = 1; // ALERT Irq Handling
static constexpr uint32_t MAX_TIME_MS = UINT16_MAX;
@@ -397,6 +398,8 @@
}
mSupportedPrimitives = defaultSupportedPrimitives;
}
+
+ mHwApi->setMinOnOffInterval(MIN_ON_OFF_INTERVAL_US);
}
ndk::ScopedAStatus Vibrator::getCapabilities(int32_t *_aidl_return) {
diff --git a/vibrator/cs40l26/Vibrator.h b/vibrator/cs40l26/Vibrator.h
index 8305d92..a183cac 100644
--- a/vibrator/cs40l26/Vibrator.h
+++ b/vibrator/cs40l26/Vibrator.h
@@ -58,6 +58,8 @@
virtual bool setF0CompEnable(bool value) = 0;
// Enables/Disables Redc compensation enable status
virtual bool setRedcCompEnable(bool value) = 0;
+ // Stores the minumun delay time between playback and stop effects.
+ virtual bool setMinOnOffInterval(uint32_t value) = 0;
// Emit diagnostic information to the given file.
virtual void debug(int fd) = 0;
};
diff --git a/vibrator/cs40l26/android.hardware.vibrator-service.cs40l26-dual.rc b/vibrator/cs40l26/android.hardware.vibrator-service.cs40l26-dual.rc
index 44023d8..5e8aac7 100644
--- a/vibrator/cs40l26/android.hardware.vibrator-service.cs40l26-dual.rc
+++ b/vibrator/cs40l26/android.hardware.vibrator-service.cs40l26-dual.rc
@@ -14,8 +14,9 @@
chown system system /sys/bus/i2c/devices/i2c-cs40l26a-dual/default/num_waves
chown system system /sys/bus/i2c/devices/i2c-cs40l26a-dual/default/f0_offset
chown system system /sys/bus/i2c/devices/i2c-cs40l26a-dual/default/owt_free_space
- chown system system /sys/bus/i2c/devices/i2c-cs40l26a/default/f0_comp_enable
- chown system system /sys/bus/i2c/devices/i2c-cs40l26a/default/redc_comp_enable
+ chown system system /sys/bus/i2c/devices/i2c-cs40l26a-dual/default/f0_comp_enable
+ chown system system /sys/bus/i2c/devices/i2c-cs40l26a-dual/default/redc_comp_enable
+ chown system system /sys/bus/i2c/devices/i2c-cs40l26a-dual/default/delay_before_stop_playback_us
enable vendor.vibrator.cs40l26-dual
@@ -39,6 +40,7 @@
default/owt_free_space
default/f0_comp_enable
default/redc_comp_enable
+ default/delay_before_stop_playback_us
"
disabled
diff --git a/vibrator/cs40l26/android.hardware.vibrator-service.cs40l26.rc b/vibrator/cs40l26/android.hardware.vibrator-service.cs40l26.rc
index 02aa0a8..89d0eb7 100644
--- a/vibrator/cs40l26/android.hardware.vibrator-service.cs40l26.rc
+++ b/vibrator/cs40l26/android.hardware.vibrator-service.cs40l26.rc
@@ -16,6 +16,7 @@
chown system system /sys/bus/i2c/devices/i2c-cs40l26a/default/owt_free_space
chown system system /sys/bus/i2c/devices/i2c-cs40l26a/default/f0_comp_enable
chown system system /sys/bus/i2c/devices/i2c-cs40l26a/default/redc_comp_enable
+ chown system system /sys/bus/i2c/devices/i2c-cs40l26a/default/delay_before_stop_playback_us
enable vendor.vibrator.cs40l26
@@ -39,6 +40,7 @@
default/owt_free_space
default/f0_comp_enable
default/redc_comp_enable
+ default/delay_before_stop_playback_us
"
disabled