Revert "Revert "transcoding: add thermal status listener""

This reverts commit b253cec66de2986379ef94e1d372123dee1b826f.

Reason for revert: reland with fix

Change-Id: Ieab554dc2dc4187e75fc223997387f4875832e52
diff --git a/media/libmediatranscoding/Android.bp b/media/libmediatranscoding/Android.bp
index 094d7c3..23e83f9 100644
--- a/media/libmediatranscoding/Android.bp
+++ b/media/libmediatranscoding/Android.bp
@@ -71,11 +71,12 @@
     ],
 
     srcs: [
-        "TranscodingClientManager.cpp",
-        "TranscodingSessionController.cpp",
-        "TranscodingResourcePolicy.cpp",
-        "TranscodingUidPolicy.cpp",
         "TranscoderWrapper.cpp",
+        "TranscodingClientManager.cpp",
+        "TranscodingResourcePolicy.cpp",
+        "TranscodingSessionController.cpp",
+        "TranscodingThermalPolicy.cpp",
+        "TranscodingUidPolicy.cpp",
     ],
 
     shared_libs: [
diff --git a/media/libmediatranscoding/TranscodingClientManager.cpp b/media/libmediatranscoding/TranscodingClientManager.cpp
index 46d1da2..b6a2381 100644
--- a/media/libmediatranscoding/TranscodingClientManager.cpp
+++ b/media/libmediatranscoding/TranscodingClientManager.cpp
@@ -138,11 +138,10 @@
         ALOGE("submitRequest rejected (clientPid %d, clientUid %d) "
               "(don't trust callingUid %d)",
               in_clientPid, in_clientUid, callingUid);
-        return STATUS_ERROR_FMT(
-                IMediaTranscodingService::ERROR_PERMISSION_DENIED,
-                "submitRequest rejected (clientPid %d, clientUid %d) "
-                "(don't trust callingUid %d)",
-                in_clientPid, in_clientUid, callingUid);
+        return STATUS_ERROR_FMT(IMediaTranscodingService::ERROR_PERMISSION_DENIED,
+                                "submitRequest rejected (clientPid %d, clientUid %d) "
+                                "(don't trust callingUid %d)",
+                                in_clientPid, in_clientUid, callingUid);
     }
 
     // Check if we can trust clientPid. Only privilege caller could forward the
@@ -155,11 +154,10 @@
         ALOGE("submitRequest rejected (clientPid %d, clientUid %d) "
               "(don't trust callingUid %d)",
               in_clientPid, in_clientUid, callingUid);
-        return STATUS_ERROR_FMT(
-                IMediaTranscodingService::ERROR_PERMISSION_DENIED,
-                "submitRequest rejected (clientPid %d, clientUid %d) "
-                "(don't trust callingUid %d)",
-                in_clientPid, in_clientUid, callingUid);
+        return STATUS_ERROR_FMT(IMediaTranscodingService::ERROR_PERMISSION_DENIED,
+                                "submitRequest rejected (clientPid %d, clientUid %d) "
+                                "(don't trust callingUid %d)",
+                                in_clientPid, in_clientUid, callingUid);
     }
 
     int32_t sessionId = mNextSessionId.fetch_add(1);
diff --git a/media/libmediatranscoding/TranscodingSessionController.cpp b/media/libmediatranscoding/TranscodingSessionController.cpp
index b77a3a4..09ad3cd 100644
--- a/media/libmediatranscoding/TranscodingSessionController.cpp
+++ b/media/libmediatranscoding/TranscodingSessionController.cpp
@@ -63,10 +63,12 @@
 TranscodingSessionController::TranscodingSessionController(
         const std::shared_ptr<TranscoderInterface>& transcoder,
         const std::shared_ptr<UidPolicyInterface>& uidPolicy,
-        const std::shared_ptr<ResourcePolicyInterface>& resourcePolicy)
+        const std::shared_ptr<ResourcePolicyInterface>& resourcePolicy,
+        const std::shared_ptr<ThermalPolicyInterface>& thermalPolicy)
       : mTranscoder(transcoder),
         mUidPolicy(uidPolicy),
         mResourcePolicy(resourcePolicy),
+        mThermalPolicy(thermalPolicy),
         mCurrentSession(nullptr),
         mResourceLost(false) {
     // Only push empty offline queue initially. Realtime queues are added when requests come in.
@@ -74,6 +76,7 @@
     mOfflineUidIterator = mUidSortedList.begin();
     mSessionQueues.emplace(OFFLINE_UID, SessionQueueType());
     mUidPackageNames[OFFLINE_UID] = "(offline)";
+    mThermalThrottling = thermalPolicy->getThrottlingStatus();
 }
 
 TranscodingSessionController::~TranscodingSessionController() {}
