transcoding: add uid state based scheduling policy

bug: 145233472
bug: 154734285

test: unit tests

Change-Id: I9e8038252c2be834eb4e2fb2945396572d37b036
diff --git a/media/libmediatranscoding/tests/Android.bp b/media/libmediatranscoding/tests/Android.bp
index 1017d29..b54022a 100644
--- a/media/libmediatranscoding/tests/Android.bp
+++ b/media/libmediatranscoding/tests/Android.bp
@@ -37,6 +37,9 @@
     srcs: ["TranscodingClientManager_tests.cpp"],
 }
 
+//
+// TranscodingJobScheduler unit test
+//
 cc_test {
     name: "TranscodingJobScheduler_tests",
     defaults: ["libmediatranscoding_test_defaults"],
diff --git a/media/libmediatranscoding/tests/TranscodingJobScheduler_tests.cpp b/media/libmediatranscoding/tests/TranscodingJobScheduler_tests.cpp
index 6bc9e20..95edf1d 100644
--- a/media/libmediatranscoding/tests/TranscodingJobScheduler_tests.cpp
+++ b/media/libmediatranscoding/tests/TranscodingJobScheduler_tests.cpp
@@ -31,6 +31,8 @@
 #include <media/TranscodingJobScheduler.h>
 #include <utils/Log.h>
 
+#include <unordered_set>
+
 namespace android {
 
 using Status = ::ndk::ScopedAStatus;
@@ -47,10 +49,39 @@
 #define JOB(n) (kClientJobId + (n))
 #define UID(n) (kClientUid + (n))
 
-class TestCallback : public TranscoderInterface, public UidPolicyInterface {
+class TestUidPolicy : public UidPolicyInterface {
 public:
-    TestCallback() : mTopUid(kInvalidUid), mLastError(TranscodingErrorCode::kUnknown) {}
-    virtual ~TestCallback() {}
+    TestUidPolicy() = default;
+    virtual ~TestUidPolicy() = default;
+
+    // UidPolicyInterface
+    void registerMonitorUid(uid_t /*uid*/) override {}
+    void unregisterMonitorUid(uid_t /*uid*/) override {}
+    bool isUidOnTop(uid_t uid) override { return mTopUids.count(uid) > 0; }
+    std::unordered_set<uid_t> getTopUids() const override { return mTopUids; }
+    void setCallback(const std::shared_ptr<UidPolicyCallbackInterface>& cb) override {
+        mUidPolicyCallback = cb;
+    }
+    void setTop(uid_t uid) {
+        std::unordered_set<uid_t> uids = {uid};
+        setTop(uids);
+    }
+    void setTop(const std::unordered_set<uid_t>& uids) {
+        mTopUids = uids;
+        auto uidPolicyCb = mUidPolicyCallback.lock();
+        if (uidPolicyCb != nullptr) {
+            uidPolicyCb->onTopUidsChanged(mTopUids);
+        }
+    }
+
+    std::unordered_set<uid_t> mTopUids;
+    std::weak_ptr<UidPolicyCallbackInterface> mUidPolicyCallback;
+};
+
+class TestTranscoder : public TranscoderInterface {
+public:
+    TestTranscoder() : mLastError(TranscodingErrorCode::kUnknown) {}
+    virtual ~TestTranscoder() {}
 
     // TranscoderInterface
     void start(int64_t clientId, int32_t jobId) override {
@@ -63,9 +94,6 @@
         mEventQueue.push_back(Resume(clientId, jobId));
     }
 
-    // UidPolicyInterface
-    bool isUidOnTop(uid_t uid) override { return uid == mTopUid; }
-
     void onFinished(int64_t clientId, int32_t jobId) {
         mEventQueue.push_back(Finished(clientId, jobId));
     }
@@ -75,8 +103,6 @@
         mEventQueue.push_back(Failed(clientId, jobId));
     }
 
-    void setTop(uid_t uid) { mTopUid = uid; }
-
     TranscodingErrorCode getLastError() {
         TranscodingErrorCode result = mLastError;
         mLastError = TranscodingErrorCode::kUnknown;
@@ -115,16 +141,16 @@
 private:
     Event mPoppedEvent;
     std::list<Event> mEventQueue;
-    uid_t mTopUid;
     TranscodingErrorCode mLastError;
 };
 
-bool operator==(const TestCallback::Event& lhs, const TestCallback::Event& rhs) {
+bool operator==(const TestTranscoder::Event& lhs, const TestTranscoder::Event& rhs) {
     return lhs.type == rhs.type && lhs.clientId == rhs.clientId && lhs.jobId == rhs.jobId;
 }
 
 struct TestClientCallback : public BnTranscodingClientCallback {
-    TestClientCallback(TestCallback* owner, int64_t clientId) : mOwner(owner), mClientId(clientId) {
+    TestClientCallback(TestTranscoder* owner, int64_t clientId)
+          : mOwner(owner), mClientId(clientId) {
         ALOGD("TestClient Created");
     }
 
@@ -152,7 +178,7 @@
     virtual ~TestClientCallback() { ALOGI("TestClient destroyed"); };
 
 private:
-    TestCallback* mOwner;
+    TestTranscoder* mOwner;
     int64_t mClientId;
     TestClientCallback(const TestClientCallback&) = delete;
     TestClientCallback& operator=(const TestClientCallback&) = delete;
@@ -164,27 +190,30 @@
 
     void SetUp() override {
         ALOGI("TranscodingJobSchedulerTest set up");
-        mCallback.reset(new TestCallback());
-        mScheduler.reset(new TranscodingJobScheduler(mCallback, mCallback));
+        mTranscoder.reset(new TestTranscoder());
+        mUidPolicy.reset(new TestUidPolicy());
+        mScheduler.reset(new TranscodingJobScheduler(mTranscoder, mUidPolicy));
+        mUidPolicy->setCallback(mScheduler);
 
         // Set priority only, ignore other fields for now.
         mOfflineRequest.priority = TranscodingJobPriority::kUnspecified;
         mRealtimeRequest.priority = TranscodingJobPriority::kHigh;
         mClientCallback0 =
-                ::ndk::SharedRefBase::make<TestClientCallback>(mCallback.get(), CLIENT(0));
+                ::ndk::SharedRefBase::make<TestClientCallback>(mTranscoder.get(), CLIENT(0));
         mClientCallback1 =
-                ::ndk::SharedRefBase::make<TestClientCallback>(mCallback.get(), CLIENT(1));
+                ::ndk::SharedRefBase::make<TestClientCallback>(mTranscoder.get(), CLIENT(1));
         mClientCallback2 =
-                ::ndk::SharedRefBase::make<TestClientCallback>(mCallback.get(), CLIENT(2));
+                ::ndk::SharedRefBase::make<TestClientCallback>(mTranscoder.get(), CLIENT(2));
         mClientCallback3 =
-                ::ndk::SharedRefBase::make<TestClientCallback>(mCallback.get(), CLIENT(3));
+                ::ndk::SharedRefBase::make<TestClientCallback>(mTranscoder.get(), CLIENT(3));
     }
 
     void TearDown() override { ALOGI("TranscodingJobSchedulerTest tear down"); }
 
     ~TranscodingJobSchedulerTest() { ALOGD("TranscodingJobSchedulerTest destroyed"); }
 
-    std::shared_ptr<TestCallback> mCallback;
+    std::shared_ptr<TestTranscoder> mTranscoder;
+    std::shared_ptr<TestUidPolicy> mUidPolicy;
     std::shared_ptr<TranscodingJobScheduler> mScheduler;
     TranscodingRequestParcel mOfflineRequest;
     TranscodingRequestParcel mRealtimeRequest;
@@ -198,44 +227,42 @@
     ALOGD("TestSubmitJob");
 
     // Start with UID(1) on top.
-    mCallback->setTop(UID(1));
+    mUidPolicy->setTop(UID(1));
 
     // Submit offline job to CLIENT(0) in UID(0).
     // Should start immediately (because this is the only job).
     mScheduler->submit(CLIENT(0), JOB(0), UID(0), mOfflineRequest, mClientCallback0);
-    EXPECT_EQ(mCallback->popEvent(), TestCallback::Start(CLIENT(0), 0));
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), 0));
 
     // Submit real-time job to CLIENT(0).
     // Should pause offline job and start new job,  even if UID(0) is not on top.
     mScheduler->submit(CLIENT(0), JOB(1), UID(0), mRealtimeRequest, mClientCallback0);
-    EXPECT_EQ(mCallback->popEvent(), TestCallback::Pause(CLIENT(0), JOB(0)));
-    EXPECT_EQ(mCallback->popEvent(), TestCallback::Start(CLIENT(0), JOB(1)));
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Pause(CLIENT(0), JOB(0)));
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), JOB(1)));
 
     // Submit real-time job to CLIENT(0), should be queued after the previous job.
     mScheduler->submit(CLIENT(0), JOB(2), UID(0), mRealtimeRequest, mClientCallback0);
