transcoding: add SimulatedTranscoder to test service

- Add SimulatedTranscoder (which is an upgraded version of
  DummyTranscoder) to allow more testing of the service.

- Add unit test that launches dummy test apps from shell
  to simulate uid policy change.

bug: 154734285
bug: 145233472
test: unit tests

Change-Id: Ic169757d64ad8da7eebd0e1febdcbfb467fe81f4
diff --git a/services/mediatranscoding/tests/Android.bp b/services/mediatranscoding/tests/Android.bp
index e0e040c..f37b39e 100644
--- a/services/mediatranscoding/tests/Android.bp
+++ b/services/mediatranscoding/tests/Android.bp
@@ -19,6 +19,7 @@
         "liblog",
         "libutils",
         "libmediatranscodingservice",
+        "libcutils",
     ],
 
     static_libs: [
@@ -32,4 +33,10 @@
     defaults: ["mediatranscodingservice_test_defaults"],
 
     srcs: ["mediatranscodingservice_tests.cpp"],
+
+    required: [
+        ":TranscodingUidPolicy_TestAppA",
+        ":TranscodingUidPolicy_TestAppB",
+        ":TranscodingUidPolicy_TestAppC",
+    ],
 }
\ No newline at end of file
diff --git a/services/mediatranscoding/tests/TranscodingUidPolicyTestApp/Android.bp b/services/mediatranscoding/tests/TranscodingUidPolicyTestApp/Android.bp
new file mode 100644
index 0000000..95a94fc
--- /dev/null
+++ b/services/mediatranscoding/tests/TranscodingUidPolicyTestApp/Android.bp
@@ -0,0 +1,23 @@
+android_test_helper_app {
+    name: "TranscodingUidPolicy_TestAppA",
+    manifest: "TestAppA.xml",
+    static_libs: ["androidx.test.rules"],
+    sdk_version: "test_current",
+    srcs: ["src/**/*.java"],
+}
+
+android_test_helper_app {
+    name: "TranscodingUidPolicy_TestAppB",
+    manifest: "TestAppB.xml",
+    static_libs: ["androidx.test.rules"],
+    sdk_version: "test_current",
+    srcs: ["src/**/*.java"],
+}
+
+android_test_helper_app {
+    name: "TranscodingUidPolicy_TestAppC",
+    manifest: "TestAppC.xml",
+    static_libs: ["androidx.test.rules"],
+    sdk_version: "test_current",
+    srcs: ["src/**/*.java"],
+}
\ No newline at end of file
diff --git a/services/mediatranscoding/tests/TranscodingUidPolicyTestApp/TestAppA.xml b/services/mediatranscoding/tests/TranscodingUidPolicyTestApp/TestAppA.xml
new file mode 100644
index 0000000..56e34f5
--- /dev/null
+++ b/services/mediatranscoding/tests/TranscodingUidPolicyTestApp/TestAppA.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.tests.transcoding.testapp.A"
+    android:versionCode="1"
+    android:versionName="1.0" >
+
+    <application android:label="TestAppA">
+        <activity android:name="com.android.tests.transcoding.MainActivity">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.DEFAULT"/>
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+</manifest>
+
diff --git a/services/mediatranscoding/tests/TranscodingUidPolicyTestApp/TestAppB.xml b/services/mediatranscoding/tests/TranscodingUidPolicyTestApp/TestAppB.xml
new file mode 100644
index 0000000..e1e7857
--- /dev/null
+++ b/services/mediatranscoding/tests/TranscodingUidPolicyTestApp/TestAppB.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.tests.transcoding.testapp.B"
+    android:versionCode="1"
+    android:versionName="1.0" >
+
+    <application android:label="TestAppB">
+        <activity android:name="com.android.tests.transcoding.MainActivity">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.DEFAULT"/>
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+</manifest>
+
diff --git a/services/mediatranscoding/tests/TranscodingUidPolicyTestApp/TestAppC.xml b/services/mediatranscoding/tests/TranscodingUidPolicyTestApp/TestAppC.xml
new file mode 100644
index 0000000..55693a4
--- /dev/null
+++ b/services/mediatranscoding/tests/TranscodingUidPolicyTestApp/TestAppC.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.tests.transcoding.testapp.C"
+    android:versionCode="1"
+    android:versionName="1.0" >
+
+    <application android:label="TestAppC">
+        <activity android:name="com.android.tests.transcoding.MainActivity">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.DEFAULT"/>
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+</manifest>
diff --git a/services/mediatranscoding/tests/TranscodingUidPolicyTestApp/src/com/android/tests/transcoding/MainActivity.java b/services/mediatranscoding/tests/TranscodingUidPolicyTestApp/src/com/android/tests/transcoding/MainActivity.java
new file mode 100644
index 0000000..7295073
--- /dev/null
+++ b/services/mediatranscoding/tests/TranscodingUidPolicyTestApp/src/com/android/tests/transcoding/MainActivity.java
@@ -0,0 +1,108 @@
+/*
+ * 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.
+ */
+package com.android.tests.transcoding;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.util.Log;
+
+/**
+ * This is an empty activity for testing the UID policy of media transcoding service.
+ */
+public class MainActivity extends Activity {
+    private static final String TAG = "MainActivity";
+
+    // Called at the start of the full lifetime.
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        // Initialize Activity and inflate the UI.
+    }
+
+    // Called after onCreate has finished, use to restore UI state
+    @Override
+    public void onRestoreInstanceState(Bundle savedInstanceState) {
+        super.onRestoreInstanceState(savedInstanceState);
+        // Restore UI state from the savedInstanceState.
+        // This bundle has also been passed to onCreate.
+        // Will only be called if the Activity has been
+        // killed by the system since it was last visible.
+    }
+
+    // Called before subsequent visible lifetimes
+    // for an activity process.
+    @Override
+    public void onRestart(){
+        super.onRestart();
+        // Load changes knowing that the Activity has already
+        // been visible within this process.
+    }
+
+    // Called at the start of the visible lifetime.
+    @Override
+    public void onStart(){
+        super.onStart();
+        // Apply any required UI change now that the Activity is visible.
+    }
+
+    // Called at the start of the active lifetime.
+    @Override
+    public void onResume(){
+        super.onResume();
+        // Resume any paused UI updates, threads, or processes required
+        // by the Activity but suspended when it was inactive.
+    }
+
+    // Called to save UI state changes at the
+    // end of the active lifecycle.
+    @Override
+    public void onSaveInstanceState(Bundle savedInstanceState) {
+        // Save UI state changes to the savedInstanceState.
+        // This bundle will be passed to onCreate and
+        // onRestoreInstanceState if the process is
+        // killed and restarted by the run time.
+        super.onSaveInstanceState(savedInstanceState);
+    }
+
+    // Called at the end of the active lifetime.
+    @Override
+    public void onPause(){
+        // Suspend UI updates, threads, or CPU intensive processes
+        // that don't need to be updated when the Activity isn't
+        // the active foreground Activity.
+        super.onPause();
+    }
+
+    // Called at the end of the visible lifetime.
+    @Override
+    public void onStop(){
+        // Suspend remaining UI updates, threads, or processing
+        // that aren't required when the Activity isn't visible.
+        // Persist all edits or state changes
+        // as after this call the process is likely to be killed.
+        super.onStop();
+    }
+
+    // Sometimes called at the end of the full lifetime.
+    @Override
+    public void onDestroy(){
+        // Clean up any resources including ending threads,
+        // closing database connections etc.
+        super.onDestroy();
+    }
+
+}
diff --git a/services/mediatranscoding/tests/build_and_run_all_unit_tests.sh b/services/mediatranscoding/tests/build_and_run_all_unit_tests.sh
index ce017d7..bdc0394 100644
--- a/services/mediatranscoding/tests/build_and_run_all_unit_tests.sh
+++ b/services/mediatranscoding/tests/build_and_run_all_unit_tests.sh
@@ -13,12 +13,15 @@
 
 mm
 