@@ -141,7 +144,7 @@
 
     snprintf(buffer, SIZE, "\n========== Dumping past sessions =========\n");
     result.append(buffer);
-    for (auto &session : mSessionHistory) {
+    for (auto& session : mSessionHistory) {
         dumpSession_l(session, result, true /*closedSession*/);
     }
 
@@ -192,18 +195,27 @@
           topSession == nullptr ? "null" : sessionToString(topSession->key).c_str(),
           curSession == nullptr ? "null" : sessionToString(curSession->key).c_str());
 
+    if (topSession == nullptr) {
+        mCurrentSession = nullptr;
+        return;
+    }
+
+    bool shouldBeRunning = !((mResourcePolicy != nullptr && mResourceLost) ||
+                             (mThermalPolicy != nullptr && mThermalThrottling));
     // If we found a topSession that should be run, and it's not already running,
     // take some actions to ensure it's running.
-    if (topSession != nullptr &&
-        (topSession != curSession || topSession->getState() != Session::RUNNING)) {
-        // If another session is currently running, pause it first.
+    if (topSession != curSession ||
+        (shouldBeRunning ^ (topSession->getState() == Session::RUNNING))) {
+        // If current session is running, pause it first. Note this is true for either
+        // cases: 1) If top session is changing, or 2) if top session is not changing but
+        // the topSession's state is changing.
         if (curSession != nullptr && curSession->getState() == Session::RUNNING) {
             mTranscoder->pause(curSession->key.first, curSession->key.second);
             curSession->setState(Session::PAUSED);
         }
-        // If we are not experiencing resource loss, we can start or resume
-        // the topSession now.
-        if (!mResourceLost) {
+        // If we are not experiencing resource loss nor thermal throttling, we can start
+        // or resume the topSession now.
+        if (shouldBeRunning) {
             if (topSession->getState() == Session::NOT_STARTED) {
                 mTranscoder->start(topSession->key.first, topSession->key.second,
                                    topSession->request, topSession->callback.lock());
@@ -566,7 +578,9 @@
         if (clientCallback != nullptr) {
             clientCallback->onTranscodingPaused(sessionKey.second);
         }
-        mResourcePolicy->setPidResourceLost(resourceLostSession->request.clientPid);
+        if (mResourcePolicy != nullptr) {
+            mResourcePolicy->setPidResourceLost(resourceLostSession->request.clientPid);
+        }
         mResourceLost = true;
 
         validateState_l();
@@ -613,6 +627,36 @@
     validateState_l();
 }
 
+void TranscodingSessionController::onThrottlingStarted() {
+    std::scoped_lock lock{mLock};
+
+    if (mThermalThrottling) {
+        return;
+    }
+
+    ALOGI("%s", __FUNCTION__);
+
+    mThermalThrottling = true;
+    updateCurrentSession_l();
+
+    validateState_l();
+}
+
+void TranscodingSessionController::onThrottlingStopped() {
+    std::scoped_lock lock{mLock};
+
+    if (!mThermalThrottling) {
+        return;
+    }
+
+    ALOGI("%s", __FUNCTION__);
+
+    mThermalThrottling = false;
+    updateCurrentSession_l();
+
+    validateState_l();
+}
+
 void TranscodingSessionController::validateState_l() {
 #ifdef VALIDATE_STATE
     LOG_ALWAYS_FATAL_IF(mSessionQueues.count(OFFLINE_UID) != 1,
diff --git a/media/libmediatranscoding/TranscodingThermalPolicy.cpp b/media/libmediatranscoding/TranscodingThermalPolicy.cpp
new file mode 100644
index 0000000..9984abe
--- /dev/null
+++ b/media/libmediatranscoding/TranscodingThermalPolicy.cpp
@@ -0,0 +1,133 @@
+/*
+ * 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 LOG_NDEBUG 0
+#define LOG_TAG "TranscodingThermalPolicy"
+
+#include <media/TranscodingThermalPolicy.h>
+#include <media/TranscodingUidPolicy.h>
+#include <utils/Log.h>
+
+namespace android {
+
+static bool needThrottling(AThermalStatus status) {
+    return (status >= ATHERMAL_STATUS_SEVERE);
+}
+
+//static
+void TranscodingThermalPolicy::onStatusChange(void* data, AThermalStatus status) {
+    TranscodingThermalPolicy* policy = static_cast<TranscodingThermalPolicy*>(data);
+    policy->onStatusChange(status);
+}
+
+TranscodingThermalPolicy::TranscodingThermalPolicy()
+      : mRegistered(false), mThermalManager(nullptr), mIsThrottling(false) {
+    registerSelf();
+}
+
+TranscodingThermalPolicy::~TranscodingThermalPolicy() {
+    unregisterSelf();
+}
+
+void TranscodingThermalPolicy::registerSelf() {
+    ALOGI("TranscodingThermalPolicy: registerSelf");
+
+    std::scoped_lock lock{mRegisteredLock};
+
+    if (mRegistered) {
+        return;
+    }
+
+    if (__builtin_available(android __TRANSCODING_MIN_API__, *)) {
+        AThermalManager* thermalManager = AThermal_acquireManager();
+        if (thermalManager == nullptr) {
+            ALOGE("Failed to acquire thermal manager");
+            return;
+        }
+
+        int ret = AThermal_registerThermalStatusListener(thermalManager, onStatusChange, this);
+        if (ret != 0) {
+            ALOGE("Failed to register thermal status listener");
+            AThermal_releaseManager(thermalManager);
+            return;
+        }
+
+        mIsThrottling = needThrottling(AThermal_getCurrentThermalStatus(thermalManager));
+        mThermalManager = thermalManager;
+    }
+
+    mRegistered = true;
+}
+
+void TranscodingThermalPolicy::unregisterSelf() {
+    ALOGI("TranscodingThermalPolicy: unregisterSelf");
+
+    std::scoped_lock lock{mRegisteredLock};
+
+    if (!mRegistered) {
+        return;
+    }
+
+    if (__builtin_available(android __TRANSCODING_MIN_API__, *)) {
+        if (mThermalManager != nullptr) {
+            // Unregister listener
+            int ret =
+                    AThermal_unregisterThermalStatusListener(mThermalManager, onStatusChange, this);
+            if (ret != 0) {
+                ALOGW("Failed to unregister thermal status listener");
+            }
+            AThermal_releaseManager(mThermalManager);
+            mThermalManager = nullptr;
+        }
+    }
+
+    mRegistered = false;
+}
+
+void TranscodingThermalPolicy::setCallback(
+        const std::shared_ptr<ThermalPolicyCallbackInterface>& cb) {
+    std::scoped_lock lock{mCallbackLock};
+    mThermalPolicyCallback = cb;
+}
+
+bool TranscodingThermalPolicy::getThrottlingStatus() {
+    std::scoped_lock lock{mRegisteredLock};
+    return mIsThrottling;
+}
+
+void TranscodingThermalPolicy::onStatusChange(AThermalStatus status) {
+    bool isThrottling = needThrottling(status);
+
+    {
+        std::scoped_lock lock{mRegisteredLock};
+        if (isThrottling == mIsThrottling) {
+            return;
+        }
+        ALOGI("Transcoding thermal throttling changed: %d", isThrottling);
+        mIsThrottling = isThrottling;
+    }
+
+    std::scoped_lock lock{mCallbackLock};
+    std::shared_ptr<ThermalPolicyCallbackInterface> cb;
+    if ((cb = mThermalPolicyCallback.lock()) != nullptr) {
+        if (isThrottling) {
+            cb->onThrottlingStarted();
+        } else {
+            cb->onThrottlingStopped();
+        }
+    }
+}
+}  // namespace android
diff --git a/media/libmediatranscoding/TranscodingUidPolicy.cpp b/media/libmediatranscoding/TranscodingUidPolicy.cpp
index feaecc6..b5eb028 100644
--- a/media/libmediatranscoding/TranscodingUidPolicy.cpp
+++ b/media/libmediatranscoding/TranscodingUidPolicy.cpp
@@ -32,9 +32,7 @@
 constexpr static int32_t IMPORTANCE_UNKNOWN = INT32_MAX;
 
 TranscodingUidPolicy::TranscodingUidPolicy()
-      : mUidObserver(nullptr),
-        mRegistered(false),
-        mTopUidState(IMPORTANCE_UNKNOWN) {
+      : mUidObserver(nullptr), mRegistered(false), mTopUidState(IMPORTANCE_UNKNOWN) {
     registerSelf();
 }
 
@@ -129,8 +127,7 @@
 bool TranscodingUidPolicy::isUidOnTop(uid_t uid) {
     Mutex::Autolock _l(mUidLock);
 
-    return mTopUidState != IMPORTANCE_UNKNOWN &&
-           mTopUidState == getProcState_l(uid);
+    return mTopUidState != IMPORTANCE_UNKNOWN && mTopUidState == getProcState_l(uid);
 }
 
 std::unordered_set<uid_t> TranscodingUidPolicy::getTopUids() const {
@@ -155,12 +152,10 @@
             // Top set changed if 1) the uid is in the current top uid set, or 2) the
             // new procState is at least the same priority as the current top uid state.
             bool isUidCurrentTop =
-                    mTopUidState != IMPORTANCE_UNKNOWN &&
-                    mStateUidMap[mTopUidState].count(uid) > 0;
+                    mTopUidState != IMPORTANCE_UNKNOWN && mStateUidMap[mTopUidState].count(uid) > 0;
             bool isNewStateHigherThanTop =
                     procState != IMPORTANCE_UNKNOWN &&
-                    (procState <= mTopUidState ||
-                     mTopUidState == IMPORTANCE_UNKNOWN);
+                    (procState <= mTopUidState || mTopUidState == IMPORTANCE_UNKNOWN);
             topUidSetChanged = (isUidCurrentTop || isNewStateHigherThanTop);
 
             // Move uid to the new procState.
@@ -192,8 +187,7 @@
 
     // Find the lowest uid state (ignoring PROCESS_STATE_UNKNOWN) with some monitored uids.
     for (auto stateIt = mStateUidMap.begin(); stateIt != mStateUidMap.end(); stateIt++) {
-        if (stateIt->first != IMPORTANCE_UNKNOWN &&
-            !stateIt->second.empty()) {
+        if (stateIt->first != IMPORTANCE_UNKNOWN && !stateIt->second.empty()) {
             mTopUidState = stateIt->first;
             break;
         }
diff --git a/media/libmediatranscoding/include/media/ThermalPolicyInterface.h b/media/libmediatranscoding/include/media/ThermalPolicyInterface.h
new file mode 100644
index 0000000..1cc0cc1
--- /dev/null
+++ b/media/libmediatranscoding/include/media/ThermalPolicyInterface.h
@@ -0,0 +1,52 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_MEDIA_THERMAL_POLICY_INTERFACE_H
+#define ANDROID_MEDIA_THERMAL_POLICY_INTERFACE_H
+#include <memory>
+
+namespace android {
+
+class ThermalPolicyCallbackInterface;
+
+// Interface for the SessionController to control the thermal policy.
+class ThermalPolicyInterface {
+public:
+    // Set the associated callback interface to send the events when the thermal
+    // status changes.
+    virtual void setCallback(const std::shared_ptr<ThermalPolicyCallbackInterface>& cb) = 0;
+
+    // Get the current thermal throttling status. Returns true if throttling is on,
+    // false otherwise.
+    virtual bool getThrottlingStatus() = 0;
+
+protected:
+    virtual ~ThermalPolicyInterface() = default;
+};
+
+// Interface for notifying the SessionController of thermal throttling status.
+class ThermalPolicyCallbackInterface {
+public:
+    // Called when the session controller should start or stop thermal throttling.
+    virtual void onThrottlingStarted() = 0;
+    virtual void onThrottlingStopped() = 0;
+
+protected:
+    virtual ~ThermalPolicyCallbackInterface() = default;
+};
+
+}  // namespace android
+#endif  // ANDROID_MEDIA_THERMAL_POLICY_INTERFACE_H
diff --git a/media/libmediatranscoding/include/media/TranscodingSessionController.h b/media/libmediatranscoding/include/media/TranscodingSessionController.h
index a443265..4fcc423 100644
--- a/media/libmediatranscoding/include/media/TranscodingSessionController.h
+++ b/media/libmediatranscoding/include/media/TranscodingSessionController.h
@@ -20,6 +20,7 @@
 #include <aidl/android/media/TranscodingSessionPriority.h>
 #include <media/ControllerClientInterface.h>
 #include <media/ResourcePolicyInterface.h>
+#include <media/ThermalPolicyInterface.h>
 #include <media/TranscoderInterface.h>
 #include <media/TranscodingRequest.h>
 #include <media/UidPolicyInterface.h>
@@ -38,7 +39,8 @@
 class TranscodingSessionController : public UidPolicyCallbackInterface,
                                      public ControllerClientInterface,
                                      public TranscoderCallbackInterface,
-                                     public ResourcePolicyCallbackInterface {
+                                     public ResourcePolicyCallbackInterface,
+                                     public ThermalPolicyCallbackInterface {
 public:
     virtual ~TranscodingSessionController();
 
@@ -70,6 +72,11 @@
     void onResourceAvailable() override;
     // ~ResourcePolicyCallbackInterface
 
+    // ThermalPolicyCallbackInterface
+    void onThrottlingStarted() override;
+    void onThrottlingStopped() override;
+    // ~ResourcePolicyCallbackInterface
+
     /**
      * Dump all the session information to the fd.
      */
@@ -88,6 +95,8 @@
             NOT_STARTED = 0,
             RUNNING,
             PAUSED,
+            // The following states would not appear in live sessions map, but could
+            // appear in past sessions map for logging purpose.
             FINISHED,
             CANCELED,
             ERROR,
@@ -130,15 +139,18 @@
     std::shared_ptr<TranscoderInterface> mTranscoder;
     std::shared_ptr<UidPolicyInterface> mUidPolicy;
     std::shared_ptr<ResourcePolicyInterface> mResourcePolicy;
+    std::shared_ptr<ThermalPolicyInterface> mThermalPolicy;
 
     Session* mCurrentSession;
     bool mResourceLost;
+    bool mThermalThrottling;
     std::list<Session> mSessionHistory;
 
     // Only allow MediaTranscodingService and unit tests to instantiate.
     TranscodingSessionController(const std::shared_ptr<TranscoderInterface>& transcoder,
                                  const std::shared_ptr<UidPolicyInterface>& uidPolicy,
-                                 const std::shared_ptr<ResourcePolicyInterface>& resourcePolicy);
+                                 const std::shared_ptr<ResourcePolicyInterface>& resourcePolicy,
+                                 const std::shared_ptr<ThermalPolicyInterface>& thermalPolicy);
 
     void dumpSession_l(const Session& session, String8& result, bool closedSession = false);
     Session* getTopSession_l();
diff --git a/media/libmediatranscoding/include/media/TranscodingThermalPolicy.h b/media/libmediatranscoding/include/media/TranscodingThermalPolicy.h
new file mode 100644
index 0000000..81c6eac
--- /dev/null
+++ b/media/libmediatranscoding/include/media/TranscodingThermalPolicy.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.
+ */
+
+#ifndef ANDROID_MEDIA_TRANSCODING_THERMAL_POLICY_H
+#define ANDROID_MEDIA_TRANSCODING_THERMAL_POLICY_H
+
+#include <android/thermal.h>
+#include <media/ThermalPolicyInterface.h>
+#include <utils/Condition.h>
+
+#include <mutex>
+
+namespace android {
+
+class TranscodingThermalPolicy : public ThermalPolicyInterface {
+public:
+    explicit TranscodingThermalPolicy();
+    ~TranscodingThermalPolicy();
+
+    void setCallback(const std::shared_ptr<ThermalPolicyCallbackInterface>& cb) override;
+    bool getThrottlingStatus() override;
+
+private:
+    mutable std::mutex mRegisteredLock;
+    bool mRegistered GUARDED_BY(mRegisteredLock);
+
+    mutable std::mutex mCallbackLock;
+    std::weak_ptr<ThermalPolicyCallbackInterface> mThermalPolicyCallback GUARDED_BY(mCallbackLock);
+
+    AThermalManager* mThermalManager;
+    bool mIsThrottling;
+
+    static void onStatusChange(void* data, AThermalStatus status);
+    void onStatusChange(AThermalStatus status);
+    void registerSelf();
+    void unregisterSelf();
+};
+
+}  // namespace android
+#endif  // ANDROID_MEDIA_TRANSCODING_THERMAL_POLICY_H
diff --git a/media/libmediatranscoding/tests/TranscodingSessionController_tests.cpp b/media/libmediatranscoding/tests/TranscodingSessionController_tests.cpp
index fa52f63..9a1c272 100644
--- a/media/libmediatranscoding/tests/TranscodingSessionController_tests.cpp
+++ b/media/libmediatranscoding/tests/TranscodingSessionController_tests.cpp
@@ -89,9 +89,7 @@
 
     // ResourcePolicyInterface
     void setCallback(const std::shared_ptr<ResourcePolicyCallbackInterface>& /*cb*/) override {}
-    void setPidResourceLost(pid_t pid) override {
-        mResourceLostPid = pid;
-    }
+    void setPidResourceLost(pid_t pid) override { mResourceLostPid = pid; }
     // ~ResourcePolicyInterface
 
     pid_t getPid() {
@@ -101,12 +99,23 @@
     }
 
 private:
-    void reset() {
-        mResourceLostPid = kInvalidPid;
-    }
+    void reset() { mResourceLostPid = kInvalidPid; }
     pid_t mResourceLostPid;
 };
 
+class TestThermalPolicy : public ThermalPolicyInterface {
+public:
+    TestThermalPolicy() = default;
+    virtual ~TestThermalPolicy() = default;
+
+    // ThermalPolicyInterface
+    void setCallback(const std::shared_ptr<ThermalPolicyCallbackInterface>& /*cb*/) override {}
+    bool getThrottlingStatus() { return false; }
+    // ~ThermalPolicyInterface
+
+private:
+};
+
 class TestTranscoder : public TranscoderInterface {
 public:
     TestTranscoder() : mLastError(TranscodingErrorCode::kUnknown) {}
@@ -245,8 +254,9 @@
         mTranscoder.reset(new TestTranscoder());
         mUidPolicy.reset(new TestUidPolicy());
         mResourcePolicy.reset(new TestResourcePolicy());
-        mController.reset(
-                new TranscodingSessionController(mTranscoder, mUidPolicy, mResourcePolicy));
+        mThermalPolicy.reset(new TestThermalPolicy());
+        mController.reset(new TranscodingSessionController(mTranscoder, mUidPolicy, mResourcePolicy,
+                                                           mThermalPolicy));
         mUidPolicy->setCallback(mController);
 
         // Set priority only, ignore other fields for now.
@@ -269,6 +279,7 @@
     std::shared_ptr<TestTranscoder> mTranscoder;
     std::shared_ptr<TestUidPolicy> mUidPolicy;
     std::shared_ptr<TestResourcePolicy> mResourcePolicy;
+    std::shared_ptr<TestThermalPolicy> mThermalPolicy;
     std::shared_ptr<TranscodingSessionController> mController;
     TranscodingRequestParcel mOfflineRequest;
     TranscodingRequestParcel mRealtimeRequest;
@@ -577,6 +588,7 @@
     EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(1), SESSION(0)));
 }
 
+/* Test resource lost without thermal throttling */
 TEST_F(TranscodingSessionControllerTest, TestResourceLost) {
     ALOGD("TestResourceLost");
 
@@ -633,11 +645,22 @@
     mController->onResourceAvailable();
     EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Resume(CLIENT(0), SESSION(0)));
 
-    // Test 3: Adding new queue during resource loss.
-    // Signal resource lost.
+    // Test 3:
     mController->onResourceLost(CLIENT(0), SESSION(0));
     EXPECT_EQ(mResourcePolicy->getPid(), PID(0));
     EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
+    // Cancel the paused top session during resource lost.
+    EXPECT_TRUE(mController->cancel(CLIENT(0), SESSION(0)));
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Stop(CLIENT(0), SESSION(0)));
+    // Signal resource available, CLIENT(2)'s session should start.
+    mController->onResourceAvailable();
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Resume(CLIENT(2), SESSION(0)));
+
+    // Test 4: Adding new queue during resource loss.
+    // Signal resource lost.
+    mController->onResourceLost(CLIENT(2), SESSION(0));
+    EXPECT_EQ(mResourcePolicy->getPid(), PID(1));
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
 
     // Move UID(2) to top.
     mUidPolicy->setTop(UID(2));