-    EXPECT_EQ(mCallback->popEvent(), TestCallback::NoEvent);
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
 
     // Submit real-time job to CLIENT(1) in same uid, should be queued after the previous job.
     mScheduler->submit(CLIENT(1), JOB(0), UID(0), mRealtimeRequest, mClientCallback1);
-    EXPECT_EQ(mCallback->popEvent(), TestCallback::NoEvent);
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
 
     // Submit real-time job to CLIENT(2) in UID(1).
-    // Should pause previous job and start new job, because UID(1) is top.
-    mCallback->setTop(UID(1));
+    // Should pause previous job and start new job, because UID(1) is (has been) top.
     mScheduler->submit(CLIENT(2), JOB(0), UID(1), mRealtimeRequest, mClientCallback2);
-    EXPECT_EQ(mCallback->popEvent(), TestCallback::Pause(CLIENT(0), JOB(1)));
-    EXPECT_EQ(mCallback->popEvent(), TestCallback::Start(CLIENT(2), JOB(0)));
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Pause(CLIENT(0), JOB(1)));
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(2), JOB(0)));
 
     // Submit offline job, shouldn't generate any event.
     mScheduler->submit(CLIENT(2), JOB(1), UID(1), mOfflineRequest, mClientCallback2);
-    EXPECT_EQ(mCallback->popEvent(), TestCallback::NoEvent);
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
 