-echo "waiting for device"
+echo "[==========] installing test apps"
+adb root
+adb install -t -r -g -d $ANDROID_TARGET_OUT_TESTCASES/TranscodingUidPolicy_TestAppA/arm64/TranscodingUidPolicy_TestAppA.apk
+adb install -t -r -g -d $ANDROID_TARGET_OUT_TESTCASES/TranscodingUidPolicy_TestAppB/arm64/TranscodingUidPolicy_TestAppB.apk
+adb install -t -r -g -d $ANDROID_TARGET_OUT_TESTCASES/TranscodingUidPolicy_TestAppC/arm64/TranscodingUidPolicy_TestAppC.apk
 
-adb root && adb wait-for-device remount && adb sync
+echo "[==========] waiting for device and sync"
+adb wait-for-device remount && adb sync
+adb shell kill -9 `pid media.transcoding`
 
-echo "========================================"
-
-echo "testing mediatranscodingservice"
 #adb shell /data/nativetest64/mediatranscodingservice_tests/mediatranscodingservice_tests
 adb shell /data/nativetest/mediatranscodingservice_tests/mediatranscodingservice_tests
diff --git a/services/mediatranscoding/tests/mediatranscodingservice_tests.cpp b/services/mediatranscoding/tests/mediatranscodingservice_tests.cpp
index 351f830..b20ce09 100644
--- a/services/mediatranscoding/tests/mediatranscodingservice_tests.cpp
+++ b/services/mediatranscoding/tests/mediatranscodingservice_tests.cpp
@@ -24,18 +24,21 @@
 #include <aidl/android/media/ITranscodingClient.h>
 #include <aidl/android/media/ITranscodingClientCallback.h>
 #include <aidl/android/media/TranscodingJobParcel.h>
+#include <aidl/android/media/TranscodingJobPriority.h>
 #include <aidl/android/media/TranscodingRequestParcel.h>
 #include <android-base/logging.h>
-#include <android-base/unique_fd.h>
-#include <android/binder_ibinder_jni.h>
 #include <android/binder_manager.h>
 #include <android/binder_process.h>
-#include <cutils/ashmem.h>
+#include <binder/PermissionController.h>
+#include <cutils/multiuser.h>
 #include <gtest/gtest.h>
-#include <stdlib.h>
-#include <sys/mman.h>
 #include <utils/Log.h>
 
