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