-    mCallback->setTop(UID(0));
-    // Submit real-time job to CLIENT(1) in UID(0).
+    // Bring UID(0) to top.
+    mUidPolicy->setTop(UID(0));
     // Should pause current job, and resume last job in UID(0).
-    mScheduler->submit(CLIENT(1), JOB(1), UID(0), mRealtimeRequest, mClientCallback1);
-    EXPECT_EQ(mCallback->popEvent(), TestCallback::Pause(CLIENT(2), JOB(0)));
-    EXPECT_EQ(mCallback->popEvent(), TestCallback::Resume(CLIENT(0), JOB(1)));
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Pause(CLIENT(2), JOB(0)));
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Resume(CLIENT(0), JOB(1)));
 }
 
 TEST_F(TranscodingJobSchedulerTest, TestCancelJob) {
@@ -243,15 +270,15 @@
 
     // Submit real-time job JOB(0), should start immediately.
     mScheduler->submit(CLIENT(0), JOB(0), UID(0), mRealtimeRequest, mClientCallback0);
-    EXPECT_EQ(mCallback->popEvent(), TestCallback::Start(CLIENT(0), JOB(0)));
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), JOB(0)));
 
     // Submit real-time job JOB(1), should not start.
     mScheduler->submit(CLIENT(0), JOB(1), UID(0), mRealtimeRequest, mClientCallback0);