@@ -652,4 +675,131 @@
     EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(3), SESSION(0)));
 }
 
+/* Test thermal throttling without resource lost */
+TEST_F(TranscodingSessionControllerTest, TestThermalCallback) {
+    ALOGD("TestThermalCallback");
+
+    // Start with unspecified top UID.
+    // Submit real-time session to CLIENT(0), session should start immediately.
+    mRealtimeRequest.clientPid = PID(0);
+    mController->submit(CLIENT(0), SESSION(0), UID(0), mRealtimeRequest, mClientCallback0);
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), SESSION(0)));
+
+    // Submit offline session to CLIENT(0), should not start.
+    mOfflineRequest.clientPid = PID(0);
+    mController->submit(CLIENT(1), SESSION(0), UID(0), mOfflineRequest, mClientCallback1);
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
+
+    // Move UID(1) to top.
+    mUidPolicy->setTop(UID(1));
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
+
+    // Submit real-time session to CLIENT(2) in different uid UID(1).
+    // Should pause previous session and start new session.
+    mRealtimeRequest.clientPid = PID(1);
+    mController->submit(CLIENT(2), SESSION(0), UID(1), mRealtimeRequest, mClientCallback2);
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Pause(CLIENT(0), SESSION(0)));
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(2), SESSION(0)));
+
+    // Test 0: Basic case, no queue change during throttling, top session should pause/resume
+    // with throttling.
+    mController->onThrottlingStarted();
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Pause(CLIENT(2), SESSION(0)));
+    mController->onThrottlingStopped();
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Resume(CLIENT(2), SESSION(0)));
+
+    // Test 1: Change of queue order during thermal throttling, when throttling stops,
+    // new top session should resume.
+    mController->onThrottlingStarted();
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Pause(CLIENT(2), SESSION(0)));
+    mUidPolicy->setTop(UID(0));
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
+    mController->onThrottlingStopped();
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Resume(CLIENT(0), SESSION(0)));
+
+    // Test 2: Cancel session during throttling, when throttling stops, new top
+    // session should resume.
+    mController->onThrottlingStarted();
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Pause(CLIENT(0), SESSION(0)));
+    // Cancel the paused top session during throttling.
+    EXPECT_TRUE(mController->cancel(CLIENT(0), SESSION(0)));
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Stop(CLIENT(0), SESSION(0)));
+    // Throttling stops, CLIENT(2)'s session should start.
+    mController->onThrottlingStopped();
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Resume(CLIENT(2), SESSION(0)));
+
+    // Test 3: Add new queue during throttling, when throttling stops, new top
+    // session should resume.
+    mController->onThrottlingStarted();
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Pause(CLIENT(2), SESSION(0)));
+    // Move UID(2) to top.
+    mUidPolicy->setTop(UID(2));
+    // Submit real-time session to CLIENT(3) in UID(2), session shouldn't start during throttling.
+    mRealtimeRequest.clientPid = PID(2);
+    mController->submit(CLIENT(3), SESSION(0), UID(2), mRealtimeRequest, mClientCallback3);
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
+    // Throttling stops, CLIENT(3)'s session should start.
+    mController->onThrottlingStopped();
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(3), SESSION(0)));
+}
+
+/* Test resource lost and thermal throttling happening simultaneously */
+TEST_F(TranscodingSessionControllerTest, TestResourceLostAndThermalCallback) {
+    ALOGD("TestResourceLostAndThermalCallback");
+
+    // Start with unspecified top UID.
+    // Submit real-time session to CLIENT(0), session should start immediately.
+    mRealtimeRequest.clientPid = PID(0);
+    mController->submit(CLIENT(0), SESSION(0), UID(0), mRealtimeRequest, mClientCallback0);
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), SESSION(0)));
+
+    // Submit offline session to CLIENT(0), should not start.
+    mOfflineRequest.clientPid = PID(0);
+    mController->submit(CLIENT(1), SESSION(0), UID(0), mOfflineRequest, mClientCallback1);
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
+
+    // Move UID(1) to top.
+    mUidPolicy->setTop(UID(1));
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
+
+    // Submit real-time session to CLIENT(2) in different uid UID(1).
+    // Should pause previous session and start new session.
+    mRealtimeRequest.clientPid = PID(1);
+    mController->submit(CLIENT(2), SESSION(0), UID(1), mRealtimeRequest, mClientCallback2);
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Pause(CLIENT(0), SESSION(0)));
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(2), SESSION(0)));
+
+    // Test 0: Resource lost during throttling.
+    // Throttling starts, top session should pause.
+    mController->onThrottlingStarted();
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Pause(CLIENT(2), SESSION(0)));
+    // Signal resource lost, this should get ignored because the session is now paused.
+    mController->onResourceLost(CLIENT(2), SESSION(0));
+    EXPECT_EQ(mResourcePolicy->getPid(), kInvalidPid);
+    // Signal resource available, CLIENT(2) shouldn't resume.
+    mController->onResourceAvailable();
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
+    // Throttling ends, top session should resume.
+    mController->onThrottlingStopped();
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Resume(CLIENT(2), SESSION(0)));
+
+    // Test 1: Throttling during resource lost.
+    mController->onResourceLost(CLIENT(2), SESSION(0));
+    EXPECT_EQ(mResourcePolicy->getPid(), PID(1));
+    mController->onThrottlingStarted();
+    mController->onThrottlingStopped();
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
+    mController->onResourceAvailable();
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Resume(CLIENT(2), SESSION(0)));
+
+    // Test 2: Interleaving resource lost and throttling.
+    mController->onResourceLost(CLIENT(2), SESSION(0));
+    EXPECT_EQ(mResourcePolicy->getPid(), PID(1));
+    mController->onThrottlingStarted();
+    mController->onResourceAvailable();
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
+    mController->onThrottlingStopped();
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Resume(CLIENT(2), SESSION(0)));
+}
+
 }  // namespace android