+#include <iostream>
+#include <list>
+
+#include "SimulatedTranscoder.h"
+
 namespace android {
 
 namespace media {
@@ -46,6 +49,7 @@
 using aidl::android::media::ITranscodingClient;
 using aidl::android::media::ITranscodingClientCallback;
 using aidl::android::media::TranscodingJobParcel;
+using aidl::android::media::TranscodingJobPriority;
 using aidl::android::media::TranscodingRequestParcel;
 
 // Note that -1 is valid and means using calling pid/uid for the service. But only privilege caller could
@@ -56,23 +60,168 @@
 
 constexpr int32_t kClientUseCallingPid = IMediaTranscodingService::USE_CALLING_PID;
 constexpr int32_t kClientUseCallingUid = IMediaTranscodingService::USE_CALLING_UID;
+
+constexpr uid_t kClientUid = 5000;
+#define UID(n) (kClientUid + (n))
+
+constexpr int32_t kClientId = 0;
+#define CLIENT(n) (kClientId + (n))
+
+constexpr int64_t kPaddingUs = 200000;
+constexpr int64_t kJobWithPaddingUs = SimulatedTranscoder::kJobDurationUs + kPaddingUs;
+
 constexpr const char* kClientName = "TestClient";
 constexpr const char* kClientOpPackageName = "TestClientPackage";
+constexpr const char* kClientPackageA = "com.android.tests.transcoding.testapp.A";
+constexpr const char* kClientPackageB = "com.android.tests.transcoding.testapp.B";
+constexpr const char* kClientPackageC = "com.android.tests.transcoding.testapp.C";
+constexpr const char* kTestActivityName = "/com.android.tests.transcoding.MainActivity";
 
-struct TestClient : public BnTranscodingClientCallback {
-    TestClient() { ALOGD("TestClient Created"); }
+static status_t getUidForPackage(String16 packageName, userid_t userId, /*inout*/ uid_t& uid) {
+    PermissionController pc;
+    uid = pc.getPackageUid(packageName, 0);
+    if (uid <= 0) {
+        ALOGE("Unknown package: '%s'", String8(packageName).string());
+        return BAD_VALUE;
+    }
 
-    virtual ~TestClient() { ALOGI("TestClient destroyed"); }
+    if (userId < 0) {
+        ALOGE("Invalid user: %d", userId);
+        return BAD_VALUE;
+    }
+
+    uid = multiuser_get_uid(userId, uid);
+    return NO_ERROR;
+}
+
+struct ShellHelper {
+    static bool RunCmd(const std::string& cmdStr) {
+        int ret = system(cmdStr.c_str());
+        if (ret != 0) {
+            ALOGE("Failed to run cmd: %s, exitcode %d", cmdStr.c_str(), ret);
+            return false;
+        }
+        return true;
+    }
+
+    static bool Start(const char* packageName, const char* activityName) {
+        return RunCmd("am start -W " + std::string(packageName) + std::string(activityName) +
+                      " &> /dev/null");
+    }
+
+    static bool Stop(const char* packageName) {
+        return RunCmd("am force-stop " + std::string(packageName));
+    }
+};
+
+struct EventTracker {
+    struct Event {
+        enum { NoEvent, Start, Pause, Resume, Finished, Failed } type;
+        int64_t clientId;
+        int32_t jobId;
+    };
+
+#define DECLARE_EVENT(action)                              \
+    static Event action(int32_t clientId, int32_t jobId) { \
+        return {Event::action, clientId, jobId};           \
+    }
+
+    DECLARE_EVENT(Start);
+    DECLARE_EVENT(Pause);
+    DECLARE_EVENT(Resume);
+    DECLARE_EVENT(Finished);
+    DECLARE_EVENT(Failed);
+
+    static constexpr Event NoEvent = {Event::NoEvent, 0, 0};
+
+    static std::string toString(const Event& event) {
+        std::string eventStr;
+        switch (event.type) {
+        case Event::Start:
+            eventStr = "Start";
+            break;
+        case Event::Pause:
+            eventStr = "Pause";
+            break;
+        case Event::Resume:
+            eventStr = "Resume";
+            break;
+        case Event::Finished:
+            eventStr = "Finished";
+            break;
+        case Event::Failed:
+            eventStr = "Failed";
+            break;
+        default:
+            return "NoEvent";
+        }
+        return "job {" + std::to_string(event.clientId) + ", " + std::to_string(event.jobId) +
+               "}: " + eventStr;
+    }
+
+    // Pop 1 event from front, wait for up to timeoutUs if empty.
+    const Event& pop(int64_t timeoutUs = 0) {
+        std::unique_lock lock(mLock);
+
+        if (mEventQueue.empty() && timeoutUs > 0) {
+            mCondition.wait_for(lock, std::chrono::microseconds(timeoutUs));
+        }
+
+        if (mEventQueue.empty()) {
+            mPoppedEvent = NoEvent;
+        } else {
+            mPoppedEvent = *mEventQueue.begin();
+            mEventQueue.pop_front();
+        }
+
+        return mPoppedEvent;
+    }
+
+    // Push 1 event to back.
+    void append(const Event& event) {
+        ALOGD("%s", toString(event).c_str());
+
+        std::unique_lock lock(mLock);
+
+        mEventQueue.push_back(event);
+        mCondition.notify_one();
+    }
+
+private:
+    std::mutex mLock;
+    std::condition_variable mCondition;
+    Event mPoppedEvent;
+    std::list<Event> mEventQueue;
+};
+
+// Operators for GTest macros.
+bool operator==(const EventTracker::Event& lhs, const EventTracker::Event& rhs) {
+    return lhs.type == rhs.type && lhs.clientId == rhs.clientId && lhs.jobId == rhs.jobId;
+}
+
+std::ostream& operator<<(std::ostream& str, const EventTracker::Event& v) {
+    str << EventTracker::toString(v);
+    return str;
+}
+
+struct TestClientCallback : public BnTranscodingClientCallback, public EventTracker {
+    TestClientCallback(int32_t id) : mClientId(id) {
+        ALOGI("TestClientCallback %d Created", mClientId);
+    }
+
+    virtual ~TestClientCallback() { ALOGI("TestClientCallback %d destroyed", mClientId); }
 
     Status onTranscodingFinished(
-            int32_t /* in_jobId */,
+            int32_t in_jobId,
             const ::aidl::android::media::TranscodingResultParcel& /* in_result */) override {
+        append(Finished(mClientId, in_jobId));
         return Status::ok();
     }
 
     Status onTranscodingFailed(
-            int32_t /* in_jobId */,
-            ::aidl::android::media::TranscodingErrorCode /*in_errorCode */) override {
+            int32_t in_jobId,
+            ::aidl::android::media::TranscodingErrorCode /* in_errorCode */) override {
+        append(Failed(mClientId, in_jobId));
         return Status::ok();
     }
 
@@ -81,57 +230,89 @@
         return Status::ok();
     }
 
-    Status onProgressUpdate(int32_t /* in_jobId */, int32_t /* in_progress */) override {
+    Status onProgressUpdate(int32_t in_jobId, int32_t in_progress) override {
+        // The progress numbers from the SimulatedTranscoder represents the
+        // event's type in the transcoder.
+        switch (in_progress) {
+        case SimulatedTranscoder::Event::Start:
+            append(EventTracker::Start(mClientId, in_jobId));
+            break;
+        case SimulatedTranscoder::Event::Pause:
+            append(EventTracker::Pause(mClientId, in_jobId));
+            break;
+        case SimulatedTranscoder::Event::Resume:
+            append(EventTracker::Resume(mClientId, in_jobId));
+            break;
+        default:
+            ALOGE("unrecognized progress number %d, ignored by test", in_progress);
+            break;
+        }
         return Status::ok();
     }
+
+    int32_t mClientId;
 };
 
 class MediaTranscodingServiceTest : public ::testing::Test {
 public:
-    MediaTranscodingServiceTest() { ALOGD("MediaTranscodingServiceTest created"); }
+    MediaTranscodingServiceTest() { ALOGI("MediaTranscodingServiceTest created"); }
 
-    ~MediaTranscodingServiceTest() { ALOGD("MediaTranscodingingServiceTest destroyed"); }
+    ~MediaTranscodingServiceTest() { ALOGI("MediaTranscodingingServiceTest destroyed"); }
 
     void SetUp() override {
+        // Need thread pool to receive callbacks, otherwise oneway callbacks are
+        // silently ignored.
+        ABinderProcess_startThreadPool();
         ::ndk::SpAIBinder binder(AServiceManager_getService("media.transcoding"));
         mService = IMediaTranscodingService::fromBinder(binder);
         if (mService == nullptr) {
             ALOGE("Failed to connect to the media.trascoding service.");
             return;
         }
-        mClientCallback = ::ndk::SharedRefBase::make<TestClient>();
-        mClientCallback2 = ::ndk::SharedRefBase::make<TestClient>();
-        mClientCallback3 = ::ndk::SharedRefBase::make<TestClient>();
+        mClientCallback1 = ::ndk::SharedRefBase::make<TestClientCallback>(CLIENT(1));
+        mClientCallback2 = ::ndk::SharedRefBase::make<TestClientCallback>(CLIENT(2));
+        mClientCallback3 = ::ndk::SharedRefBase::make<TestClientCallback>(CLIENT(3));
+    }
+
+    std::shared_ptr<ITranscodingClient> registerOneClient(
+            const char* packageName, const std::shared_ptr<TestClientCallback>& callback,
+            uid_t defaultUid) {
+        uid_t uid;
+        if (getUidForPackage(String16(packageName), 0 /*userId*/, uid) != NO_ERROR) {
+            uid = defaultUid;
+        }
+
+        ALOGD("registering %s with uid %d", packageName, uid);
+
+        std::shared_ptr<ITranscodingClient> client;
+        Status status = mService->registerClient(callback, kClientName, packageName, uid,
+                                                 kClientUseCallingPid, &client);
+        return status.isOk() ? client : nullptr;
     }
 
     void registerMultipleClients() {
         // Register 3 clients.
-        Status status =
-                mService->registerClient(mClientCallback, kClientName, kClientOpPackageName,
-                                         kClientUseCallingUid, kClientUseCallingPid, &mClient1);
-        EXPECT_TRUE(status.isOk());
+        mClient1 = registerOneClient(kClientPackageA, mClientCallback1, UID(1));
         EXPECT_TRUE(mClient1 != nullptr);
 
-        status = mService->registerClient(mClientCallback2, kClientName, kClientOpPackageName,
-                                          kClientUseCallingUid, kClientUseCallingPid, &mClient2);
-        EXPECT_TRUE(status.isOk());
+        mClient2 = registerOneClient(kClientPackageB, mClientCallback2, UID(2));
         EXPECT_TRUE(mClient2 != nullptr);
 
-        status = mService->registerClient(mClientCallback3, kClientName, kClientOpPackageName,
-                                          kClientUseCallingUid, kClientUseCallingPid, &mClient3);
-        EXPECT_TRUE(status.isOk());
+        mClient3 = registerOneClient(kClientPackageC, mClientCallback3, UID(3));
         EXPECT_TRUE(mClient3 != nullptr);
 
         // Check the number of clients.
         int32_t numOfClients;
-        status = mService->getNumOfClients(&numOfClients);
+        Status status = mService->getNumOfClients(&numOfClients);
         EXPECT_TRUE(status.isOk());
         EXPECT_EQ(3, numOfClients);
     }
 
     void unregisterMultipleClients() {
+        Status status;
+
         // Unregister the clients.
-        Status status = mClient1->unregister();
+        status = mClient1->unregister();
         EXPECT_TRUE(status.isOk());
 
         status = mClient2->unregister();
@@ -147,10 +328,66 @@
         EXPECT_EQ(0, numOfClients);
     }
 
+    static constexpr bool success = true;
+    static constexpr bool fail = false;
+
+    template <bool expectation = success>
+    bool submit(const std::shared_ptr<ITranscodingClient>& client, int32_t jobId,
+                const char* filename,
+                TranscodingJobPriority priority = TranscodingJobPriority::kNormal) {
+        constexpr bool shouldSucceed = (expectation == success);
+        bool result;
+        TranscodingRequestParcel request;
+        TranscodingJobParcel job;
+
+        request.fileName = filename;
+        request.priority = priority;
+        Status status = client->submitRequest(request, &job, &result);
+
+        EXPECT_TRUE(status.isOk());
+        EXPECT_EQ(result, shouldSucceed);
+        if (shouldSucceed) {
+            EXPECT_EQ(job.jobId, jobId);
+        }
+
+        return status.isOk() && (result == shouldSucceed) && (!shouldSucceed || job.jobId == jobId);
+    }
+
+    template <bool expectation = success>
+    bool cancel(const std::shared_ptr<ITranscodingClient>& client, int32_t jobId) {
+        constexpr bool shouldSucceed = (expectation == success);
+        bool result;
+        Status status = client->cancelJob(jobId, &result);
+
+        EXPECT_TRUE(status.isOk());
+        EXPECT_EQ(result, shouldSucceed);
+
+        return status.isOk() && (result == shouldSucceed);
+    }
+
+    template <bool expectation = success>
+    bool getJob(const std::shared_ptr<ITranscodingClient>& client, int32_t jobId,
+                const char* filename) {
+        constexpr bool shouldSucceed = (expectation == success);
+        bool result;
+        TranscodingJobParcel job;
+        Status status = client->getJobWithId(jobId, &job, &result);
+
+        EXPECT_TRUE(status.isOk());
+        EXPECT_EQ(result, shouldSucceed);
+        if (shouldSucceed) {
+            EXPECT_EQ(job.jobId, jobId);
+            EXPECT_EQ(job.request.fileName, filename);
+        }
+
+        return status.isOk() && (result == shouldSucceed) &&
+               (!shouldSucceed || (job.jobId == jobId && job.request.fileName == filename));
+    }
+
     std::shared_ptr<IMediaTranscodingService> mService;
-    std::shared_ptr<ITranscodingClientCallback> mClientCallback;
-    std::shared_ptr<ITranscodingClientCallback> mClientCallback2;
-    std::shared_ptr<ITranscodingClientCallback> mClientCallback3;
+    std::shared_ptr<TestClientCallback> mClientCallback1;
+    std::shared_ptr<TestClientCallback> mClientCallback2;
+    std::shared_ptr<TestClientCallback> mClientCallback3;
     std::shared_ptr<ITranscodingClient> mClient1;
     std::shared_ptr<ITranscodingClient> mClient2;
     std::shared_ptr<ITranscodingClient> mClient3;
@@ -169,7 +406,7 @@
     std::shared_ptr<ITranscodingClient> client;
 
     // Register the client with the service.
-    Status status = mService->registerClient(mClientCallback, kClientName, kClientOpPackageName,
+    Status status = mService->registerClient(mClientCallback1, kClientName, kClientOpPackageName,
                                              kClientUseCallingUid, kInvalidClientPid, &client);
     EXPECT_FALSE(status.isOk());
 }
@@ -178,7 +415,7 @@
     std::shared_ptr<ITranscodingClient> client;
 
     // Register the client with the service.
-    Status status = mService->registerClient(mClientCallback, kInvalidClientName,
+    Status status = mService->registerClient(mClientCallback1, kInvalidClientName,
                                              kInvalidClientOpPackageName, kClientUseCallingUid,
                                              kClientUseCallingPid, &client);
     EXPECT_FALSE(status.isOk());
@@ -189,7 +426,7 @@
 
     // Register the client with the service.
     Status status =
-            mService->registerClient(mClientCallback, kClientName, kInvalidClientOpPackageName,
+            mService->registerClient(mClientCallback1, kClientName, kInvalidClientOpPackageName,
                                      kClientUseCallingUid, kClientUseCallingPid, &client);
     EXPECT_FALSE(status.isOk());
 }
@@ -197,7 +434,7 @@
 TEST_F(MediaTranscodingServiceTest, TestRegisterOneClient) {
     std::shared_ptr<ITranscodingClient> client;
 
-    Status status = mService->registerClient(mClientCallback, kClientName, kClientOpPackageName,
+    Status status = mService->registerClient(mClientCallback1, kClientName, kClientOpPackageName,
                                              kClientUseCallingUid, kClientUseCallingPid, &client);
     EXPECT_TRUE(status.isOk());
 
@@ -223,7 +460,7 @@
 TEST_F(MediaTranscodingServiceTest, TestRegisterClientTwice) {
     std::shared_ptr<ITranscodingClient> client;
 
-    Status status = mService->registerClient(mClientCallback, kClientName, kClientOpPackageName,
+    Status status = mService->registerClient(mClientCallback1, kClientName, kClientOpPackageName,
                                              kClientUseCallingUid, kClientUseCallingPid, &client);
     EXPECT_TRUE(status.isOk());
 
@@ -232,7 +469,7 @@
 
     // Register the client again and expects failure.
     std::shared_ptr<ITranscodingClient> client1;
-    status = mService->registerClient(mClientCallback, kClientName, kClientOpPackageName,
+    status = mService->registerClient(mClientCallback1, kClientName, kClientOpPackageName,
                                       kClientUseCallingUid, kClientUseCallingPid, &client1);
     EXPECT_FALSE(status.isOk());
 
@@ -246,76 +483,194 @@
     unregisterMultipleClients();
 }
 
-TEST_F(MediaTranscodingServiceTest, TestSubmitCancelGetJobs) {
+TEST_F(MediaTranscodingServiceTest, TestJobIdIndependence) {
+    registerMultipleClients();
+
+    // Submit 2 requests on client1 first.
+    EXPECT_TRUE(submit(mClient1, 0, "test_file"));
+    EXPECT_TRUE(submit(mClient1, 1, "test_file"));
+
+    // Submit 2 requests on client2, jobId should be independent for each client.
+    EXPECT_TRUE(submit(mClient2, 0, "test_file"));
+    EXPECT_TRUE(submit(mClient2, 1, "test_file"));
+
+    // Cancel all jobs.
+    EXPECT_TRUE(cancel(mClient1, 0));
+    EXPECT_TRUE(cancel(mClient1, 1));
+    EXPECT_TRUE(cancel(mClient2, 0));
+    EXPECT_TRUE(cancel(mClient2, 1));
+
+    unregisterMultipleClients();
+}
+
+TEST_F(MediaTranscodingServiceTest, TestSubmitCancelJobs) {
     registerMultipleClients();
 
     // Test jobId assignment.
-    TranscodingRequestParcel request;
-    request.fileName = "test_file_0";
-    TranscodingJobParcel job;
-    bool result;
-    EXPECT_TRUE(mClient1->submitRequest(request, &job, &result).isOk());
-    EXPECT_TRUE(result);
-    EXPECT_EQ(job.jobId, 0);
-
-    request.fileName = "test_file_1";
-    EXPECT_TRUE(mClient1->submitRequest(request, &job, &result).isOk());
-    EXPECT_TRUE(result);
-    EXPECT_EQ(job.jobId, 1);
-
-    request.fileName = "test_file_2";
-    EXPECT_TRUE(mClient1->submitRequest(request, &job, &result).isOk());
-    EXPECT_TRUE(result);
-    EXPECT_EQ(job.jobId, 2);
+    EXPECT_TRUE(submit(mClient1, 0, "test_file_0"));
+    EXPECT_TRUE(submit(mClient1, 1, "test_file_1"));
+    EXPECT_TRUE(submit(mClient1, 2, "test_file_2"));
 
     // Test submit bad request (no valid fileName) fails.
-    TranscodingRequestParcel badRequest;
-    EXPECT_TRUE(mClient1->submitRequest(badRequest, &job, &result).isOk());
-    EXPECT_FALSE(result);
+    EXPECT_TRUE(submit<fail>(mClient1, 0, ""));
 
-    // Test get jobs by id.
-    EXPECT_TRUE(mClient1->getJobWithId(2, &job, &result).isOk());
-    EXPECT_EQ(job.jobId, 2);
-    EXPECT_EQ(job.request.fileName, "test_file_2");
-    EXPECT_TRUE(result);
+    // Test cancel non-existent job fails.
+    EXPECT_TRUE(cancel<fail>(mClient1, 100));
 
-    // Test get jobs by invalid id fails.
-    EXPECT_TRUE(mClient1->getJobWithId(100, &job, &result).isOk());
-    EXPECT_FALSE(result);
+    // Job 0 should start immediately and finish in 2 seconds, followed by Job 1 start.
+    EXPECT_EQ(mClientCallback1->pop(kPaddingUs), EventTracker::Start(CLIENT(1), 0));
+    EXPECT_EQ(mClientCallback1->pop(kJobWithPaddingUs), EventTracker::Finished(CLIENT(1), 0));
+    EXPECT_EQ(mClientCallback1->pop(kPaddingUs), EventTracker::Start(CLIENT(1), 1));
 
-    // Test cancel non-existent job fail.
-    EXPECT_TRUE(mClient2->cancelJob(100, &result).isOk());
-    EXPECT_FALSE(result);
-
-    // Test cancel valid jobId in arbitrary order.
-    EXPECT_TRUE(mClient1->cancelJob(2, &result).isOk());
-    EXPECT_TRUE(result);
-
-    EXPECT_TRUE(mClient1->cancelJob(0, &result).isOk());
-    EXPECT_TRUE(result);
-
-    EXPECT_TRUE(mClient1->cancelJob(1, &result).isOk());
-    EXPECT_TRUE(result);
+    // Test cancel valid jobId in random order.
+    // Test cancel finished job fails.
+    EXPECT_TRUE(cancel(mClient1, 2));
+    EXPECT_TRUE(cancel<fail>(mClient1, 0));
+    EXPECT_TRUE(cancel(mClient1, 1));
 
     // Test cancel job again fails.
-    EXPECT_TRUE(mClient1->cancelJob(1, &result).isOk());
-    EXPECT_FALSE(result);
+    EXPECT_TRUE(cancel<fail>(mClient1, 1));
 
-    // Test get job after cancel fails.
-    EXPECT_TRUE(mClient1->getJobWithId(2, &job, &result).isOk());
-    EXPECT_FALSE(result);
-
-    // Test jobId independence for each client.
-    EXPECT_TRUE(mClient2->submitRequest(request, &job, &result).isOk());
-    EXPECT_TRUE(result);
-    EXPECT_EQ(job.jobId, 0);
-
-    EXPECT_TRUE(mClient2->submitRequest(request, &job, &result).isOk());
-    EXPECT_TRUE(result);
-    EXPECT_EQ(job.jobId, 1);
+    // Test no more events arriving after cancel.
+    EXPECT_EQ(mClientCallback1->pop(kJobWithPaddingUs), EventTracker::NoEvent);
 
     unregisterMultipleClients();
 }
 
+TEST_F(MediaTranscodingServiceTest, TestGetJobs) {
+    registerMultipleClients();
+
+    // Submit 3 requests.
+    EXPECT_TRUE(submit(mClient1, 0, "test_file_0"));
+    EXPECT_TRUE(submit(mClient1, 1, "test_file_1"));
+    EXPECT_TRUE(submit(mClient1, 2, "test_file_2"));
+
+    // Test get jobs by id.
+    EXPECT_TRUE(getJob(mClient1, 2, "test_file_2"));
+    EXPECT_TRUE(getJob(mClient1, 1, "test_file_1"));
+    EXPECT_TRUE(getJob(mClient1, 0, "test_file_0"));
+
+    // Test get job by invalid id fails.
+    EXPECT_TRUE(getJob<fail>(mClient1, 100, ""));
+    EXPECT_TRUE(getJob<fail>(mClient1, -1, ""));
+
+    // Test get job after cancel fails.
+    EXPECT_TRUE(cancel(mClient1, 2));
+    EXPECT_TRUE(getJob<fail>(mClient1, 2, ""));
+
+    // Job 0 should start immediately and finish in 2 seconds, followed by Job 1 start.
+    EXPECT_EQ(mClientCallback1->pop(kPaddingUs), EventTracker::Start(CLIENT(1), 0));
+    EXPECT_EQ(mClientCallback1->pop(kJobWithPaddingUs), EventTracker::Finished(CLIENT(1), 0));
+    EXPECT_EQ(mClientCallback1->pop(kPaddingUs), EventTracker::Start(CLIENT(1), 1));
+
+    // Test get job after finish fails.
+    EXPECT_TRUE(getJob<fail>(mClient1, 0, ""));
+
+    // Test get the remaining job 1.
+    EXPECT_TRUE(getJob(mClient1, 1, "test_file_1"));
+
+    // Cancel remaining job 1.
+    EXPECT_TRUE(cancel(mClient1, 1));
+
+    unregisterMultipleClients();
+}
+
+TEST_F(MediaTranscodingServiceTest, TestSubmitCancelWithOfflineJobs) {
+    registerMultipleClients();
+
+    // Submit some offline jobs first.
+    EXPECT_TRUE(submit(mClient1, 0, "test_file_0", TranscodingJobPriority::kUnspecified));
+    EXPECT_TRUE(submit(mClient1, 1, "test_file_1", TranscodingJobPriority::kUnspecified));
+
+    // Job 0 should start immediately.
+    EXPECT_EQ(mClientCallback1->pop(kPaddingUs), EventTracker::Start(CLIENT(1), 0));
+
+    // Submit more real-time jobs.
+    EXPECT_TRUE(submit(mClient1, 2, "test_file_2"));
+    EXPECT_TRUE(submit(mClient1, 3, "test_file_3"));
+
+    // Job 0 should pause immediately and job 2 should start.
+    EXPECT_EQ(mClientCallback1->pop(kPaddingUs), EventTracker::Pause(CLIENT(1), 0));
+    EXPECT_EQ(mClientCallback1->pop(kPaddingUs), EventTracker::Start(CLIENT(1), 2));
+
+    // Job 2 should finish in 2 seconds and job 3 should start.
+    EXPECT_EQ(mClientCallback1->pop(kJobWithPaddingUs), EventTracker::Finished(CLIENT(1), 2));
+    EXPECT_EQ(mClientCallback1->pop(kPaddingUs), EventTracker::Start(CLIENT(1), 3));
+
+    // Cancel job 3 now
+    EXPECT_TRUE(cancel(mClient1, 3));
+
+    // Job 0 should resume and finish in 2 seconds, followed by job 1 start.
+    EXPECT_EQ(mClientCallback1->pop(kPaddingUs), EventTracker::Resume(CLIENT(1), 0));
+    EXPECT_EQ(mClientCallback1->pop(kJobWithPaddingUs), EventTracker::Finished(CLIENT(1), 0));
+    EXPECT_EQ(mClientCallback1->pop(kPaddingUs), EventTracker::Start(CLIENT(1), 1));
+
+    // Cancel remaining job 1.
+    EXPECT_TRUE(cancel(mClient1, 1));
+
+    unregisterMultipleClients();
+}
+
+TEST_F(MediaTranscodingServiceTest, TestTranscodingUidPolicy) {
+    ALOGD("TestTranscodingUidPolicy starting...");
+
+    EXPECT_TRUE(ShellHelper::RunCmd("input keyevent KEYCODE_WAKEUP"));
+    EXPECT_TRUE(ShellHelper::RunCmd("wm dismiss-keyguard"));
+    EXPECT_TRUE(ShellHelper::Stop(kClientPackageA));
+    EXPECT_TRUE(ShellHelper::Stop(kClientPackageB));
+    EXPECT_TRUE(ShellHelper::Stop(kClientPackageC));
+
+    registerMultipleClients();
+
+    ALOGD("Moving app A to top...");
+    EXPECT_TRUE(ShellHelper::Start(kClientPackageA, kTestActivityName));
+
+    // Submit 3 requests.
+    ALOGD("Submitting job to client1 (app A) ...");
+    EXPECT_TRUE(submit(mClient1, 0, "test_file_0"));
+    EXPECT_TRUE(submit(mClient1, 1, "test_file_1"));
+    EXPECT_TRUE(submit(mClient1, 2, "test_file_2"));
+
+    // Job 0 should start immediately.
+    EXPECT_EQ(mClientCallback1->pop(kPaddingUs), EventTracker::Start(CLIENT(1), 0));
+
+    ALOGD("Moving app B to top...");
+    EXPECT_TRUE(ShellHelper::Start(kClientPackageB, kTestActivityName));
+
+    // Job 0 should continue and finish in 2 seconds, then job 1 should start.
+    EXPECT_EQ(mClientCallback1->pop(kJobWithPaddingUs), EventTracker::Finished(CLIENT(1), 0));
+    EXPECT_EQ(mClientCallback1->pop(kPaddingUs), EventTracker::Start(CLIENT(1), 1));
+
+    ALOGD("Submitting job to client2 (app B) ...");
+    EXPECT_TRUE(submit(mClient2, 0, "test_file_0"));
+
+    // Client1's job should pause, client2's job should start.
+    EXPECT_EQ(mClientCallback1->pop(kPaddingUs), EventTracker::Pause(CLIENT(1), 1));
+    EXPECT_EQ(mClientCallback2->pop(kPaddingUs), EventTracker::Start(CLIENT(2), 0));
+
+    ALOGD("Moving app A back to top...");
+    EXPECT_TRUE(ShellHelper::Start(kClientPackageA, kTestActivityName));
+
+    // Client2's job should pause, client1's job 1 should resume.
+    EXPECT_EQ(mClientCallback2->pop(kPaddingUs), EventTracker::Pause(CLIENT(2), 0));
+    EXPECT_EQ(mClientCallback1->pop(kPaddingUs), EventTracker::Resume(CLIENT(1), 1));
+
+    // Client2's job 1 should finish in 2 seconds, then its job 2 should start.
+    EXPECT_EQ(mClientCallback1->pop(kJobWithPaddingUs), EventTracker::Finished(CLIENT(1), 1));
+    EXPECT_EQ(mClientCallback1->pop(kPaddingUs), EventTracker::Start(CLIENT(1), 2));
+
+    // After client2's jobs finish, client1's job should resume.
+    EXPECT_EQ(mClientCallback1->pop(kJobWithPaddingUs), EventTracker::Finished(CLIENT(1), 2));
+    EXPECT_EQ(mClientCallback2->pop(kPaddingUs), EventTracker::Resume(CLIENT(2), 0));
+
+    unregisterMultipleClients();
+
+    EXPECT_TRUE(ShellHelper::Stop(kClientPackageA));
+    EXPECT_TRUE(ShellHelper::Stop(kClientPackageB));
+    EXPECT_TRUE(ShellHelper::Stop(kClientPackageC));
+
+    ALOGD("TestTranscodingUidPolicy finished.");
+}
+
 }  // namespace media
 }  // namespace android