-    EXPECT_EQ(mCallback->popEvent(), TestCallback::NoEvent);
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
 
     // Submit offline job JOB(2), should not start.
     mScheduler->submit(CLIENT(0), JOB(2), UID(0), mOfflineRequest, mClientCallback0);
-    EXPECT_EQ(mCallback->popEvent(), TestCallback::NoEvent);
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
 
     // Cancel queued real-time job.
     // Cancel real-time job JOB(1), should be cancelled.
@@ -263,224 +290,283 @@
 
     // Submit offline job JOB(3), shouldn't cause any event.
     mScheduler->submit(CLIENT(0), JOB(3), UID(0), mOfflineRequest, mClientCallback0);
-    EXPECT_EQ(mCallback->popEvent(), TestCallback::NoEvent);
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
 
     // Cancel running real-time job JOB(0).
     // - Should be paused first then cancelled.
     // - Should also start offline job JOB(2) because real-time queue is empty.
     EXPECT_TRUE(mScheduler->cancel(CLIENT(0), JOB(0)));
-    EXPECT_EQ(mCallback->popEvent(), TestCallback::Pause(CLIENT(0), JOB(0)));
-    EXPECT_EQ(mCallback->popEvent(), TestCallback::Start(CLIENT(0), JOB(3)));
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Pause(CLIENT(0), JOB(0)));
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), JOB(3)));
 }
 
 TEST_F(TranscodingJobSchedulerTest, TestFinishJob) {
     ALOGD("TestFinishJob");
 
-    // Fail without any jobs submitted, should be ignored.
+    // Start with unspecified top UID.
+    // Finish without any jobs submitted, should be ignored.
     mScheduler->onFinish(CLIENT(0), JOB(0));
-    EXPECT_EQ(mCallback->popEvent(), TestCallback::NoEvent);
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
 
     // Submit offline job JOB(0), should start immediately.
     mScheduler->submit(CLIENT(0), JOB(0), UID(0), mOfflineRequest, mClientCallback0);
-    EXPECT_EQ(mCallback->popEvent(), TestCallback::Start(CLIENT(0), JOB(0)));
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), JOB(0)));
 
     // Submit real-time job JOB(1), should pause offline job and start immediately.
     mScheduler->submit(CLIENT(0), JOB(1), UID(0), mRealtimeRequest, mClientCallback0);
-    EXPECT_EQ(mCallback->popEvent(), TestCallback::Pause(CLIENT(0), JOB(0)));
-    EXPECT_EQ(mCallback->popEvent(), TestCallback::Start(CLIENT(0), JOB(1)));
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Pause(CLIENT(0), JOB(0)));
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), JOB(1)));
 
     // Submit real-time job JOB(2), should not start.
     mScheduler->submit(CLIENT(0), JOB(2), UID(0), mRealtimeRequest, mClientCallback0);
-    EXPECT_EQ(mCallback->popEvent(), TestCallback::NoEvent);
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
 
-    // Fail when the job never started, should be ignored.
+    // Finish when the job never started, should be ignored.
     mScheduler->onFinish(CLIENT(0), JOB(2));
-    EXPECT_EQ(mCallback->popEvent(), TestCallback::NoEvent);
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
 
     // UID(1) moves to top.
-    mCallback->setTop(UID(1));
+    mUidPolicy->setTop(UID(1));
     // Submit real-time job to CLIENT(1) in UID(1), should pause previous job and start new job.
     mScheduler->submit(CLIENT(1), JOB(0), UID(1), mRealtimeRequest, mClientCallback1);
-    EXPECT_EQ(mCallback->popEvent(), TestCallback::Pause(CLIENT(0), JOB(1)));
-    EXPECT_EQ(mCallback->popEvent(), TestCallback::Start(CLIENT(1), JOB(0)));
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Pause(CLIENT(0), JOB(1)));
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(1), JOB(0)));
 
-    // Simulate Fail that arrived late, after pause issued by scheduler.
+    // Simulate Finish that arrived late, after pause issued by scheduler.
     // Should still be propagated to client, but shouldn't trigger any new start.
     mScheduler->onFinish(CLIENT(0), JOB(1));