diff --git a/services/mediatranscoding/Android.bp b/services/mediatranscoding/Android.bp
index 48c2584..532e175 100644
--- a/services/mediatranscoding/Android.bp
+++ b/services/mediatranscoding/Android.bp
@@ -1,5 +1,5 @@
 // service library
-cc_library_shared {
+cc_library {
     name: "libmediatranscodingservice",
 
     srcs: [
diff --git a/services/mediatranscoding/MediaTranscodingService.cpp b/services/mediatranscoding/MediaTranscodingService.cpp
index b94dd01..5c8cc1a 100644
--- a/services/mediatranscoding/MediaTranscodingService.cpp
+++ b/services/mediatranscoding/MediaTranscodingService.cpp
@@ -26,6 +26,7 @@
 #include <media/TranscodingClientManager.h>
 #include <media/TranscodingResourcePolicy.h>
 #include <media/TranscodingSessionController.h>
+#include <media/TranscodingThermalPolicy.h>
 #include <media/TranscodingUidPolicy.h>
 #include <utils/Log.h>
 #include <utils/Vector.h>
@@ -44,13 +45,15 @@
         const std::shared_ptr<TranscoderInterface>& transcoder)
       : mUidPolicy(new TranscodingUidPolicy()),
         mResourcePolicy(new TranscodingResourcePolicy()),
-        mSessionController(
-                new TranscodingSessionController(transcoder, mUidPolicy, mResourcePolicy)),
+        mThermalPolicy(new TranscodingThermalPolicy()),
+        mSessionController(new TranscodingSessionController(transcoder, mUidPolicy, mResourcePolicy,
+                                                            mThermalPolicy)),
         mClientManager(new TranscodingClientManager(mSessionController)) {
     ALOGV("MediaTranscodingService is created");
     transcoder->setCallback(mSessionController);
     mUidPolicy->setCallback(mSessionController);
     mResourcePolicy->setCallback(mSessionController);
+    mThermalPolicy->setCallback(mSessionController);
 }
 
 MediaTranscodingService::~MediaTranscodingService() {
diff --git a/services/mediatranscoding/MediaTranscodingService.h b/services/mediatranscoding/MediaTranscodingService.h
index 428f777..a22acf2 100644
--- a/services/mediatranscoding/MediaTranscodingService.h
+++ b/services/mediatranscoding/MediaTranscodingService.h
@@ -33,6 +33,7 @@
 class TranscoderInterface;
 class UidPolicyInterface;
 class ResourcePolicyInterface;
+class ThermalPolicyInterface;
 
 class MediaTranscodingService : public BnMediaTranscodingService {
 public:
@@ -61,6 +62,7 @@
 
     std::shared_ptr<UidPolicyInterface> mUidPolicy;
     std::shared_ptr<ResourcePolicyInterface> mResourcePolicy;
+    std::shared_ptr<ThermalPolicyInterface> mThermalPolicy;
     std::shared_ptr<TranscodingSessionController> mSessionController;
     std::shared_ptr<TranscodingClientManager> mClientManager;
 };
diff --git a/services/mediatranscoding/tests/Android.bp b/services/mediatranscoding/tests/Android.bp
index 5a7c4cc..9d3e56c 100644
--- a/services/mediatranscoding/tests/Android.bp
+++ b/services/mediatranscoding/tests/Android.bp
@@ -18,13 +18,13 @@
         "libbinder_ndk",
         "liblog",
         "libutils",
-        "libmediatranscodingservice",
         "libcutils",
     ],
 
     static_libs: [
         "mediatranscoding_aidl_interface-ndk_platform",
         "resourcemanager_aidl_interface-ndk_platform",
+        "libmediatranscodingservice",
     ],
 
     required: [
diff --git a/services/mediatranscoding/tests/mediatranscodingservice_simulated_tests.cpp b/services/mediatranscoding/tests/mediatranscodingservice_simulated_tests.cpp
index 7dfda44..601bb1b 100644
--- a/services/mediatranscoding/tests/mediatranscodingservice_simulated_tests.cpp
+++ b/services/mediatranscoding/tests/mediatranscodingservice_simulated_tests.cpp
@@ -354,5 +354,36 @@
     ALOGD("TestTranscodingUidPolicy finished.");
 }
 
+TEST_F(MediaTranscodingServiceSimulatedTest, TestTranscodingThermalPolicy) {
+    ALOGD("TestTranscodingThermalPolicy starting...");
+
+    registerMultipleClients();
+
+    // Submit request, should start immediately.
+    EXPECT_TRUE(mClient1->submit(0, "test_source_file_0", "test_destination_file_0"));
+    EXPECT_EQ(mClient1->pop(kPaddingUs), EventTracker::Start(CLIENT(1), 0));
+
+    // Now, simulate thermal status change by adb cmd. The status code is as defined in
+    // frameworks/native/include/android/thermal.h.
+    // ATHERMAL_STATUS_SEVERE(3): should start throttling.
+    EXPECT_TRUE(ShellHelper::RunCmd("cmd thermalservice override-status 3"));
+    EXPECT_EQ(mClient1->pop(kPaddingUs), EventTracker::Pause(CLIENT(1), 0));
+
+    // ATHERMAL_STATUS_CRITICAL(4): shouldn't start throttling again (already started).
+    EXPECT_TRUE(ShellHelper::RunCmd("cmd thermalservice override-status 4"));
+    EXPECT_EQ(mClient1->pop(kPaddingUs), EventTracker::NoEvent);
+
+    // ATHERMAL_STATUS_MODERATE(2): should stop throttling.
+    EXPECT_TRUE(ShellHelper::RunCmd("cmd thermalservice override-status 2"));
+    EXPECT_EQ(mClient1->pop(kPaddingUs), EventTracker::Resume(CLIENT(1), 0));
+
+    // ATHERMAL_STATUS_LIGHT(1): shouldn't stop throttling again (already stopped).
+    EXPECT_TRUE(ShellHelper::RunCmd("cmd thermalservice override-status 1"));
+    EXPECT_EQ(mClient1->pop(kSessionWithPaddingUs), EventTracker::Finished(CLIENT(1), 0));
+
+    unregisterMultipleClients();
+
+    ALOGD("TestTranscodingThermalPolicy finished.");
+}
 }  // namespace media
 }  // namespace android