Register resource observer and handle resource lost.

bug: 168307955
bug: 154733526
Change-Id: I2d5e39ce23bc873dd3bac090e3f7aa1124d0a579
diff --git a/media/libmediatranscoding/Android.bp b/media/libmediatranscoding/Android.bp
index 2e66df7..128d0d8 100644
--- a/media/libmediatranscoding/Android.bp
+++ b/media/libmediatranscoding/Android.bp
@@ -54,6 +54,7 @@
     srcs: [
         "TranscodingClientManager.cpp",
         "TranscodingJobScheduler.cpp",
+        "TranscodingResourcePolicy.cpp",
         "TranscodingUidPolicy.cpp",
         "TranscoderWrapper.cpp",
     ],
@@ -67,12 +68,16 @@
         "libbinder",
         "libmediandk",
     ],
+    export_shared_lib_headers: [
+        "libmediandk",
+    ],
 
     export_include_dirs: ["include"],
 
     static_libs: [
         "mediatranscoding_aidl_interface-ndk_platform",
         "resourcemanager_aidl_interface-ndk_platform",
+        "resourceobserver_aidl_interface-ndk_platform",
     ],
 
     cflags: [
diff --git a/media/libmediatranscoding/TranscoderWrapper.cpp b/media/libmediatranscoding/TranscoderWrapper.cpp
index bd03671..8062fcf 100644
--- a/media/libmediatranscoding/TranscoderWrapper.cpp
+++ b/media/libmediatranscoding/TranscoderWrapper.cpp
@@ -89,26 +89,40 @@
 }
 
 //static