-    EXPECT_EQ(mCallback->popEvent(), TestCallback::Finished(CLIENT(0), JOB(1)));
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Finished(CLIENT(0), JOB(1)));
 
-    // Fail running real-time job, should start next real-time job in queue.
+    // Finish running real-time job, should start next real-time job in queue.
     mScheduler->onFinish(CLIENT(1), JOB(0));
-    EXPECT_EQ(mCallback->popEvent(), TestCallback::Finished(CLIENT(1), JOB(0)));
-    EXPECT_EQ(mCallback->popEvent(), TestCallback::Start(CLIENT(0), JOB(2)));
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Finished(CLIENT(1), JOB(0)));
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), JOB(2)));
 
-    // Fail running real-time job, should resume next job (offline job) in queue.
+    // Finish running real-time job, should resume next job (offline job) in queue.
     mScheduler->onFinish(CLIENT(0), JOB(2));
-    EXPECT_EQ(mCallback->popEvent(), TestCallback::Finished(CLIENT(0), JOB(2)));
-    EXPECT_EQ(mCallback->popEvent(), TestCallback::Resume(CLIENT(0), JOB(0)));
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Finished(CLIENT(0), JOB(2)));
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Resume(CLIENT(0), JOB(0)));
 
-    // Fail running offline job.
+    // Finish running offline job.
     mScheduler->onFinish(CLIENT(0), JOB(0));
-    EXPECT_EQ(mCallback->popEvent(), TestCallback::Finished(CLIENT(0), JOB(0)));
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Finished(CLIENT(0), JOB(0)));
 
-    // Duplicate fail for last job, should be ignored.
+    // Duplicate finish for last job, should be ignored.
     mScheduler->onFinish(CLIENT(0), JOB(0));
-    EXPECT_EQ(mCallback->popEvent(), TestCallback::NoEvent);
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
 }
 
 TEST_F(TranscodingJobSchedulerTest, TestFailJob) {
     ALOGD("TestFailJob");
 
+    // Start with unspecified top UID.
     // Fail without any jobs submitted, should be ignored.
     mScheduler->onError(CLIENT(0), JOB(0), TranscodingErrorCode::kUnknown);
-    EXPECT_EQ(mCallback->popEvent(), TestCallback::NoEvent);
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
 
     // Submit offline job JOB(0), should start immediately.
     mScheduler->submit(CLIENT(0), JOB(0), UID(0), mOfflineRequest, mClientCallback0);
-    EXPECT_EQ(mCallback->popEvent(), TestCallback::Start(CLIENT(0), JOB(0)));
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), JOB(0)));
 
     // Submit real-time job JOB(1), should pause offline job and start immediately.
     mScheduler->submit(CLIENT(0), JOB(1), UID(0), mRealtimeRequest, mClientCallback0);
-    EXPECT_EQ(mCallback->popEvent(), TestCallback::Pause(CLIENT(0), JOB(0)));
-    EXPECT_EQ(mCallback->popEvent(), TestCallback::Start(CLIENT(0), JOB(1)));
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Pause(CLIENT(0), JOB(0)));
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), JOB(1)));
 
     // Submit real-time job JOB(2), should not start.
     mScheduler->submit(CLIENT(0), JOB(2), UID(0), mRealtimeRequest, mClientCallback0);
-    EXPECT_EQ(mCallback->popEvent(), TestCallback::NoEvent);
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
 
     // Fail when the job never started, should be ignored.
     mScheduler->onError(CLIENT(0), JOB(2), TranscodingErrorCode::kUnknown);
-    EXPECT_EQ(mCallback->popEvent(), TestCallback::NoEvent);
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
 
     // UID(1) moves to top.
-    mCallback->setTop(UID(1));
+    mUidPolicy->setTop(UID(1));
     // Submit real-time job to CLIENT(1) in UID(1), should pause previous job and start new job.
     mScheduler->submit(CLIENT(1), JOB(0), UID(1), mRealtimeRequest, mClientCallback1);
