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/media/libmediatranscoding/TranscodingUidPolicy.cpp b/media/libmediatranscoding/TranscodingUidPolicy.cpp
index 9c8d3fe..36bb264 100644
--- a/media/libmediatranscoding/TranscodingUidPolicy.cpp
+++ b/media/libmediatranscoding/TranscodingUidPolicy.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-// #define LOG_NDEBUG 0
+//#define LOG_NDEBUG 0
#define LOG_TAG "TranscodingUidPolicy"
#include <binder/ActivityManager.h>
@@ -224,8 +224,9 @@
}
void TranscodingUidPolicy::updateTopUid_l() {
- // Update top uid state.
mTopUidState = ActivityManager::PROCESS_STATE_UNKNOWN;
+
+ // Find the lowest uid state (ignoring PROCESS_STATE_UNKNOWN) with some monitored uids.
for (auto stateIt = mStateUidMap.begin(); stateIt != mStateUidMap.end(); stateIt++) {
if (stateIt->first != ActivityManager::PROCESS_STATE_UNKNOWN && !stateIt->second.empty()) {
mTopUidState = stateIt->first;
diff --git a/media/libmediatranscoding/include/media/TranscoderInterface.h b/media/libmediatranscoding/include/media/TranscoderInterface.h
index a2afa00..3c72c17 100644
--- a/media/libmediatranscoding/include/media/TranscoderInterface.h
+++ b/media/libmediatranscoding/include/media/TranscoderInterface.h
@@ -22,12 +22,14 @@
namespace android {
using ::aidl::android::media::TranscodingErrorCode;
+class TranscoderCallbackInterface;
// Interface for the scheduler to call the transcoder to take actions.
class TranscoderInterface {
public:
// TODO(chz): determine what parameters are needed here.
// For now, always pass in clientId&jobId.
+ virtual void setCallback(const std::shared_ptr<TranscoderCallbackInterface>& cb) = 0;
virtual void start(int64_t clientId, int32_t jobId) = 0;
virtual void pause(int64_t clientId, int32_t jobId) = 0;
virtual void resume(int64_t clientId, int32_t jobId) = 0;
diff --git a/media/libmediatranscoding/tests/TranscodingJobScheduler_tests.cpp b/media/libmediatranscoding/tests/TranscodingJobScheduler_tests.cpp
index 95edf1d..adb16a2 100644
--- a/media/libmediatranscoding/tests/TranscodingJobScheduler_tests.cpp
+++ b/media/libmediatranscoding/tests/TranscodingJobScheduler_tests.cpp
@@ -84,6 +84,8 @@
virtual ~TestTranscoder() {}
// TranscoderInterface
+ void setCallback(const std::shared_ptr<TranscoderCallbackInterface>& /*cb*/) override {}
+
void start(int64_t clientId, int32_t jobId) override {
mEventQueue.push_back(Start(clientId, jobId));
}
@@ -157,6 +159,7 @@
Status onTranscodingFinished(int32_t in_jobId,
const TranscodingResultParcel& in_result) override {
EXPECT_EQ(in_jobId, in_result.jobId);
+ ALOGD("TestClientCallback: received onTranscodingFinished");
mOwner->onFinished(mClientId, in_jobId);
return Status::ok();
}
diff --git a/services/mediatranscoding/Android.bp b/services/mediatranscoding/Android.bp
index 17347a9..79e9fbc 100644
--- a/services/mediatranscoding/Android.bp
+++ b/services/mediatranscoding/Android.bp
@@ -2,7 +2,10 @@
cc_library_shared {
name: "libmediatranscodingservice",
- srcs: ["MediaTranscodingService.cpp"],
+ srcs: [
+ "MediaTranscodingService.cpp",
+ "SimulatedTranscoder.cpp",
+ ],
shared_libs: [
"libbase",
@@ -11,6 +14,10 @@
"libmediatranscoding",
"libutils",
],
+
+ export_shared_lib_headers: [
+ "libmediatranscoding",
+ ],
static_libs: [
"mediatranscoding_aidl_interface-ndk_platform",
diff --git a/services/mediatranscoding/MediaTranscodingService.cpp b/services/mediatranscoding/MediaTranscodingService.cpp
index 6c10e3e..9e62b97 100644
--- a/services/mediatranscoding/MediaTranscodingService.cpp
+++ b/services/mediatranscoding/MediaTranscodingService.cpp
@@ -27,6 +27,8 @@
#include <utils/Log.h>
#include <utils/Vector.h>
+#include "SimulatedTranscoder.h"
+
namespace android {
// Convenience methods for constructing binder::Status objects for error returns
@@ -49,26 +51,8 @@
}
}
-// DummyTranscoder is currently used to instantiate MediaTranscodingService on
-// service side for testing, so that we could actually test the IPC calls of
-// MediaTranscodingService to expose some issues that's observable only over IPC.
-class DummyTranscoder : public TranscoderInterface {
- void start(int64_t clientId, int32_t jobId) override {
- (void)clientId;
- (void)jobId;
- }
- void pause(int64_t clientId, int32_t jobId) override {
- (void)clientId;
- (void)jobId;
- }
- void resume(int64_t clientId, int32_t jobId) override {
- (void)clientId;
- (void)jobId;
- }
-};
-
MediaTranscodingService::MediaTranscodingService()
- : MediaTranscodingService(std::make_shared<DummyTranscoder>(),
+ : MediaTranscodingService(std::make_shared<SimulatedTranscoder>(),
std::make_shared<TranscodingUidPolicy>()) {}
MediaTranscodingService::MediaTranscodingService(
@@ -77,6 +61,8 @@
: mJobScheduler(new TranscodingJobScheduler(transcoder, uidPolicy)),
mClientManager(new TranscodingClientManager(mJobScheduler)) {
ALOGV("MediaTranscodingService is created");
+
+ transcoder->setCallback(mJobScheduler);
uidPolicy->setCallback(mJobScheduler);
}
diff --git a/services/mediatranscoding/SimulatedTranscoder.cpp b/services/mediatranscoding/SimulatedTranscoder.cpp
new file mode 100644
index 0000000..7fbfb99
--- /dev/null
+++ b/services/mediatranscoding/SimulatedTranscoder.cpp
@@ -0,0 +1,142 @@
+/*
+ * 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 "SimulatedTranscoder"
+#include "SimulatedTranscoder.h"
+
+#include <utils/Log.h>
+
+#include <thread>
+
+namespace android {
+
+//static
+const char* SimulatedTranscoder::toString(Event::Type type) {
+ switch (type) {
+ case Event::Start:
+ return "Start";
+ case Event::Pause:
+ return "Pause";
+ case Event::Resume:
+ return "Resume";
+ default:
+ break;
+ }
+ return "(unknown)";
+}
+
+SimulatedTranscoder::SimulatedTranscoder() {
+ std::thread(&SimulatedTranscoder::threadLoop, this).detach();
+}
+
+void SimulatedTranscoder::setCallback(const std::shared_ptr<TranscoderCallbackInterface>& cb) {
+ mCallback = cb;
+}
+
+void SimulatedTranscoder::start(int64_t clientId, int32_t jobId) {
+ queueEvent(Event::Start, clientId, jobId);
+}
+
+void SimulatedTranscoder::pause(int64_t clientId, int32_t jobId) {
+ queueEvent(Event::Pause, clientId, jobId);
+}
+
+void SimulatedTranscoder::resume(int64_t clientId, int32_t jobId) {
+ queueEvent(Event::Resume, clientId, jobId);
+}
+
+void SimulatedTranscoder::queueEvent(Event::Type type, int64_t clientId, int32_t jobId) {
+ ALOGV("%s: job {%lld, %d}: %s", __FUNCTION__, (long long)clientId, jobId, toString(type));
+
+ auto lock = std::scoped_lock(mLock);
+
+ mQueue.push_back({type, clientId, jobId});
+ mCondition.notify_one();
+}
+
+void SimulatedTranscoder::threadLoop() {
+ bool running = false;
+ std::chrono::microseconds remainingUs(kJobDurationUs);
+ std::chrono::system_clock::time_point lastRunningTime;
+ Event lastRunningEvent;
+
+ std::unique_lock<std::mutex> lock(mLock);
+ // SimulatedTranscoder currently lives in the transcoding service, as long as
+ // MediaTranscodingService itself.
+ while (true) {
+ // Wait for the next event.
+ while (mQueue.empty()) {
+ if (!running) {
+ mCondition.wait(lock);
+ continue;
+ }
+ // If running, wait for the remaining life of this job. Report finish if timed out.
+ std::cv_status status = mCondition.wait_for(lock, remainingUs);
+ if (status == std::cv_status::timeout) {
+ running = false;
+
+ auto callback = mCallback.lock();
+ if (callback != nullptr) {
+ lock.unlock();
+ callback->onFinish(lastRunningEvent.clientId, lastRunningEvent.jobId);
+ lock.lock();
+ }
+ } else {
+ // Advance last running time and remaining time. This is needed to guard
+ // against bad events (which will be ignored) or spurious wakeups, in that
+ // case we don't want to wait for the same time again.
+ auto now = std::chrono::system_clock::now();
+ remainingUs -= (now - lastRunningTime);
+ lastRunningTime = now;
+ }
+ }
+
+ // Handle the events, adjust state and send updates to client accordingly.
+ while (!mQueue.empty()) {
+ Event event = *mQueue.begin();
+ mQueue.pop_front();
+
+ ALOGV("%s: job {%lld, %d}: %s", __FUNCTION__, (long long)event.clientId, event.jobId,
+ toString(event.type));
+
+ if (!running && (event.type == Event::Start || event.type == Event::Resume)) {
+ running = true;
+ lastRunningTime = std::chrono::system_clock::now();
+ lastRunningEvent = event;
+ if (event.type == Event::Start) {
+ remainingUs = std::chrono::microseconds(kJobDurationUs);
+ }
+ } else if (running && event.type == Event::Pause) {
+ running = false;
+ remainingUs -= (std::chrono::system_clock::now() - lastRunningTime);
+ } else {
+ ALOGW("%s: discarding bad event: job {%lld, %d}: %s", __FUNCTION__,
+ (long long)event.clientId, event.jobId, toString(event.type));
+ continue;
+ }
+
+ auto callback = mCallback.lock();
+ if (callback != nullptr) {
+ lock.unlock();
+ callback->onProgressUpdate(event.clientId, event.jobId, event.type);
+ lock.lock();
+ }
+ }
+ }
+}
+
+} // namespace android
diff --git a/services/mediatranscoding/SimulatedTranscoder.h b/services/mediatranscoding/SimulatedTranscoder.h
new file mode 100644
index 0000000..41a06f5
--- /dev/null
+++ b/services/mediatranscoding/SimulatedTranscoder.h
@@ -0,0 +1,69 @@
+/*
+ * 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_SIMULATED_TRANSCODER_H
+#define ANDROID_MEDIA_SIMULATED_TRANSCODER_H
+
+#include <android-base/thread_annotations.h>
+#include <media/TranscoderInterface.h>
+
+#include <list>
+#include <mutex>
+
+namespace android {
+
+/**
+ * SimulatedTranscoder is currently used to instantiate MediaTranscodingService
+ * on service side for testing, so that we could actually test the IPC calls of
+ * MediaTranscodingService to expose issues that's observable only over IPC.
+ *
+ * SimulatedTranscoder simulates job execution by reporting finish after kJobDurationUs.
+ * Job lifecycle events are reported via progress updates with special progress
+ * numbers (equal to the Event's type).
+ */
+class SimulatedTranscoder : public TranscoderInterface {
+public:
+ struct Event {
+ enum Type { NoEvent, Start, Pause, Resume, Finished, Failed } type;
+ int64_t clientId;
+ int32_t jobId;
+ };
+
+ static constexpr int64_t kJobDurationUs = 1000000;
+
+ SimulatedTranscoder();
+
+ // TranscoderInterface
+ void setCallback(const std::shared_ptr<TranscoderCallbackInterface>& cb) override;
+ void start(int64_t clientId, int32_t jobId) override;
+ void pause(int64_t clientId, int32_t jobId) override;
+ void resume(int64_t clientId, int32_t jobId) override;
+ // ~TranscoderInterface
+
+private:
+ std::weak_ptr<TranscoderCallbackInterface> mCallback;
+ std::mutex mLock;
+ std::condition_variable mCondition;
+ std::list<Event> mQueue GUARDED_BY(mLock);
+
+ static const char* toString(Event::Type type);
+ void queueEvent(Event::Type type, int64_t clientId, int32_t jobId);
+ void threadLoop();
+};
+
+} // namespace android
+
+#endif // ANDROID_MEDIA_SIMULATED_TRANSCODER_H
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