-const char* TranscoderWrapper::toString(Event::Type type) {
-    switch (type) {
+std::string TranscoderWrapper::toString(const Event& event) {
+    std::string typeStr;
+    switch (event.type) {
     case Event::Start:
-        return "Start";
-    case Event::Pause:
-        return "Pause";
-    case Event::Resume:
-        return "Resume";
-    case Event::Stop:
-        return "Stop";
-    case Event::Finish:
-        return "Finish";
-    case Event::Error:
-        return "Error";
-    case Event::Progress:
-        return "Progress";
-    default:
+        typeStr = "Start";
         break;
+    case Event::Pause:
+        typeStr = "Pause";
+        break;
+    case Event::Resume:
+        typeStr = "Resume";
+        break;
+    case Event::Stop:
+        typeStr = "Stop";
+        break;
+    case Event::Finish:
+        typeStr = "Finish";
+        break;
+    case Event::Error:
+        typeStr = "Error";
+        break;
+    case Event::Progress:
+        typeStr = "Progress";
+        break;
+    default:
+        return "(unknown)";
     }
-    return "(unknown)";
+    std::string result;
+    result = "job {" + std::to_string(event.clientId) + "," + std::to_string(event.jobId) +
+             "}: " + typeStr;
+    if (event.type == Event::Error || event.type == Event::Progress) {
+        result += " " + std::to_string(event.arg);
+    }
+    return result;
 }
 
 class TranscoderWrapper::CallbackImpl : public MediaTranscoder::CallbackInterface {
@@ -128,7 +142,7 @@
                          media_status_t error) override {
         auto owner = mOwner.lock();
         if (owner != nullptr) {
-            owner->onError(mClientId, mJobId, toTranscodingError(error));
+            owner->onError(mClientId, mJobId, error);
         }
     }
 
@@ -160,20 +174,41 @@
     mCallback = cb;
 }
 
+static bool isResourceError(media_status_t err) {
+    return err == AMEDIACODEC_ERROR_RECLAIMED || err == AMEDIACODEC_ERROR_INSUFFICIENT_RESOURCE;
+}
+
+void TranscoderWrapper::reportError(ClientIdType clientId, JobIdType jobId, media_status_t err) {
+    auto callback = mCallback.lock();
+    if (callback != nullptr) {
+        if (isResourceError(err)) {
+            // Add a placeholder pause state to mPausedStateMap. This is required when resuming.
+            // TODO: remove this when transcoder pause/resume logic is ready. New logic will
+            // no longer use the pause states.
+            auto it = mPausedStateMap.find(JobKeyType(clientId, jobId));
+            if (it == mPausedStateMap.end()) {
+                mPausedStateMap.emplace(JobKeyType(clientId, jobId),
+                                        std::shared_ptr<const Parcel>());
+            }
+
+            callback->onResourceLost();
+        } else {
+            callback->onError(clientId, jobId, toTranscodingError(err));
+        }
+    }
+}
+
 void TranscoderWrapper::start(ClientIdType clientId, JobIdType jobId,
                               const TranscodingRequestParcel& request,
                               const std::shared_ptr<ITranscodingClientCallback>& clientCb) {
     queueEvent(Event::Start, clientId, jobId, [=] {
-        TranscodingErrorCode err = handleStart(clientId, jobId, request, clientCb);
+        media_status_t err = handleStart(clientId, jobId, request, clientCb);
 
-        auto callback = mCallback.lock();
-        if (err != TranscodingErrorCode::kNoError) {
+        if (err != AMEDIA_OK) {
             cleanup();
-
-            if (callback != nullptr) {
-                callback->onError(clientId, jobId, err);
-            }
+            reportError(clientId, jobId, err);
         } else {
+            auto callback = mCallback.lock();
             if (callback != nullptr) {
                 callback->onStarted(clientId, jobId);
             }
@@ -183,15 +218,15 @@
 
 void TranscoderWrapper::pause(ClientIdType clientId, JobIdType jobId) {
     queueEvent(Event::Pause, clientId, jobId, [=] {
-        TranscodingErrorCode err = handlePause(clientId, jobId);
+        media_status_t err = handlePause(clientId, jobId);
 
         cleanup();
 
-        auto callback = mCallback.lock();
-        if (callback != nullptr) {
-            if (err != TranscodingErrorCode::kNoError) {
-                callback->onError(clientId, jobId, err);
-            } else {
+        if (err != AMEDIA_OK) {
+            reportError(clientId, jobId, err);
+        } else {
+            auto callback = mCallback.lock();
+            if (callback != nullptr) {
                 callback->onPaused(clientId, jobId);
             }
         }
@@ -202,16 +237,13 @@
                                const TranscodingRequestParcel& request,
                                const std::shared_ptr<ITranscodingClientCallback>& clientCb) {
     queueEvent(Event::Resume, clientId, jobId, [=] {
-        TranscodingErrorCode err = handleResume(clientId, jobId, request, clientCb);
+        media_status_t err = handleResume(clientId, jobId, request, clientCb);
 
-        auto callback = mCallback.lock();
-        if (err != TranscodingErrorCode::kNoError) {
+        if (err != AMEDIA_OK) {
             cleanup();
-
-            if (callback != nullptr) {
-                callback->onError(clientId, jobId, err);
-            }
+            reportError(clientId, jobId, err);
         } else {
+            auto callback = mCallback.lock();
             if (callback != nullptr) {
                 callback->onResumed(clientId, jobId);
             }
@@ -225,7 +257,7 @@
             // Cancelling the currently running job.
             media_status_t err = mTranscoder->cancel();
             if (err != AMEDIA_OK) {
-                ALOGE("failed to stop transcoder: %d", err);
+                ALOGW("failed to stop transcoder: %d", err);
             } else {
                 ALOGI("transcoder stopped");
             }
@@ -251,41 +283,43 @@
     });
 }
 
-void TranscoderWrapper::onError(ClientIdType clientId, JobIdType jobId,
-                                TranscodingErrorCode error) {
-    queueEvent(Event::Error, clientId, jobId, [=] {
-        if (mTranscoder != nullptr && clientId == mCurrentClientId && jobId == mCurrentJobId) {
-            cleanup();
-        }
-
-        auto callback = mCallback.lock();
-        if (callback != nullptr) {
-            callback->onError(clientId, jobId, error);
-        }
-    });
+void TranscoderWrapper::onError(ClientIdType clientId, JobIdType jobId, media_status_t error) {
+    queueEvent(
+            Event::Error, clientId, jobId,
+            [=] {
+                if (mTranscoder != nullptr && clientId == mCurrentClientId &&
+                    jobId == mCurrentJobId) {
+                    cleanup();
+                }
+                reportError(clientId, jobId, error);
+            },
+            error);
 }
 
 void TranscoderWrapper::onProgress(ClientIdType clientId, JobIdType jobId, int32_t progress) {
-    queueEvent(Event::Progress, clientId, jobId, [=] {
-        auto callback = mCallback.lock();
-        if (callback != nullptr) {
-            callback->onProgressUpdate(clientId, jobId, progress);
-        }
-    });
+    queueEvent(
+            Event::Progress, clientId, jobId,
+            [=] {
+                auto callback = mCallback.lock();
+                if (callback != nullptr) {
+                    callback->onProgressUpdate(clientId, jobId, progress);
+                }
+            },
+            progress);
 }
 
-TranscodingErrorCode TranscoderWrapper::setupTranscoder(
+media_status_t TranscoderWrapper::setupTranscoder(
         ClientIdType clientId, JobIdType jobId, const TranscodingRequestParcel& request,
         const std::shared_ptr<ITranscodingClientCallback>& clientCb,
         const std::shared_ptr<const Parcel>& pausedState) {
     if (clientCb == nullptr) {
         ALOGE("client callback is null");
-        return TranscodingErrorCode::kInvalidParameter;
+        return AMEDIA_ERROR_INVALID_PARAMETER;
     }
 
     if (mTranscoder != nullptr) {
         ALOGE("transcoder already running");
-        return TranscodingErrorCode::kInvalidOperation;
+        return AMEDIA_ERROR_INVALID_OPERATION;
     }
 
     Status status;
@@ -293,7 +327,7 @@
     status = clientCb->openFileDescriptor(request.sourceFilePath, "r", &srcFd);
     if (!status.isOk() || srcFd.get() < 0) {
         ALOGE("failed to open source");
-        return TranscodingErrorCode::kErrorIO;
+        return AMEDIA_ERROR_IO;
     }
 
     // Open dest file with "rw", as the transcoder could potentially reuse part of it
@@ -302,7 +336,7 @@
     status = clientCb->openFileDescriptor(request.destinationFilePath, "rw", &dstFd);
     if (!status.isOk() || dstFd.get() < 0) {
         ALOGE("failed to open destination");
-        return TranscodingErrorCode::kErrorIO;
+        return AMEDIA_ERROR_IO;
     }
 
     mCurrentClientId = clientId;
@@ -311,19 +345,19 @@
     mTranscoder = MediaTranscoder::create(mTranscoderCb, pausedState);
     if (mTranscoder == nullptr) {
         ALOGE("failed to create transcoder");
-        return TranscodingErrorCode::kUnknown;
+        return AMEDIA_ERROR_UNKNOWN;
     }
 
     media_status_t err = mTranscoder->configureSource(srcFd.get());
     if (err != AMEDIA_OK) {
         ALOGE("failed to configure source: %d", err);
-        return toTranscodingError(err);
+        return err;
     }
 
     std::vector<std::shared_ptr<AMediaFormat>> trackFormats = mTranscoder->getTrackFormats();
     if (trackFormats.size() == 0) {
         ALOGE("failed to get track formats!");
-        return TranscodingErrorCode::kMalformed;
+        return AMEDIA_ERROR_MALFORMED;
     }
 
     for (int i = 0; i < trackFormats.size(); ++i) {
@@ -341,43 +375,43 @@
         }
         if (err != AMEDIA_OK) {
             ALOGE("failed to configure track format for track %d: %d", i, err);
-            return toTranscodingError(err);
+            return err;
         }
     }
 
     err = mTranscoder->configureDestination(dstFd.get());
     if (err != AMEDIA_OK) {
         ALOGE("failed to configure dest: %d", err);
-        return toTranscodingError(err);
+        return err;
     }
 
-    return TranscodingErrorCode::kNoError;
+    return AMEDIA_OK;
 }
 
-TranscodingErrorCode TranscoderWrapper::handleStart(
+media_status_t TranscoderWrapper::handleStart(
         ClientIdType clientId, JobIdType jobId, const TranscodingRequestParcel& request,
         const std::shared_ptr<ITranscodingClientCallback>& clientCb) {
-    ALOGI("setting up transcoder for start");
-    TranscodingErrorCode err = setupTranscoder(clientId, jobId, request, clientCb);
-    if (err != TranscodingErrorCode::kNoError) {
+    ALOGI("%s: setting up transcoder for start", __FUNCTION__);
+    media_status_t err = setupTranscoder(clientId, jobId, request, clientCb);
+    if (err != AMEDIA_OK) {
         ALOGI("%s: failed to setup transcoder", __FUNCTION__);
         return err;
     }
 
-    media_status_t status = mTranscoder->start();
-    if (status != AMEDIA_OK) {
+    err = mTranscoder->start();
+    if (err != AMEDIA_OK) {
         ALOGE("%s: failed to start transcoder: %d", __FUNCTION__, err);
-        return toTranscodingError(status);
+        return err;
     }
 
     ALOGI("%s: transcoder started", __FUNCTION__);
-    return TranscodingErrorCode::kNoError;
+    return AMEDIA_OK;
 }
 
-TranscodingErrorCode TranscoderWrapper::handlePause(ClientIdType clientId, JobIdType jobId) {
+media_status_t TranscoderWrapper::handlePause(ClientIdType clientId, JobIdType jobId) {
     if (mTranscoder == nullptr) {
         ALOGE("%s: transcoder is not running", __FUNCTION__);
-        return TranscodingErrorCode::kInvalidOperation;
+        return AMEDIA_ERROR_INVALID_OPERATION;
     }
 
     if (clientId != mCurrentClientId || jobId != mCurrentJobId) {
@@ -385,19 +419,21 @@
               (long long)clientId, jobId, (long long)mCurrentClientId, mCurrentJobId);
     }
 
+    ALOGI("%s: pausing transcoder", __FUNCTION__);
+
     std::shared_ptr<const Parcel> pauseStates;
     media_status_t err = mTranscoder->pause(&pauseStates);
     if (err != AMEDIA_OK) {
         ALOGE("%s: failed to pause transcoder: %d", __FUNCTION__, err);
-        return toTranscodingError(err);
+        return err;
     }
     mPausedStateMap[JobKeyType(clientId, jobId)] = pauseStates;
 
     ALOGI("%s: transcoder paused", __FUNCTION__);
-    return TranscodingErrorCode::kNoError;
+    return AMEDIA_OK;
 }
 
-TranscodingErrorCode TranscoderWrapper::handleResume(
+media_status_t TranscoderWrapper::handleResume(
         ClientIdType clientId, JobIdType jobId, const TranscodingRequestParcel& request,
         const std::shared_ptr<ITranscodingClientCallback>& clientCb) {
     std::shared_ptr<const Parcel> pausedState;
@@ -407,24 +443,24 @@
         mPausedStateMap.erase(it);
     } else {
         ALOGE("%s: can't find paused state", __FUNCTION__);
-        return TranscodingErrorCode::kInvalidOperation;
+        return AMEDIA_ERROR_INVALID_OPERATION;
     }
 
-    ALOGI("setting up transcoder for resume");
-    TranscodingErrorCode err = setupTranscoder(clientId, jobId, request, clientCb, pausedState);
-    if (err != TranscodingErrorCode::kNoError) {
-        ALOGE("%s: failed to setup transcoder", __FUNCTION__);
+    ALOGI("%s: setting up transcoder for resume", __FUNCTION__);
+    media_status_t err = setupTranscoder(clientId, jobId, request, clientCb, pausedState);
+    if (err != AMEDIA_OK) {
+        ALOGE("%s: failed to setup transcoder: %d", __FUNCTION__, err);
         return err;
     }
 
-    media_status_t status = mTranscoder->resume();
-    if (status != AMEDIA_OK) {
+    err = mTranscoder->resume();
+    if (err != AMEDIA_OK) {
         ALOGE("%s: failed to resume transcoder: %d", __FUNCTION__, err);
-        return toTranscodingError(status);
+        return err;
     }
 
     ALOGI("%s: transcoder resumed", __FUNCTION__);
-    return TranscodingErrorCode::kNoError;
+    return AMEDIA_OK;
 }
 
 void TranscoderWrapper::cleanup() {
@@ -435,12 +471,10 @@
 }
 
 void TranscoderWrapper::queueEvent(Event::Type type, ClientIdType clientId, JobIdType jobId,
-                                   const std::function<void()> runnable) {
-    ALOGV("%s: job {%lld, %d}: %s", __FUNCTION__, (long long)clientId, jobId, toString(type));
-
+                                   const std::function<void()> runnable, int32_t arg) {
     std::scoped_lock lock{mLock};
 
-    mQueue.push_back({type, clientId, jobId, runnable});
+    mQueue.push_back({type, clientId, jobId, runnable, arg});
     mCondition.notify_one();
 }
 
@@ -457,8 +491,7 @@
         Event event = *mQueue.begin();
         mQueue.pop_front();
 
-        ALOGD("%s: job {%lld, %d}: %s", __FUNCTION__, (long long)event.clientId, event.jobId,
-              toString(event.type));
+        ALOGD("%s: %s", __FUNCTION__, toString(event).c_str());
 
         lock.unlock();
         event.runnable();
diff --git a/media/libmediatranscoding/TranscodingJobScheduler.cpp b/media/libmediatranscoding/TranscodingJobScheduler.cpp
index 3e4f319..24ac682 100644
--- a/media/libmediatranscoding/TranscodingJobScheduler.cpp
+++ b/media/libmediatranscoding/TranscodingJobScheduler.cpp
@@ -38,8 +38,13 @@
 
 TranscodingJobScheduler::TranscodingJobScheduler(
         const std::shared_ptr<TranscoderInterface>& transcoder,
-        const std::shared_ptr<UidPolicyInterface>& uidPolicy)
-      : mTranscoder(transcoder), mUidPolicy(uidPolicy), mCurrentJob(nullptr), mResourceLost(false) {
+        const std::shared_ptr<UidPolicyInterface>& uidPolicy,
+        const std::shared_ptr<ResourcePolicyInterface>& resourcePolicy)
+      : mTranscoder(transcoder),
+        mUidPolicy(uidPolicy),
+        mResourcePolicy(resourcePolicy),
+        mCurrentJob(nullptr),
+        mResourceLost(false) {
     // Only push empty offline queue initially. Realtime queues are added when requests come in.
     mUidSortedList.push_back(OFFLINE_UID);
     mOfflineUidIterator = mUidSortedList.begin();
@@ -398,15 +403,24 @@
 }
 
 void TranscodingJobScheduler::onResourceLost() {
-    ALOGV("%s", __FUNCTION__);
+    ALOGI("%s", __FUNCTION__);
 
     std::scoped_lock lock{mLock};
 
+    if (mResourceLost) {
+        return;
+    }
+
     // If we receive a resource loss event, the TranscoderLibrary already paused
     // the transcoding, so we don't need to call onPaused to notify it to pause.
     // Only need to update the job state here.
     if (mCurrentJob != nullptr && mCurrentJob->state == Job::RUNNING) {
         mCurrentJob->state = Job::PAUSED;
+        // Notify the client as a paused event.
+        auto clientCallback = mCurrentJob->callback.lock();
+        if (clientCallback != nullptr) {
+            clientCallback->onTranscodingPaused(mCurrentJob->key.second);
+        }
     }
     mResourceLost = true;
 
@@ -439,10 +453,14 @@
 }
 
 void TranscodingJobScheduler::onResourceAvailable() {
-    ALOGV("%s", __FUNCTION__);
-
     std::scoped_lock lock{mLock};
 
+    if (!mResourceLost) {
+        return;
+    }
+
+    ALOGI("%s", __FUNCTION__);
+
     mResourceLost = false;
     updateCurrentJob_l();
 
diff --git a/media/libmediatranscoding/TranscodingResourcePolicy.cpp b/media/libmediatranscoding/TranscodingResourcePolicy.cpp
new file mode 100644
index 0000000..4fd8338
--- /dev/null
+++ b/media/libmediatranscoding/TranscodingResourcePolicy.cpp
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "TranscodingResourcePolicy"
+
+#include <aidl/android/media/BnResourceObserver.h>
+#include <aidl/android/media/IResourceObserverService.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
+#include <binder/IServiceManager.h>
+#include <media/TranscodingResourcePolicy.h>
+#include <utils/Log.h>
+
+namespace android {
+
+using Status = ::ndk::ScopedAStatus;
+using ::aidl::android::media::BnResourceObserver;
+using ::aidl::android::media::IResourceObserverService;
+using ::aidl::android::media::MediaObservableEvent;
+using ::aidl::android::media::MediaObservableFilter;
+using ::aidl::android::media::MediaObservableParcel;
+using ::aidl::android::media::MediaObservableType;
+
+static std::string toString(const MediaObservableParcel& observable) {
+    return "{" + ::aidl::android::media::toString(observable.type) + ", " +
+           std::to_string(observable.value) + "}";
+}
+
+struct TranscodingResourcePolicy::ResourceObserver : public BnResourceObserver {
+    explicit ResourceObserver(TranscodingResourcePolicy* owner) : mOwner(owner), mPid(getpid()) {}
+
+    // IResourceObserver
+    ::ndk::ScopedAStatus onStatusChanged(
+            MediaObservableEvent event, int32_t uid, int32_t pid,
+            const std::vector<MediaObservableParcel>& observables) override {
+        ALOGD("%s: %s, uid %d, pid %d, %s", __FUNCTION__,
+              ::aidl::android::media::toString(event).c_str(), uid, pid,
+              toString(observables[0]).c_str());
+
+        // Only report kIdle event for codec resources from other processes.
+        if (((uint64_t)event & (uint64_t)MediaObservableEvent::kIdle) != 0 && (pid != mPid)) {
+            for (auto& observable : observables) {
+                if (observable.type == MediaObservableType::kVideoSecureCodec ||
+                    observable.type == MediaObservableType::kVideoNonSecureCodec) {
+                    mOwner->onResourceAvailable();
+                    break;
+                }
+            }
+        }
+        return ::ndk::ScopedAStatus::ok();
+    }
+
+    TranscodingResourcePolicy* mOwner;
+    const pid_t mPid;
+};
+
+// static
+void TranscodingResourcePolicy::BinderDiedCallback(void* cookie) {
+    TranscodingResourcePolicy* owner = reinterpret_cast<TranscodingResourcePolicy*>(cookie);
+    if (owner != nullptr) {
+        owner->unregisterSelf();
+    }
+    // TODO(chz): retry to connecting to IResourceObserverService after failure.
+    // Also need to have back-up logic if IResourceObserverService is offline for
+    // Prolonged period of time. A possible alternative could be, during period where
+    // IResourceObserverService is not available, trigger onResourceAvailable() everytime
+    // when top uid changes (in hope that'll free up some codec instances that we could
+    // reclaim).
+}
+
+TranscodingResourcePolicy::TranscodingResourcePolicy()
+      : mRegistered(false), mDeathRecipient(AIBinder_DeathRecipient_new(BinderDiedCallback)) {
+    registerSelf();
+}
+
+TranscodingResourcePolicy::~TranscodingResourcePolicy() {
+    unregisterSelf();
+}
+
+void TranscodingResourcePolicy::registerSelf() {
+    ALOGI("TranscodingResourcePolicy: registerSelf");
+
+    ::ndk::SpAIBinder binder(AServiceManager_getService("media.resource_observer"));
+
+    std::scoped_lock lock{mRegisteredLock};
+
+    if (mRegistered) {
+        return;
+    }
+
+    // TODO(chz): retry to connecting to IResourceObserverService after failure.
+    mService = IResourceObserverService::fromBinder(binder);
+    if (mService == nullptr) {
+        ALOGE("Failed to get IResourceObserverService");
+        return;
+    }
+
+    // Only register filters for codec resource available.
+    mObserver = ::ndk::SharedRefBase::make<ResourceObserver>(this);
+    std::vector<MediaObservableFilter> filters = {
+            {MediaObservableType::kVideoSecureCodec, MediaObservableEvent::kIdle},
+            {MediaObservableType::kVideoNonSecureCodec, MediaObservableEvent::kIdle}};
+
+    Status status = mService->registerObserver(mObserver, filters);
+    if (!status.isOk()) {
+        ALOGE("failed to register: error %d", status.getServiceSpecificError());
+        mService = nullptr;
+        mObserver = nullptr;
+        return;
+    }
+
+    AIBinder_linkToDeath(binder.get(), mDeathRecipient.get(), reinterpret_cast<void*>(this));
+
+    ALOGD("@@@ registered observer");
+    mRegistered = true;
+}
+
+void TranscodingResourcePolicy::unregisterSelf() {
+    ALOGI("TranscodingResourcePolicy: unregisterSelf");
+
+    std::scoped_lock lock{mRegisteredLock};
+
+    if (!mRegistered) {
+        return;
+    }
+
+    ::ndk::SpAIBinder binder = mService->asBinder();
+    if (binder.get() != nullptr) {
+        Status status = mService->unregisterObserver(mObserver);
+        AIBinder_unlinkToDeath(binder.get(), mDeathRecipient.get(), reinterpret_cast<void*>(this));
+    }
+
+    mService = nullptr;
+    mObserver = nullptr;
+    mRegistered = false;
+}
+
+void TranscodingResourcePolicy::setCallback(
+        const std::shared_ptr<ResourcePolicyCallbackInterface>& cb) {
+    std::scoped_lock lock{mCallbackLock};
+    mResourcePolicyCallback = cb;
+}
+
+void TranscodingResourcePolicy::onResourceAvailable() {
+    std::shared_ptr<ResourcePolicyCallbackInterface> cb;
+    {
+        std::scoped_lock lock{mCallbackLock};
+        cb = mResourcePolicyCallback.lock();
+    }
+
+    if (cb != nullptr) {
+        cb->onResourceAvailable();
+    }
+}
+}  // namespace android
diff --git a/media/libmediatranscoding/include/media/ResourcePolicyInterface.h b/media/libmediatranscoding/include/media/ResourcePolicyInterface.h
new file mode 100644
index 0000000..8bd7d6b
--- /dev/null
+++ b/media/libmediatranscoding/include/media/ResourcePolicyInterface.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_MEDIA_RESOURCE_POLICY_INTERFACE_H
+#define ANDROID_MEDIA_RESOURCE_POLICY_INTERFACE_H
+#include <memory>
+namespace android {
+
+class ResourcePolicyCallbackInterface;
+
+// Interface for the JobScheduler to control the resource status updates.
+class ResourcePolicyInterface {
+public:
+    // Set the associated callback interface to send the events when resource
+    // status changes. (Set to nullptr will stop the updates.)
+    virtual void setCallback(const std::shared_ptr<ResourcePolicyCallbackInterface>& cb) = 0;
+
+protected:
+    virtual ~ResourcePolicyInterface() = default;
+};
+
+// Interface for notifying the JobScheduler of a change in resource status.
+class ResourcePolicyCallbackInterface {
+public:
+    // Called when codec resources become available. The scheduler may use this
+    // as a signal to attempt restart transcoding jobs that were previously
+    // paused due to temporary resource loss.
+    virtual void onResourceAvailable() = 0;
+
+protected:
+    virtual ~ResourcePolicyCallbackInterface() = default;
+};
+
+}  // namespace android
+#endif  // ANDROID_MEDIA_RESOURCE_POLICY_INTERFACE_H
diff --git a/media/libmediatranscoding/include/media/TranscoderWrapper.h b/media/libmediatranscoding/include/media/TranscoderWrapper.h
index a4c92c5..c956042 100644
--- a/media/libmediatranscoding/include/media/TranscoderWrapper.h
+++ b/media/libmediatranscoding/include/media/TranscoderWrapper.h
@@ -18,6 +18,7 @@
 #define ANDROID_TRANSCODER_WRAPPER_H
 
 #include <android-base/thread_annotations.h>
+#include <media/NdkMediaError.h>
 #include <media/TranscoderInterface.h>
 
 #include <list>
@@ -55,6 +56,7 @@
         ClientIdType clientId;
         JobIdType jobId;
         std::function<void()> runnable;
+        int32_t arg;
     };
     using JobKeyType = std::pair<ClientIdType, JobIdType>;
 
@@ -68,26 +70,27 @@
     ClientIdType mCurrentClientId;
     JobIdType mCurrentJobId;
 
-    static const char* toString(Event::Type type);
+    static std::string toString(const Event& event);
     void onFinish(ClientIdType clientId, JobIdType jobId);
-    void onError(ClientIdType clientId, JobIdType jobId, TranscodingErrorCode error);
+    void onError(ClientIdType clientId, JobIdType jobId, media_status_t status);
     void onProgress(ClientIdType clientId, JobIdType jobId, int32_t progress);
 
-    TranscodingErrorCode handleStart(ClientIdType clientId, JobIdType jobId,
-                                     const TranscodingRequestParcel& request,
-                                     const std::shared_ptr<ITranscodingClientCallback>& callback);
-    TranscodingErrorCode handlePause(ClientIdType clientId, JobIdType jobId);
-    TranscodingErrorCode handleResume(ClientIdType clientId, JobIdType jobId,
-                                      const TranscodingRequestParcel& request,
-                                      const std::shared_ptr<ITranscodingClientCallback>& callback);
-    TranscodingErrorCode setupTranscoder(
-            ClientIdType clientId, JobIdType jobId, const TranscodingRequestParcel& request,
-            const std::shared_ptr<ITranscodingClientCallback>& callback,
-            const std::shared_ptr<const Parcel>& pausedState = nullptr);
+    media_status_t handleStart(ClientIdType clientId, JobIdType jobId,
+                               const TranscodingRequestParcel& request,
+                               const std::shared_ptr<ITranscodingClientCallback>& callback);
+    media_status_t handlePause(ClientIdType clientId, JobIdType jobId);
+    media_status_t handleResume(ClientIdType clientId, JobIdType jobId,
+                                const TranscodingRequestParcel& request,
+                                const std::shared_ptr<ITranscodingClientCallback>& callback);
+    media_status_t setupTranscoder(ClientIdType clientId, JobIdType jobId,
+                                   const TranscodingRequestParcel& request,
+                                   const std::shared_ptr<ITranscodingClientCallback>& callback,
+                                   const std::shared_ptr<const Parcel>& pausedState = nullptr);
 
     void cleanup();
+    void reportError(ClientIdType clientId, JobIdType jobId, media_status_t err);
     void queueEvent(Event::Type type, ClientIdType clientId, JobIdType jobId,
-                    const std::function<void()> runnable);
+                    const std::function<void()> runnable, int32_t arg = 0);
     void threadLoop();
 };
 
diff --git a/media/libmediatranscoding/include/media/TranscodingJobScheduler.h b/media/libmediatranscoding/include/media/TranscodingJobScheduler.h
index 5ccadad..8f5e2aa 100644
--- a/media/libmediatranscoding/include/media/TranscodingJobScheduler.h
+++ b/media/libmediatranscoding/include/media/TranscodingJobScheduler.h
@@ -18,6 +18,7 @@
 #define ANDROID_MEDIA_TRANSCODING_JOB_SCHEDULER_H
 
 #include <aidl/android/media/TranscodingJobPriority.h>
+#include <media/ResourcePolicyInterface.h>
 #include <media/SchedulerClientInterface.h>
 #include <media/TranscoderInterface.h>
 #include <media/TranscodingRequest.h>
@@ -34,7 +35,8 @@
 
 class TranscodingJobScheduler : public UidPolicyCallbackInterface,
                                 public SchedulerClientInterface,
-                                public TranscoderCallbackInterface {
+                                public TranscoderCallbackInterface,
+                                public ResourcePolicyCallbackInterface {
 public:
     virtual ~TranscodingJobScheduler();
 
@@ -58,9 +60,12 @@
 
     // UidPolicyCallbackInterface
     void onTopUidsChanged(const std::unordered_set<uid_t>& uids) override;
-    void onResourceAvailable() override;
     // ~UidPolicyCallbackInterface
 
+    // ResourcePolicyCallbackInterface
+    void onResourceAvailable() override;
+    // ~ResourcePolicyCallbackInterface
+
 private:
     friend class MediaTranscodingService;
     friend class TranscodingJobSchedulerTest;
@@ -96,13 +101,15 @@
 
     std::shared_ptr<TranscoderInterface> mTranscoder;
     std::shared_ptr<UidPolicyInterface> mUidPolicy;
+    std::shared_ptr<ResourcePolicyInterface> mResourcePolicy;
 
     Job* mCurrentJob;
     bool mResourceLost;
 
     // Only allow MediaTranscodingService and unit tests to instantiate.
     TranscodingJobScheduler(const std::shared_ptr<TranscoderInterface>& transcoder,
-                            const std::shared_ptr<UidPolicyInterface>& uidPolicy);
+                            const std::shared_ptr<UidPolicyInterface>& uidPolicy,
+                            const std::shared_ptr<ResourcePolicyInterface>& resourcePolicy);
 
     Job* getTopJob_l();
     void updateCurrentJob_l();
diff --git a/media/libmediatranscoding/include/media/TranscodingResourcePolicy.h b/media/libmediatranscoding/include/media/TranscodingResourcePolicy.h
new file mode 100644
index 0000000..0836eda
--- /dev/null
+++ b/media/libmediatranscoding/include/media/TranscodingResourcePolicy.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_MEDIA_TRANSCODING_RESOURCE_POLICY_H
+#define ANDROID_MEDIA_TRANSCODING_RESOURCE_POLICY_H
+
+#include <android/binder_auto_utils.h>
+#include <media/ResourcePolicyInterface.h>
+#include <utils/Condition.h>
+
+#include <mutex>
+namespace aidl {
+namespace android {
+namespace media {
+class IResourceObserverService;
+}
+}  // namespace android
+}  // namespace aidl
+
+namespace android {
+
+using ::aidl::android::media::IResourceObserverService;
+
+class TranscodingResourcePolicy : public ResourcePolicyInterface {
+public:
+    explicit TranscodingResourcePolicy();
+    ~TranscodingResourcePolicy();
+
+    void setCallback(const std::shared_ptr<ResourcePolicyCallbackInterface>& cb) override;
+
+private:
+    struct ResourceObserver;
+    mutable std::mutex mRegisteredLock;
+    bool mRegistered GUARDED_BY(mRegisteredLock);
+    std::shared_ptr<IResourceObserverService> mService GUARDED_BY(mRegisteredLock);
+    std::shared_ptr<ResourceObserver> mObserver;
+
+    mutable std::mutex mCallbackLock;
+    std::weak_ptr<ResourcePolicyCallbackInterface> mResourcePolicyCallback
+            GUARDED_BY(mCallbackLock);
+
+    ::ndk::ScopedAIBinder_DeathRecipient mDeathRecipient;
+
+    static void BinderDiedCallback(void* cookie);
+
+    void registerSelf();
+    void unregisterSelf();
+    void onResourceAvailable();
+};  // class TranscodingUidPolicy
+
+}  // namespace android
+#endif  // ANDROID_MEDIA_TRANSCODING_RESOURCE_POLICY_H
diff --git a/media/libmediatranscoding/include/media/UidPolicyInterface.h b/media/libmediatranscoding/include/media/UidPolicyInterface.h
index dc28027..f88c1ed 100644
--- a/media/libmediatranscoding/include/media/UidPolicyInterface.h
+++ b/media/libmediatranscoding/include/media/UidPolicyInterface.h
@@ -41,19 +41,13 @@
     virtual ~UidPolicyInterface() = default;
 };
 
-// Interface for notifying the scheduler of a change in uid states or
-// transcoding resource availability.
+// Interface for notifying the scheduler of a change in uid states.
 class UidPolicyCallbackInterface {
 public:
     // Called when the set of uids that's top priority among the uids of interest
     // has changed. The receiver of this callback should adjust accordingly.
     virtual void onTopUidsChanged(const std::unordered_set<uid_t>& uids) = 0;
 
-    // Called when resources become available for transcoding use. The scheduler
-    // may use this as a signal to attempt restart transcoding activity that
-    // were previously paused due to temporary resource loss.
-    virtual void onResourceAvailable() = 0;
-
 protected:
     virtual ~UidPolicyCallbackInterface() = default;
 };
diff --git a/media/libmediatranscoding/tests/TranscodingJobScheduler_tests.cpp b/media/libmediatranscoding/tests/TranscodingJobScheduler_tests.cpp
index d21b595..9b9df87 100644
--- a/media/libmediatranscoding/tests/TranscodingJobScheduler_tests.cpp
+++ b/media/libmediatranscoding/tests/TranscodingJobScheduler_tests.cpp
@@ -213,7 +213,8 @@
         ALOGI("TranscodingJobSchedulerTest set up");
         mTranscoder.reset(new TestTranscoder());
         mUidPolicy.reset(new TestUidPolicy());
-        mScheduler.reset(new TranscodingJobScheduler(mTranscoder, mUidPolicy));
+        mScheduler.reset(
+                new TranscodingJobScheduler(mTranscoder, mUidPolicy, nullptr /*resourcePolicy*/));
         mUidPolicy->setCallback(mScheduler);
 
         // Set priority only, ignore other fields for now.