-    EXPECT_EQ(mCallback->popEvent(), TestCallback::Pause(CLIENT(0), JOB(1)));
-    EXPECT_EQ(mCallback->popEvent(), TestCallback::Start(CLIENT(1), JOB(0)));
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Pause(CLIENT(0), JOB(1)));
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(1), JOB(0)));
 
     // Simulate Fail that arrived late, after pause issued by scheduler.
     // Should still be propagated to client, but shouldn't trigger any new start.
     mScheduler->onError(CLIENT(0), JOB(1), TranscodingErrorCode::kUnknown);
-    EXPECT_EQ(mCallback->popEvent(), TestCallback::Failed(CLIENT(0), JOB(1)));
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Failed(CLIENT(0), JOB(1)));
 
     // Fail running real-time job, should start next real-time job in queue.
     mScheduler->onError(CLIENT(1), JOB(0), TranscodingErrorCode::kUnknown);
-    EXPECT_EQ(mCallback->popEvent(), TestCallback::Failed(CLIENT(1), JOB(0)));
-    EXPECT_EQ(mCallback->popEvent(), TestCallback::Start(CLIENT(0), JOB(2)));
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Failed(CLIENT(1), JOB(0)));
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), JOB(2)));
 
     // Fail running real-time job, should resume next job (offline job) in queue.
     mScheduler->onError(CLIENT(0), JOB(2), TranscodingErrorCode::kUnknown);
-    EXPECT_EQ(mCallback->popEvent(), TestCallback::Failed(CLIENT(0), JOB(2)));
-    EXPECT_EQ(mCallback->popEvent(), TestCallback::Resume(CLIENT(0), JOB(0)));
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Failed(CLIENT(0), JOB(2)));
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Resume(CLIENT(0), JOB(0)));
 
     // Fail running offline job, and test error code propagation.
     mScheduler->onError(CLIENT(0), JOB(0), TranscodingErrorCode::kInvalidBitstream);
-    EXPECT_EQ(mCallback->popEvent(), TestCallback::Failed(CLIENT(0), JOB(0)));
-    EXPECT_EQ(mCallback->getLastError(), TranscodingErrorCode::kInvalidBitstream);
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Failed(CLIENT(0), JOB(0)));
+    EXPECT_EQ(mTranscoder->getLastError(), TranscodingErrorCode::kInvalidBitstream);
 
     // Duplicate fail for last job, should be ignored.
     mScheduler->onError(CLIENT(0), JOB(0), TranscodingErrorCode::kUnknown);
-    EXPECT_EQ(mCallback->popEvent(), TestCallback::NoEvent);
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
 }
 
 TEST_F(TranscodingJobSchedulerTest, TestTopUidChanged) {
     ALOGD("TestTopUidChanged");
 
+    // Start with unspecified top UID.
     // Submit real-time job to CLIENT(0), job should start immediately.
     mScheduler->submit(CLIENT(0), JOB(0), UID(0), mRealtimeRequest, mClientCallback0);
-    EXPECT_EQ(mCallback->popEvent(), TestCallback::Start(CLIENT(0), JOB(0)));
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), JOB(0)));
 
     // Submit offline job to CLIENT(0), should not start.
     mScheduler->submit(CLIENT(1), JOB(0), UID(0), mOfflineRequest, mClientCallback1);
-    EXPECT_EQ(mCallback->popEvent(), TestCallback::NoEvent);
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
 
     // Move UID(1) to top.
-    mCallback->setTop(UID(1));
+    mUidPolicy->setTop(UID(1));
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
+
     // Submit real-time job to CLIENT(2) in different uid UID(1).
     // Should pause previous job and start new job.
     mScheduler->submit(CLIENT(2), JOB(0), UID(1), mRealtimeRequest, mClientCallback2);
-    EXPECT_EQ(mCallback->popEvent(), TestCallback::Pause(CLIENT(0), JOB(0)));
-    EXPECT_EQ(mCallback->popEvent(), TestCallback::Start(CLIENT(2), JOB(0)));
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Pause(CLIENT(0), JOB(0)));
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(2), JOB(0)));
 
     // Bring UID(0) back to top.
-    mCallback->setTop(UID(0));
-    mScheduler->onTopUidChanged(UID(0));
-    EXPECT_EQ(mCallback->popEvent(), TestCallback::Pause(CLIENT(2), JOB(0)));
-    EXPECT_EQ(mCallback->popEvent(), TestCallback::Resume(CLIENT(0), JOB(0)));
+    mUidPolicy->setTop(UID(0));
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Pause(CLIENT(2), JOB(0)));
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Resume(CLIENT(0), JOB(0)));
 
     // Bring invalid uid to top.
-    mScheduler->onTopUidChanged(kInvalidUid);
-    EXPECT_EQ(mCallback->popEvent(), TestCallback::NoEvent);
+    mUidPolicy->setTop(kInvalidUid);
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
 
     // Finish job, next real-time job should resume.
     mScheduler->onFinish(CLIENT(0), JOB(0));
-    EXPECT_EQ(mCallback->popEvent(), TestCallback::Finished(CLIENT(0), JOB(0)));
-    EXPECT_EQ(mCallback->popEvent(), TestCallback::Resume(CLIENT(2), JOB(0)));
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Finished(CLIENT(0), JOB(0)));
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Resume(CLIENT(2), JOB(0)));
 
     // Finish job, offline job should start.
     mScheduler->onFinish(CLIENT(2), JOB(0));
-    EXPECT_EQ(mCallback->popEvent(), TestCallback::Finished(CLIENT(2), JOB(0)));
-    EXPECT_EQ(mCallback->popEvent(), TestCallback::Start(CLIENT(1), JOB(0)));
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Finished(CLIENT(2), JOB(0)));
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(1), JOB(0)));
+}
+
+TEST_F(TranscodingJobSchedulerTest, TestTopUidSetChanged) {
+    ALOGD("TestTopUidChanged_MultipleUids");
+
+    // Start with unspecified top UID.
+    // Submit real-time job to CLIENT(0), job should start immediately.
+    mScheduler->submit(CLIENT(0), JOB(0), UID(0), mRealtimeRequest, mClientCallback0);
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), JOB(0)));
+
+    // Submit offline job to CLIENT(0), should not start.
+    mScheduler->submit(CLIENT(1), JOB(0), UID(0), mOfflineRequest, mClientCallback1);
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
+
+    // Set UID(0), UID(1) to top set.
+    // UID(0) should continue to run.
+    mUidPolicy->setTop({UID(0), UID(1)});
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
+
+    // Submit real-time job to CLIENT(2) in different uid UID(1).
+    // UID(0) should pause and UID(1) should start.
+    mScheduler->submit(CLIENT(2), JOB(0), UID(1), mRealtimeRequest, mClientCallback2);
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Pause(CLIENT(0), JOB(0)));
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(2), JOB(0)));
+
+    // Remove UID(0) from top set, and only leave UID(1) in the set.
+    // UID(1) should continue to run.
+    mUidPolicy->setTop(UID(1));
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
+
+    // Set UID(0), UID(2) to top set.
+    // UID(1) should continue to run.
+    mUidPolicy->setTop({UID(1), UID(2)});
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
+
+    // Bring UID(0) back to top.
+    mUidPolicy->setTop(UID(0));
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Pause(CLIENT(2), JOB(0)));
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Resume(CLIENT(0), JOB(0)));
+
+    // Bring invalid uid to top.
+    mUidPolicy->setTop(kInvalidUid);
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
+
+    // Finish job, next real-time job from UID(1) should resume, even if UID(1) no longer top.
+    mScheduler->onFinish(CLIENT(0), JOB(0));
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Finished(CLIENT(0), JOB(0)));
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Resume(CLIENT(2), JOB(0)));
+
+    // Finish job, offline job should start.
+    mScheduler->onFinish(CLIENT(2), JOB(0));
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Finished(CLIENT(2), JOB(0)));
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(1), JOB(0)));
 }
 
 TEST_F(TranscodingJobSchedulerTest, TestResourceLost) {
     ALOGD("TestResourceLost");
 
+    // Start with unspecified top UID.
     // Submit real-time job to CLIENT(0), job should start immediately.
     mScheduler->submit(CLIENT(0), JOB(0), UID(0), mRealtimeRequest, mClientCallback0);
-    EXPECT_EQ(mCallback->popEvent(), TestCallback::Start(CLIENT(0), JOB(0)));
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), JOB(0)));
 
     // Submit offline job to CLIENT(0), should not start.
     mScheduler->submit(CLIENT(1), JOB(0), UID(0), mOfflineRequest, mClientCallback1);
-    EXPECT_EQ(mCallback->popEvent(), TestCallback::NoEvent);
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
 
     // Move UID(1) to top.
-    mCallback->setTop(UID(1));
+    mUidPolicy->setTop(UID(1));
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
 
     // Submit real-time job to CLIENT(2) in different uid UID(1).
     // Should pause previous job and start new job.
     mScheduler->submit(CLIENT(2), JOB(0), UID(1), mRealtimeRequest, mClientCallback2);
-    EXPECT_EQ(mCallback->popEvent(), TestCallback::Pause(CLIENT(0), JOB(0)));
-    EXPECT_EQ(mCallback->popEvent(), TestCallback::Start(CLIENT(2), JOB(0)));
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Pause(CLIENT(0), JOB(0)));
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(2), JOB(0)));
 
     // Test 1: No queue change during resource loss.
     // Signal resource lost.
     mScheduler->onResourceLost();
-    EXPECT_EQ(mCallback->popEvent(), TestCallback::NoEvent);
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
 
     // Signal resource available, CLIENT(2) should resume.
     mScheduler->onResourceAvailable();
-    EXPECT_EQ(mCallback->popEvent(), TestCallback::Resume(CLIENT(2), JOB(0)));
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Resume(CLIENT(2), JOB(0)));
 
     // Test 2: Change of queue order during resource loss.
     // Signal resource lost.
     mScheduler->onResourceLost();
-    EXPECT_EQ(mCallback->popEvent(), TestCallback::NoEvent);
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
 
     // Move UID(0) back to top, should have no resume due to no resource.
-    mScheduler->onTopUidChanged(UID(0));
-    EXPECT_EQ(mCallback->popEvent(), TestCallback::NoEvent);
+    mUidPolicy->setTop(UID(0));
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
 
     // Signal resource available, CLIENT(0) should resume.
     mScheduler->onResourceAvailable();
-    EXPECT_EQ(mCallback->popEvent(), TestCallback::Resume(CLIENT(0), JOB(0)));
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Resume(CLIENT(0), JOB(0)));
 
     // Test 3: Adding new queue during resource loss.
     // Signal resource lost.
     mScheduler->onResourceLost();
-    EXPECT_EQ(mCallback->popEvent(), TestCallback::NoEvent);
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
 
     // Move UID(2) to top.
-    mCallback->setTop(UID(2));
+    mUidPolicy->setTop(UID(2));
 
     // Submit real-time job to CLIENT(3) in UID(2), job shouldn't start due to no resource.
     mScheduler->submit(CLIENT(3), JOB(0), UID(2), mRealtimeRequest, mClientCallback3);
-    EXPECT_EQ(mCallback->popEvent(), TestCallback::NoEvent);
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
 
     // Signal resource available, CLIENT(3)'s job should start.
     mScheduler->onResourceAvailable();
-    EXPECT_EQ(mCallback->popEvent(), TestCallback::Start(CLIENT(3), JOB(0)));
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(3), JOB(0)));
 }
 
 }  // namespace android