Merge "Add haptic-generating effect."
diff --git a/drm/drmserver/Android.bp b/drm/drmserver/Android.bp
index b68e6c2..fcd291f 100644
--- a/drm/drmserver/Android.bp
+++ b/drm/drmserver/Android.bp
@@ -43,7 +43,7 @@
         "-Werror",
     ],
 
-    compile_multilib: "32",
+    compile_multilib: "prefer32",
 
     init_rc: ["drmserver.rc"],
 }
diff --git a/media/TEST_MAPPING b/media/TEST_MAPPING
index 206f87f..b006f38 100644
--- a/media/TEST_MAPPING
+++ b/media/TEST_MAPPING
@@ -48,7 +48,7 @@
         }
     ],
 
-    "staged-platinum-postsubmit": [
+    "platinum-postsubmit": [
         // runs regularly, independent of changes in this tree.
         // signals if changes elsewhere break media functionality
         {
diff --git a/media/codec2/sfplugin/CCodec.cpp b/media/codec2/sfplugin/CCodec.cpp
index 70171fd..73b3857 100644
--- a/media/codec2/sfplugin/CCodec.cpp
+++ b/media/codec2/sfplugin/CCodec.cpp
@@ -1868,7 +1868,7 @@
                 config->mInputSurface->onInputBufferDone(work->input.ordinal.frameIndex);
             }
             mChannel->onWorkDone(
-                    std::move(work), changed ? config->mOutputFormat : nullptr,
+                    std::move(work), changed ? config->mOutputFormat->dup() : nullptr,
                     initData.hasChanged() ? initData.update().get() : nullptr);
             break;
         }
diff --git a/media/libaudiohal/impl/StreamPowerLog.h b/media/libaudiohal/impl/StreamPowerLog.h
index 5fd3912..f6a554b 100644
--- a/media/libaudiohal/impl/StreamPowerLog.h
+++ b/media/libaudiohal/impl/StreamPowerLog.h
@@ -19,6 +19,7 @@
 
 #include <audio_utils/clock.h>
 #include <audio_utils/PowerLog.h>
+#include <cutils/bitops.h>
 #include <cutils/properties.h>
 #include <system/audio.h>
 
diff --git a/media/libmediatranscoding/TranscoderWrapper.cpp b/media/libmediatranscoding/TranscoderWrapper.cpp
index 428d86e..aaa15c4 100644
--- a/media/libmediatranscoding/TranscoderWrapper.cpp
+++ b/media/libmediatranscoding/TranscoderWrapper.cpp
@@ -56,6 +56,38 @@
     }
 }
 
+static AMediaFormat* getVideoFormat(
+        const char* originalMime,
+        const std::optional<TranscodingVideoTrackFormat>& requestedFormat) {
+    if (requestedFormat == std::nullopt) {
+        return nullptr;
+    }
+
+    AMediaFormat* format = AMediaFormat_new();
+    bool changed = false;
+    if (requestedFormat->codecType == TranscodingVideoCodecType::kHevc &&
+        strcmp(originalMime, AMEDIA_MIMETYPE_VIDEO_HEVC)) {
+        AMediaFormat_setString(format, AMEDIAFORMAT_KEY_MIME, AMEDIA_MIMETYPE_VIDEO_HEVC);
+        changed = true;
+    } else if (requestedFormat->codecType == TranscodingVideoCodecType::kAvc &&
+               strcmp(originalMime, AMEDIA_MIMETYPE_VIDEO_AVC)) {
+        AMediaFormat_setString(format, AMEDIAFORMAT_KEY_MIME, AMEDIA_MIMETYPE_VIDEO_AVC);
+        changed = true;
+    }
+    if (requestedFormat->bitrateBps > 0) {
+        AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_BIT_RATE, requestedFormat->bitrateBps);
+        changed = true;
+    }
+    // TODO: translate other fields from requestedFormat to the format for MediaTranscoder.
+    // Also need to determine more settings to expose in TranscodingVideoTrackFormat.
+    if (!changed) {
+        AMediaFormat_delete(format);
+        // Use null format for passthru.
+        format = nullptr;
+    }
+    return format;
+}
+
 //static
 const char* TranscoderWrapper::toString(Event::Type type) {
     switch (type) {
@@ -105,7 +137,7 @@
     }
 
     virtual void onCodecResourceLost(const MediaTranscoder* transcoder __unused,
-                                     const std::shared_ptr<const Parcelable>& pausedState
+                                     const std::shared_ptr<const Parcel>& pausedState
                                              __unused) override {
         ALOGV("%s: job {%lld, %d}", __FUNCTION__, (long long)mClientId, mJobId);
     }
@@ -126,51 +158,87 @@
 
 void TranscoderWrapper::start(ClientIdType clientId, JobIdType jobId,
                               const TranscodingRequestParcel& request,
-                              const std::shared_ptr<ITranscodingClientCallback>& callback) {
+                              const std::shared_ptr<ITranscodingClientCallback>& clientCb) {
     queueEvent(Event::Start, clientId, jobId, [=] {
-        TranscodingErrorCode err = handleStart(clientId, jobId, request, callback);
+        TranscodingErrorCode err = handleStart(clientId, jobId, request, clientCb);
 
+        auto callback = mCallback.lock();
         if (err != TranscodingErrorCode::kNoError) {
             cleanup();
 
-            auto callback = mCallback.lock();
             if (callback != nullptr) {
                 callback->onError(clientId, jobId, err);
             }
+        } else {
+            if (callback != nullptr) {
+                callback->onStarted(clientId, jobId);
+            }
         }
     });
 }
 
 void TranscoderWrapper::pause(ClientIdType clientId, JobIdType jobId) {
-    queueEvent(Event::Pause, clientId, jobId, [] {});
+    queueEvent(Event::Pause, clientId, jobId, [=] {
+        TranscodingErrorCode err = handlePause(clientId, jobId);
+
+        cleanup();
+
+        auto callback = mCallback.lock();
+        if (callback != nullptr) {
+            if (err != TranscodingErrorCode::kNoError) {
+                callback->onError(clientId, jobId, err);
+            } else {
+                callback->onPaused(clientId, jobId);
+            }
+        }
+    });
 }
 
-void TranscoderWrapper::resume(ClientIdType clientId, JobIdType jobId) {
-    queueEvent(Event::Resume, clientId, jobId, [] {});
+void TranscoderWrapper::resume(ClientIdType clientId, JobIdType jobId,
+                               const TranscodingRequestParcel& request,
+                               const std::shared_ptr<ITranscodingClientCallback>& clientCb) {
+    queueEvent(Event::Resume, clientId, jobId, [=] {
+        TranscodingErrorCode err = handleResume(clientId, jobId, request, clientCb);
+
+        auto callback = mCallback.lock();
+        if (err != TranscodingErrorCode::kNoError) {
+            cleanup();
+
+            if (callback != nullptr) {
+                callback->onError(clientId, jobId, err);
+            }
+        } else {
+            if (callback != nullptr) {
+                callback->onResumed(clientId, jobId);
+            }
+        }
+    });
 }
 
 void TranscoderWrapper::stop(ClientIdType clientId, JobIdType jobId) {
     queueEvent(Event::Stop, clientId, jobId, [=] {
-        if (clientId != mCurrentClientId || jobId != mCurrentJobId) {
-            ALOGW("Stopping job {%lld, %d} that's not current job {%lld, %d}", (long long)clientId,
-                  jobId, (long long)mCurrentClientId, mCurrentJobId);
-        }
-
-        // stop transcoder.
-        media_status_t err = mTranscoder->cancel();
-        if (err != AMEDIA_OK) {
-            ALOGE("failed to stop transcoder: %d", err);
+        if (mTranscoder != nullptr && clientId == mCurrentClientId && jobId == mCurrentJobId) {
+            // Cancelling the currently running job.
+            media_status_t err = mTranscoder->cancel();
+            if (err != AMEDIA_OK) {
+                ALOGE("failed to stop transcoder: %d", err);
+            } else {
+                ALOGI("transcoder stopped");
+            }
+            cleanup();
         } else {
-            ALOGI("transcoder stopped");
+            // For jobs that's not currently running, release any pausedState for the job.
+            mPausedStateMap.erase(JobKeyType(clientId, jobId));
         }
-
-        cleanup();
+        // No callback needed for stop.
     });
 }
 
 void TranscoderWrapper::onFinish(ClientIdType clientId, JobIdType jobId) {
     queueEvent(Event::Finish, clientId, jobId, [=] {
-        cleanup();
+        if (mTranscoder != nullptr && clientId == mCurrentClientId && jobId == mCurrentJobId) {
+            cleanup();
+        }
 
         auto callback = mCallback.lock();
         if (callback != nullptr) {
@@ -182,7 +250,9 @@
 void TranscoderWrapper::onError(ClientIdType clientId, JobIdType jobId,
                                 TranscodingErrorCode error) {
     queueEvent(Event::Error, clientId, jobId, [=] {
-        cleanup();
+        if (mTranscoder != nullptr && clientId == mCurrentClientId && jobId == mCurrentJobId) {
+            cleanup();
+        }
 
         auto callback = mCallback.lock();
         if (callback != nullptr) {
@@ -191,41 +261,10 @@
     });
 }
 
-static AMediaFormat* getVideoFormat(
-        const char* originalMime,
-        const std::optional<TranscodingVideoTrackFormat>& requestedFormat) {
-    if (requestedFormat == std::nullopt) {
-        return nullptr;
-    }
-
-    AMediaFormat* format = AMediaFormat_new();
-    bool changed = false;
-    if (requestedFormat->codecType == TranscodingVideoCodecType::kHevc &&
-        strcmp(originalMime, AMEDIA_MIMETYPE_VIDEO_HEVC)) {
-        AMediaFormat_setString(format, AMEDIAFORMAT_KEY_MIME, AMEDIA_MIMETYPE_VIDEO_HEVC);
-        changed = true;
-    } else if (requestedFormat->codecType == TranscodingVideoCodecType::kAvc &&
-               strcmp(originalMime, AMEDIA_MIMETYPE_VIDEO_AVC)) {
-        AMediaFormat_setString(format, AMEDIAFORMAT_KEY_MIME, AMEDIA_MIMETYPE_VIDEO_AVC);
-        changed = true;
-    }
-    if (requestedFormat->bitrateBps > 0) {
-        AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_BIT_RATE, requestedFormat->bitrateBps);
-        changed = true;
-    }
-    // TODO: translate other fields from requestedFormat to the format for MediaTranscoder.
-    // Also need to determine more settings to expose in TranscodingVideoTrackFormat.
-    if (!changed) {
-        AMediaFormat_delete(format);
-        // Use null format for passthru.
-        format = nullptr;
-    }
-    return format;
-}
-
-TranscodingErrorCode TranscoderWrapper::handleStart(
+TranscodingErrorCode TranscoderWrapper::setupTranscoder(
         ClientIdType clientId, JobIdType jobId, const TranscodingRequestParcel& request,
-        const std::shared_ptr<ITranscodingClientCallback>& clientCb) {
+        const std::shared_ptr<ITranscodingClientCallback>& clientCb,
+        const std::shared_ptr<const Parcel>& pausedState) {
     if (clientCb == nullptr) {
         ALOGE("client callback is null");
         return TranscodingErrorCode::kInvalidParameter;
@@ -244,7 +283,10 @@
         return TranscodingErrorCode::kErrorIO;
     }
 
-    status = clientCb->openFileDescriptor(request.destinationFilePath, "w", &dstFd);
+    // Open dest file with "rw", as the transcoder could potentially reuse part of it
+    // for resume case. We might want the further differentiate and open with "w" only
+    // for start.
+    status = clientCb->openFileDescriptor(request.destinationFilePath, "rw", &dstFd);
     if (!status.isOk() || dstFd.get() < 0) {
         ALOGE("failed to open destination");
         return TranscodingErrorCode::kErrorIO;
@@ -253,7 +295,7 @@
     mCurrentClientId = clientId;
     mCurrentJobId = jobId;
     mTranscoderCb = std::make_shared<CallbackImpl>(shared_from_this(), clientId, jobId);
-    mTranscoder = MediaTranscoder::create(mTranscoderCb, nullptr);
+    mTranscoder = MediaTranscoder::create(mTranscoderCb, pausedState);
     if (mTranscoder == nullptr) {
         ALOGE("failed to create transcoder");
         return TranscodingErrorCode::kUnknown;
@@ -296,13 +338,79 @@
         return toTranscodingError(err);
     }
 
-    err = mTranscoder->start();
-    if (err != AMEDIA_OK) {
-        ALOGE("failed to start transcoder: %d", err);
-        return toTranscodingError(err);
+    return TranscodingErrorCode::kNoError;
+}
+
+TranscodingErrorCode TranscoderWrapper::handleStart(
+        ClientIdType clientId, JobIdType jobId, const TranscodingRequestParcel& request,
+        const std::shared_ptr<ITranscodingClientCallback>& clientCb) {
+    ALOGI("setting up transcoder for start");
+    TranscodingErrorCode err = setupTranscoder(clientId, jobId, request, clientCb);
+    if (err != TranscodingErrorCode::kNoError) {
+        ALOGI("%s: failed to setup transcoder", __FUNCTION__);
+        return err;
     }
 
-    ALOGI("transcoder started");
+    media_status_t status = mTranscoder->start();
+    if (status != AMEDIA_OK) {
+        ALOGE("%s: failed to start transcoder: %d", __FUNCTION__, err);
+        return toTranscodingError(status);
+    }
+
+    ALOGI("%s: transcoder started", __FUNCTION__);
+    return TranscodingErrorCode::kNoError;
+}
+
+TranscodingErrorCode TranscoderWrapper::handlePause(ClientIdType clientId, JobIdType jobId) {
+    if (mTranscoder == nullptr) {
+        ALOGE("%s: transcoder is not running", __FUNCTION__);
+        return TranscodingErrorCode::kInvalidOperation;
+    }
+
+    if (clientId != mCurrentClientId || jobId != mCurrentJobId) {
+        ALOGW("%s: stopping job {%lld, %d} that's not current job {%lld, %d}", __FUNCTION__,
+              (long long)clientId, jobId, (long long)mCurrentClientId, mCurrentJobId);
+    }
+
+    std::shared_ptr<const Parcel> pauseStates;
+    media_status_t err = mTranscoder->pause(&pauseStates);
+    if (err != AMEDIA_OK) {
+        ALOGE("%s: failed to pause transcoder: %d", __FUNCTION__, err);
+        return toTranscodingError(err);
+    }
+    mPausedStateMap[JobKeyType(clientId, jobId)] = pauseStates;
+
+    ALOGI("%s: transcoder paused", __FUNCTION__);
+    return TranscodingErrorCode::kNoError;
+}
+
+TranscodingErrorCode TranscoderWrapper::handleResume(
+        ClientIdType clientId, JobIdType jobId, const TranscodingRequestParcel& request,
+        const std::shared_ptr<ITranscodingClientCallback>& clientCb) {
+    std::shared_ptr<const Parcel> pausedState;
+    auto it = mPausedStateMap.find(JobKeyType(clientId, jobId));
+    if (it != mPausedStateMap.end()) {
+        pausedState = it->second;
+        mPausedStateMap.erase(it);
+    } else {
+        ALOGE("%s: can't find paused state", __FUNCTION__);
+        return TranscodingErrorCode::kInvalidOperation;
+    }
+
+    ALOGI("setting up transcoder for resume");
+    TranscodingErrorCode err = setupTranscoder(clientId, jobId, request, clientCb, pausedState);
+    if (err != TranscodingErrorCode::kNoError) {
+        ALOGE("%s: failed to setup transcoder", __FUNCTION__);
+        return err;
+    }
+
+    media_status_t status = mTranscoder->resume();
+    if (status != AMEDIA_OK) {
+        ALOGE("%s: failed to resume transcoder: %d", __FUNCTION__, err);
+        return toTranscodingError(status);
+    }
+
+    ALOGI("%s: transcoder resumed", __FUNCTION__);
     return TranscodingErrorCode::kNoError;
 }
 
@@ -339,7 +447,9 @@
         ALOGD("%s: job {%lld, %d}: %s", __FUNCTION__, (long long)event.clientId, event.jobId,
               toString(event.type));
 
+        lock.unlock();
         event.runnable();
+        lock.lock();
     }
 }
 
diff --git a/media/libmediatranscoding/TranscodingJobScheduler.cpp b/media/libmediatranscoding/TranscodingJobScheduler.cpp
index 07eb949..3e4f319 100644
--- a/media/libmediatranscoding/TranscodingJobScheduler.cpp
+++ b/media/libmediatranscoding/TranscodingJobScheduler.cpp
@@ -79,7 +79,8 @@
                 mTranscoder->start(topJob->key.first, topJob->key.second, topJob->request,
                                    topJob->callback.lock());
             } else if (topJob->state == Job::PAUSED) {
-                mTranscoder->resume(topJob->key.first, topJob->key.second);
+                mTranscoder->resume(topJob->key.first, topJob->key.second, topJob->request,
+                                    topJob->callback.lock());
             }
             topJob->state = Job::RUNNING;
         }
diff --git a/media/libmediatranscoding/include/media/TranscoderInterface.h b/media/libmediatranscoding/include/media/TranscoderInterface.h
index 85e0164..1a3f505 100644
--- a/media/libmediatranscoding/include/media/TranscoderInterface.h
+++ b/media/libmediatranscoding/include/media/TranscoderInterface.h
@@ -32,14 +32,14 @@
 // 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(ClientIdType clientId, JobIdType jobId,
                        const TranscodingRequestParcel& request,
                        const std::shared_ptr<ITranscodingClientCallback>& clientCallback) = 0;
     virtual void pause(ClientIdType clientId, JobIdType jobId) = 0;
-    virtual void resume(ClientIdType clientId, JobIdType jobId) = 0;
+    virtual void resume(ClientIdType clientId, JobIdType jobId,
+                        const TranscodingRequestParcel& request,
+                        const std::shared_ptr<ITranscodingClientCallback>& clientCallback) = 0;
     virtual void stop(ClientIdType clientId, JobIdType jobId) = 0;
 
 protected:
diff --git a/media/libmediatranscoding/include/media/TranscoderWrapper.h b/media/libmediatranscoding/include/media/TranscoderWrapper.h
index ba48085..804119f 100644
--- a/media/libmediatranscoding/include/media/TranscoderWrapper.h
+++ b/media/libmediatranscoding/include/media/TranscoderWrapper.h
@@ -21,11 +21,13 @@
 #include <media/TranscoderInterface.h>
 
 #include <list>
+#include <map>
 #include <mutex>
 
 namespace android {
 
 class MediaTranscoder;
+class Parcelable;
 
 /*
  * Wrapper class around MediaTranscoder.
@@ -41,7 +43,9 @@
                        const TranscodingRequestParcel& request,
                        const std::shared_ptr<ITranscodingClientCallback>& clientCallback) override;
     virtual void pause(ClientIdType clientId, JobIdType jobId) override;
-    virtual void resume(ClientIdType clientId, JobIdType jobId) override;
+    virtual void resume(ClientIdType clientId, JobIdType jobId,
+                        const TranscodingRequestParcel& request,
+                        const std::shared_ptr<ITranscodingClientCallback>& clientCallback) override;
     virtual void stop(ClientIdType clientId, JobIdType jobId) override;
 
 private:
@@ -52,12 +56,15 @@
         JobIdType jobId;
         std::function<void()> runnable;
     };
+    using JobKeyType = std::pair<ClientIdType, JobIdType>;
+
     std::shared_ptr<CallbackImpl> mTranscoderCb;
     std::shared_ptr<MediaTranscoder> mTranscoder;
     std::weak_ptr<TranscoderCallbackInterface> mCallback;
     std::mutex mLock;
     std::condition_variable mCondition;
     std::list<Event> mQueue;  // GUARDED_BY(mLock);
+    std::map<JobKeyType, std::shared_ptr<const Parcel>> mPausedStateMap;
     ClientIdType mCurrentClientId;
     JobIdType mCurrentJobId;
 
@@ -68,6 +75,14 @@
     TranscodingErrorCode handleStart(ClientIdType clientId, JobIdType jobId,
                                      const TranscodingRequestParcel& request,
                                      const std::shared_ptr<ITranscodingClientCallback>& callback);
+    TranscodingErrorCode handlePause(ClientIdType clientId, JobIdType jobId);
+    TranscodingErrorCode handleResume(ClientIdType clientId, JobIdType jobId,
+                                      const TranscodingRequestParcel& request,
+                                      const std::shared_ptr<ITranscodingClientCallback>& callback);
+    TranscodingErrorCode setupTranscoder(
+            ClientIdType clientId, JobIdType jobId, const TranscodingRequestParcel& request,
+            const std::shared_ptr<ITranscodingClientCallback>& callback,
+            const std::shared_ptr<const Parcel>& pausedState = nullptr);
 
     void cleanup();
     void queueEvent(Event::Type type, ClientIdType clientId, JobIdType jobId,
diff --git a/media/libmediatranscoding/tests/TranscodingJobScheduler_tests.cpp b/media/libmediatranscoding/tests/TranscodingJobScheduler_tests.cpp
index 1931a0e..d21b595 100644
--- a/media/libmediatranscoding/tests/TranscodingJobScheduler_tests.cpp
+++ b/media/libmediatranscoding/tests/TranscodingJobScheduler_tests.cpp
@@ -94,7 +94,8 @@
     void pause(ClientIdType clientId, JobIdType jobId) override {
         mEventQueue.push_back(Pause(clientId, jobId));
     }
-    void resume(ClientIdType clientId, JobIdType jobId) override {
+    void resume(ClientIdType clientId, JobIdType jobId, const TranscodingRequestParcel& /*request*/,
+                const std::shared_ptr<ITranscodingClientCallback>& /*clientCallback*/) override {
         mEventQueue.push_back(Resume(clientId, jobId));
     }
     void stop(ClientIdType clientId, JobIdType jobId) override {
diff --git a/media/libmediatranscoding/tests/assets/longtest_15s.mp4 b/media/libmediatranscoding/tests/assets/longtest_15s.mp4
new file mode 100644
index 0000000..b50d8e4
--- /dev/null
+++ b/media/libmediatranscoding/tests/assets/longtest_15s.mp4
Binary files differ
diff --git a/media/libmediatranscoding/transcoder/Android.bp b/media/libmediatranscoding/transcoder/Android.bp
index 59cd96d..c153a42 100644
--- a/media/libmediatranscoding/transcoder/Android.bp
+++ b/media/libmediatranscoding/transcoder/Android.bp
@@ -34,6 +34,8 @@
         "libmediandk",
         "libnativewindow",
         "libutils",
+        // TODO: Use libbinder_ndk
+        "libbinder",
     ],
 
     export_include_dirs: [
diff --git a/media/libmediatranscoding/transcoder/MediaSampleWriter.cpp b/media/libmediatranscoding/transcoder/MediaSampleWriter.cpp
index 0fdb0b7..91dbf78 100644
--- a/media/libmediatranscoding/transcoder/MediaSampleWriter.cpp
+++ b/media/libmediatranscoding/transcoder/MediaSampleWriter.cpp
@@ -122,7 +122,12 @@
         return false;
     }
 
-    mTracks.emplace_back(sampleQueue, static_cast<size_t>(trackIndex));
+    int64_t durationUs;
+    if (!AMediaFormat_getInt64(trackFormat.get(), AMEDIAFORMAT_KEY_DURATION, &durationUs)) {
+        durationUs = 0;
+    }
+
+    mTracks.emplace_back(sampleQueue, static_cast<size_t>(trackIndex), durationUs);
     return true;
 }
 
@@ -200,10 +205,23 @@
                 } else if (sample->info.flags & SAMPLE_FLAG_END_OF_STREAM) {
                     // Track reached end of stream.
                     track.mReachedEos = true;
-                    break;
+
+                    // Preserve source track duration by setting the appropriate timestamp on the
+                    // empty End-Of-Stream sample.
+                    if (track.mDurationUs > 0 && track.mFirstSampleTimeSet) {
+                        sample->info.presentationTimeUs =
+                                track.mDurationUs + track.mFirstSampleTimeUs;
+                    }
+                } else {
+                    samplesLeft = true;
                 }
 
-                samplesLeft = true;
+                // Record the first sample's timestamp in order to translate duration to EOS time
+                // for tracks that does not start at 0.
+                if (!track.mFirstSampleTimeSet) {
+                    track.mFirstSampleTimeUs = sample->info.presentationTimeUs;
+                    track.mFirstSampleTimeSet = true;
+                }
 
                 bufferInfo.offset = sample->dataOffset;
                 bufferInfo.size = sample->info.size;
@@ -217,7 +235,7 @@
                     return status;
                 }
 
-            } while (sample->info.presentationTimeUs < segmentEndTimeUs);
+            } while (sample->info.presentationTimeUs < segmentEndTimeUs && !track.mReachedEos);
         }
 
         segmentEndTimeUs += mTrackSegmentLengthUs;
diff --git a/media/libmediatranscoding/transcoder/MediaTranscoder.cpp b/media/libmediatranscoding/transcoder/MediaTranscoder.cpp
index 69932f4..bde1cf6 100644
--- a/media/libmediatranscoding/transcoder/MediaTranscoder.cpp
+++ b/media/libmediatranscoding/transcoder/MediaTranscoder.cpp
@@ -18,6 +18,7 @@
 #define LOG_TAG "MediaTranscoder"
 
 #include <android-base/logging.h>
+#include <binder/Parcel.h>
 #include <fcntl.h>
 #include <media/MediaSampleReaderNDK.h>
 #include <media/MediaSampleWriter.h>
@@ -84,6 +85,17 @@
 }
 
 void MediaTranscoder::sendCallback(media_status_t status) {
+    // If the transcoder is already cancelled explicitly, don't send any error callbacks.
+    // Tracks and sample writer will report errors for abort. However, currently we can't
+    // tell it apart from real errors. Ideally we still want to report real errors back
+    // to client, as there is a small chance that explicit abort and the real error come
+    // at around the same time, we should report that if abort has a specific error code.
+    // On the other hand, if the transcoder actually finished (status is AMEDIA_OK) at around
+    // the same time of the abort, we should still report the finish back to the client.
+    if (mCancelled && status != AMEDIA_OK) {
+        return;
+    }
+
     bool expected = false;
     if (mCallbackSent.compare_exchange_strong(expected, true)) {
         if (status == AMEDIA_OK) {
@@ -149,11 +161,11 @@
 
 std::shared_ptr<MediaTranscoder> MediaTranscoder::create(
         const std::shared_ptr<CallbackInterface>& callbacks,
-        const std::shared_ptr<Parcel>& pausedState) {
+        const std::shared_ptr<const Parcel>& pausedState) {
     if (pausedState != nullptr) {
-        LOG(ERROR) << "Initializing from paused state is currently not supported.";
-        return nullptr;
-    } else if (callbacks == nullptr) {
+        LOG(INFO) << "Initializing from paused state.";
+    }
+    if (callbacks == nullptr) {
         LOG(ERROR) << "Callbacks cannot be null";
         return nullptr;
     }
@@ -309,15 +321,15 @@
     return AMEDIA_OK;
 }
 
-media_status_t MediaTranscoder::pause(std::shared_ptr<const Parcelable>* pausedState) {
-    (void)pausedState;
-    LOG(ERROR) << "Pause is not currently supported";
-    return AMEDIA_ERROR_UNSUPPORTED;
+media_status_t MediaTranscoder::pause(std::shared_ptr<const Parcel>* pausedState) {
+    // TODO: write internal states to parcel.
+    *pausedState = std::make_shared<Parcel>();
+    return cancel();
 }
 
 media_status_t MediaTranscoder::resume() {
-    LOG(ERROR) << "Resume is not currently supported";
-    return AMEDIA_ERROR_UNSUPPORTED;
+    // TODO: restore internal states from parcel.
+    return start();
 }
 
 media_status_t MediaTranscoder::cancel() {
diff --git a/media/libmediatranscoding/transcoder/VideoTrackTranscoder.cpp b/media/libmediatranscoding/transcoder/VideoTrackTranscoder.cpp
index 4df3296..8ee252f 100644
--- a/media/libmediatranscoding/transcoder/VideoTrackTranscoder.cpp
+++ b/media/libmediatranscoding/transcoder/VideoTrackTranscoder.cpp
@@ -372,6 +372,14 @@
         AMediaFormat_setInt32(formatCopy, AMEDIAFORMAT_KEY_ROTATION, rotation);
     }
 
+    // Transfer track duration.
+    // Preserve the source track duration by sending it to MediaSampleWriter.
+    int64_t durationUs;
+    if (AMediaFormat_getInt64(mSourceFormat.get(), AMEDIAFORMAT_KEY_DURATION, &durationUs) &&
+        durationUs > 0) {
+        AMediaFormat_setInt64(formatCopy, AMEDIAFORMAT_KEY_DURATION, durationUs);
+    }
+
     // TODO: transfer other fields as required.
 
     mActualOutputFormat = std::shared_ptr<AMediaFormat>(formatCopy, &AMediaFormat_delete);
@@ -380,20 +388,24 @@
 }
 
 media_status_t VideoTrackTranscoder::runTranscodeLoop() {
-    media_status_t status = AMEDIA_OK;
+    // Push start decoder and encoder as two messages, so that these are subject to the
+    // stop request as well. If the job is cancelled (or paused) immediately after start,
+    // we don't need to waste time start then stop the codecs.
+    mCodecMessageQueue.push([this] {
+        media_status_t status = AMediaCodec_start(mDecoder);
+        if (status != AMEDIA_OK) {
+            LOG(ERROR) << "Unable to start video decoder: " << status;
+            mStatus = status;
+        }
+    });
 
-    status = AMediaCodec_start(mDecoder);
-    if (status != AMEDIA_OK) {
-        LOG(ERROR) << "Unable to start video decoder: " << status;
-        return status;
-    }
-
-    status = AMediaCodec_start(mEncoder.get());
-    if (status != AMEDIA_OK) {
-        LOG(ERROR) << "Unable to start video encoder: " << status;
-        AMediaCodec_stop(mDecoder);
-        return status;
-    }
+    mCodecMessageQueue.push([this] {
+        media_status_t status = AMediaCodec_start(mEncoder.get());
+        if (status != AMEDIA_OK) {
+            LOG(ERROR) << "Unable to start video encoder: " << status;
+            mStatus = status;
+        }
+    });
 
     // Process codec events until EOS is reached, transcoding is stopped or an error occurs.
     while (!mStopRequested && !mEosFromEncoder && mStatus == AMEDIA_OK) {
diff --git a/media/libmediatranscoding/transcoder/include/media/MediaSampleWriter.h b/media/libmediatranscoding/transcoder/include/media/MediaSampleWriter.h
index da83167..d971f3e 100644
--- a/media/libmediatranscoding/transcoder/include/media/MediaSampleWriter.h
+++ b/media/libmediatranscoding/transcoder/include/media/MediaSampleWriter.h
@@ -144,11 +144,20 @@
     media_status_t runWriterLoop();
 
     struct TrackRecord {
-        TrackRecord(const std::shared_ptr<MediaSampleQueue>& sampleQueue, size_t trackIndex)
-              : mSampleQueue(sampleQueue), mTrackIndex(trackIndex), mReachedEos(false) {}
+        TrackRecord(const std::shared_ptr<MediaSampleQueue>& sampleQueue, size_t trackIndex,
+                    int64_t durationUs)
+              : mSampleQueue(sampleQueue),
+                mTrackIndex(trackIndex),
+                mDurationUs(durationUs),
+                mFirstSampleTimeUs(0),
+                mFirstSampleTimeSet(false),
+                mReachedEos(false) {}
 
         std::shared_ptr<MediaSampleQueue> mSampleQueue;
         const size_t mTrackIndex;
+        int64_t mDurationUs;
+        int64_t mFirstSampleTimeUs;
+        bool mFirstSampleTimeSet;
         bool mReachedEos;
     };
 
diff --git a/media/libmediatranscoding/transcoder/include/media/MediaTranscoder.h b/media/libmediatranscoding/transcoder/include/media/MediaTranscoder.h
index e5a5d64..7a36c8c 100644
--- a/media/libmediatranscoding/transcoder/include/media/MediaTranscoder.h
+++ b/media/libmediatranscoding/transcoder/include/media/MediaTranscoder.h
@@ -17,8 +17,6 @@
 #ifndef ANDROID_MEDIA_TRANSCODER_H
 #define ANDROID_MEDIA_TRANSCODER_H
 
-#include <binder/Parcel.h>
-#include <binder/Parcelable.h>
 #include <media/MediaTrackTranscoderCallback.h>
 #include <media/NdkMediaError.h>
 #include <media/NdkMediaFormat.h>
@@ -33,6 +31,7 @@
 
 class MediaSampleReader;
 class MediaSampleWriter;
+class Parcel;
 
 class MediaTranscoder : public std::enable_shared_from_this<MediaTranscoder>,
                         public MediaTrackTranscoderCallback {
@@ -57,7 +56,7 @@
          *      resume.
          */
         virtual void onCodecResourceLost(const MediaTranscoder* transcoder,
-                                         const std::shared_ptr<const Parcelable>& pausedState) = 0;
+                                         const std::shared_ptr<const Parcel>& pausedState) = 0;
 
         virtual ~CallbackInterface() = default;
     };
@@ -69,7 +68,7 @@
      */
     static std::shared_ptr<MediaTranscoder> create(
             const std::shared_ptr<CallbackInterface>& callbacks,
-            const std::shared_ptr<Parcel>& pausedState = nullptr);
+            const std::shared_ptr<const Parcel>& pausedState = nullptr);
 
     /** Configures source from path fd. */
     media_status_t configureSource(int fd);
@@ -102,8 +101,12 @@
      * release the transcoder instance, clear the paused state and delete the partial destination
      * file. The caller can optionally call cancel to let the transcoder clean up the partial
      * destination file.
+     *
+     * TODO: use NDK AParcel instead
+     * libbinder shouldn't be used by mainline modules. When transcoding goes mainline
+     * it needs to be replaced by stable AParcel.
      */
-    media_status_t pause(std::shared_ptr<const Parcelable>* pausedState);
+    media_status_t pause(std::shared_ptr<const Parcel>* pausedState);
 
     /** Resumes a paused transcoding. */
     media_status_t resume();
diff --git a/media/libmediatranscoding/transcoder/tests/MediaSampleWriterTests.cpp b/media/libmediatranscoding/transcoder/tests/MediaSampleWriterTests.cpp
index 98813e7..e3cb192 100644
--- a/media/libmediatranscoding/transcoder/tests/MediaSampleWriterTests.cpp
+++ b/media/libmediatranscoding/transcoder/tests/MediaSampleWriterTests.cpp
@@ -374,6 +374,16 @@
         EXPECT_EQ(event.info.flags, sample->info.flags);
     }
 
+    // Verify EOS samples.
+    for (int trackIndex = 0; trackIndex < kNumTracks; ++trackIndex) {
+        auto trackFormat = mediaSource.mTrackFormats[trackIndex % mediaSource.mTrackCount];
+        int64_t duration = 0;
+        AMediaFormat_getInt64(trackFormat.get(), AMEDIAFORMAT_KEY_DURATION, &duration);
+
+        const AMediaCodecBufferInfo info = {0, 0, duration, AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM};
+        EXPECT_EQ(mTestMuxer->popEvent(), TestMuxer::WriteSample(trackIndex, nullptr, &info));
+    }
+
     EXPECT_EQ(mTestMuxer->popEvent(), TestMuxer::Stop());
     EXPECT_TRUE(writer.stop());
 }
diff --git a/media/libmediatranscoding/transcoder/tests/MediaTranscoderTests.cpp b/media/libmediatranscoding/transcoder/tests/MediaTranscoderTests.cpp
index 67f1ca1..e68eaac 100644
--- a/media/libmediatranscoding/transcoder/tests/MediaTranscoderTests.cpp
+++ b/media/libmediatranscoding/transcoder/tests/MediaTranscoderTests.cpp
@@ -20,6 +20,7 @@
 #define LOG_TAG "MediaTranscoderTests"
 
 #include <android-base/logging.h>
+#include <fcntl.h>
 #include <gtest/gtest.h>
 #include <media/MediaSampleReaderNDK.h>
 #include <media/MediaTranscoder.h>
@@ -74,7 +75,7 @@
                                   int32_t progress __unused) override {}
 
     virtual void onCodecResourceLost(const MediaTranscoder* transcoder __unused,
-                                     const std::shared_ptr<const Parcelable>& pausedState
+                                     const std::shared_ptr<const Parcel>& pausedState
                                              __unused) override {}
 
     void waitForTranscodingFinished() {
@@ -203,6 +204,7 @@
 
         std::shared_ptr<MediaSampleReader> sampleReader =
                 MediaSampleReaderNDK::createFromFd(dstFd, 0, fileSize);
+        ASSERT_NE(sampleReader, nullptr);
 
         std::shared_ptr<AMediaFormat> videoFormat;
         const size_t trackCount = sampleReader->getTrackCount();
@@ -211,6 +213,7 @@
             if (trackFormat != nullptr) {
                 const char* mime = nullptr;
                 AMediaFormat_getString(trackFormat, AMEDIAFORMAT_KEY_MIME, &mime);
+
                 if (strncmp(mime, "video/", 6) == 0) {
                     LOG(INFO) << "Track # " << trackIndex << ": "
                               << AMediaFormat_toString(trackFormat);
diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp
index 4da85a2..7c2a5ff 100644
--- a/media/libstagefright/MPEG4Writer.cpp
+++ b/media/libstagefright/MPEG4Writer.cpp
@@ -1116,6 +1116,21 @@
     writeInt32(0x40000000);  // w
 }
 
+void MPEG4Writer::printWriteDurations() {
+    if (mWriteDurationPQ.empty()) {
+        return;
+    }
+    std::string writeDurationsString =
+            "Top " + std::to_string(mWriteDurationPQ.size()) + " write durations(microseconds):";
+    uint8_t i = 0;
+    while (!mWriteDurationPQ.empty()) {
+        writeDurationsString +=
+                " #" + std::to_string(++i) + ":" + std::to_string(mWriteDurationPQ.top().count());
+        mWriteDurationPQ.pop();
+    }
+    ALOGD("%s", writeDurationsString.c_str());
+}
+
 status_t MPEG4Writer::release() {
     ALOGD("release()");
     status_t err = OK;
@@ -1144,6 +1159,9 @@
     mStarted = false;
     free(mInMemoryCache);
     mInMemoryCache = NULL;
+
+    printWriteDurations();
+
     return err;
 }
 
@@ -1628,7 +1646,17 @@
 void MPEG4Writer::writeOrPostError(int fd, const void* buf, size_t count) {
     if (mWriteSeekErr == true)
         return;
+
+    auto beforeTP = std::chrono::high_resolution_clock::now();
     ssize_t bytesWritten = ::write(fd, buf, count);
+    auto afterTP = std::chrono::high_resolution_clock::now();
+    auto writeDuration =
+            std::chrono::duration_cast<std::chrono::microseconds>(afterTP - beforeTP).count();
+    mWriteDurationPQ.emplace(writeDuration);
+    if (mWriteDurationPQ.size() > kWriteDurationsCount) {
+        mWriteDurationPQ.pop();
+    }
+
     /* Write as much as possible during stop() execution when there was an error
      * (mWriteSeekErr == true) in the previous call to write() or lseek64().
      */
diff --git a/media/libstagefright/include/media/stagefright/MPEG4Writer.h b/media/libstagefright/include/media/stagefright/MPEG4Writer.h
index 40c4bfc..2582ed0 100644
--- a/media/libstagefright/include/media/stagefright/MPEG4Writer.h
+++ b/media/libstagefright/include/media/stagefright/MPEG4Writer.h
@@ -27,6 +27,7 @@
 #include <media/stagefright/foundation/AHandlerReflector.h>
 #include <media/stagefright/foundation/ALooper.h>
 #include <mutex>
+#include <queue>
 
 namespace android {
 
@@ -126,6 +127,10 @@
     bool mFallocateErr;
     bool mPreAllocationEnabled;
     status_t mResetStatus;
+    // Queue to hold top long write durations
+    std::priority_queue<std::chrono::microseconds, std::vector<std::chrono::microseconds>,
+                        std::greater<std::chrono::microseconds>> mWriteDurationPQ;
+    const uint8_t kWriteDurationsCount = 5;
 
     sp<ALooper> mLooper;
     sp<AHandlerReflector<MPEG4Writer> > mReflector;
@@ -152,6 +157,7 @@
     int64_t estimateMoovBoxSize(int32_t bitRate);
     int64_t estimateFileLevelMetaSize(MetaData *params);
     void writeCachedBoxToFile(const char *type);
+    void printWriteDurations();
 
     struct Chunk {
         Track               *mTrack;        // Owner
diff --git a/media/mediaserver/Android.bp b/media/mediaserver/Android.bp
index a968890..afca7c4 100644
--- a/media/mediaserver/Android.bp
+++ b/media/mediaserver/Android.bp
@@ -15,13 +15,14 @@
     srcs: ["main_mediaserver.cpp"],
 
     shared_libs: [
-        "libresourcemanagerservice",
+        "android.hardware.media.omx@1.0",
+        "libandroidicu",
+        "libbinder",
+        "libhidlbase",
         "liblog",
         "libmediaplayerservice",
+        "libresourcemanagerservice",
         "libutils",
-        "libbinder",
-        "libandroidicu",
-        "android.hardware.media.omx@1.0",
     ],
 
     static_libs: [
diff --git a/media/mediaserver/main_mediaserver.cpp b/media/mediaserver/main_mediaserver.cpp
index 7b22b05..316732b 100644
--- a/media/mediaserver/main_mediaserver.cpp
+++ b/media/mediaserver/main_mediaserver.cpp
@@ -22,6 +22,7 @@
 #include <binder/IPCThreadState.h>
 #include <binder/ProcessState.h>
 #include <binder/IServiceManager.h>
+#include <hidl/HidlTransportSupport.h>
 #include <utils/Log.h>
 #include "RegisterExtensions.h"
 
@@ -42,6 +43,8 @@
     MediaPlayerService::instantiate();
     ResourceManagerService::instantiate();
     registerExtensions();
+    ::android::hardware::configureRpcThreadpool(16, false);
     ProcessState::self()->startThreadPool();
     IPCThreadState::self()->joinThreadPool();
+    ::android::hardware::joinRpcThreadpool();
 }
diff --git a/media/utils/fuzzers/Android.bp b/media/utils/fuzzers/Android.bp
new file mode 100644
index 0000000..ca1123c
--- /dev/null
+++ b/media/utils/fuzzers/Android.bp
@@ -0,0 +1,51 @@
+cc_defaults {
+    name: "libmediautils_fuzzer_defaults",
+    shared_libs: [
+        "libbinder",
+        "libcutils",
+        "liblog",
+        "libmediautils",
+        "libutils",
+    ],
+
+    cflags: [
+        "-Wall",
+        "-Wextra",
+        "-Werror",
+        "-Wno-c++2a-extensions",
+    ],
+
+    header_libs: [
+        "bionic_libc_platform_headers",
+        "libmedia_headers",
+    ],
+
+    include_dirs: [
+        // For DEBUGGER_SIGNAL
+        "system/core/debuggerd/include",
+    ],
+}
+
+cc_fuzz {
+    name: "libmediautils_fuzzer_battery_notifier",
+    defaults: ["libmediautils_fuzzer_defaults"],
+    srcs: ["BatteryNotifierFuzz.cpp"],
+}
+
+cc_fuzz {
+    name: "libmediautils_fuzzer_scheduling_policy_service",
+    defaults: ["libmediautils_fuzzer_defaults"],
+    srcs: ["SchedulingPolicyServiceFuzz.cpp"],
+}
+
+cc_fuzz {
+    name: "libmediautils_fuzzer_service_utilities",
+    defaults: ["libmediautils_fuzzer_defaults"],
+    srcs: ["ServiceUtilitiesFuzz.cpp"],
+}
+
+cc_fuzz {
+    name: "libmediautils_fuzzer_time_check",
+    defaults: ["libmediautils_fuzzer_defaults"],
+    srcs: ["TimeCheckFuzz.cpp"],
+}
diff --git a/media/utils/fuzzers/BatteryNotifierFuzz.cpp b/media/utils/fuzzers/BatteryNotifierFuzz.cpp
new file mode 100644
index 0000000..00b3cce
--- /dev/null
+++ b/media/utils/fuzzers/BatteryNotifierFuzz.cpp
@@ -0,0 +1,90 @@
+/*
+ * Copyright 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.
+ */
+
+#include <functional>
+#include <string>
+#include <vector>
+
+#include <utils/String8.h>
+
+#include "fuzzer/FuzzedDataProvider.h"
+#include "mediautils/BatteryNotifier.h"
+
+static constexpr int kMaxOperations = 30;
+static constexpr int kMaxStringLength = 500;
+using android::BatteryNotifier;
+
+std::vector<std::function<void(std::string /*flashlight_name*/, std::string /*camera_name*/,
+                               uid_t /*video_id*/, uid_t /*audio_id*/, uid_t /*light_id*/,
+                               uid_t /*camera_id*/)>>
+    operations = {
+        [](std::string, std::string, uid_t, uid_t, uid_t, uid_t) -> void {
+            BatteryNotifier::getInstance().noteResetVideo();
+        },
+        [](std::string, std::string, uid_t, uid_t, uid_t, uid_t) -> void {
+            BatteryNotifier::getInstance().noteResetAudio();
+        },
+        [](std::string, std::string, uid_t, uid_t, uid_t, uid_t) -> void {
+            BatteryNotifier::getInstance().noteResetFlashlight();
+        },
+        [](std::string, std::string, uid_t, uid_t, uid_t, uid_t) -> void {
+            BatteryNotifier::getInstance().noteResetCamera();
+        },
+        [](std::string, std::string, uid_t video_id, uid_t, uid_t, uid_t) -> void {
+            BatteryNotifier::getInstance().noteStartVideo(video_id);
+        },
+        [](std::string, std::string, uid_t video_id, uid_t, uid_t, uid_t) -> void {
+            BatteryNotifier::getInstance().noteStopVideo(video_id);
+        },
+        [](std::string, std::string, uid_t, uid_t audio_id, uid_t, uid_t) -> void {
+            BatteryNotifier::getInstance().noteStartAudio(audio_id);
+        },
+        [](std::string, std::string, uid_t, uid_t audio_id, uid_t, uid_t) -> void {
+            BatteryNotifier::getInstance().noteStopAudio(audio_id);
+        },
+        [](std::string flashlight_name, std::string, uid_t, uid_t, uid_t light_id, uid_t) -> void {
+            android::String8 name(flashlight_name.c_str());
+            BatteryNotifier::getInstance().noteFlashlightOn(name, light_id);
+        },
+        [](std::string flashlight_name, std::string, uid_t, uid_t, uid_t light_id, uid_t) -> void {
+            android::String8 name(flashlight_name.c_str());
+            BatteryNotifier::getInstance().noteFlashlightOff(name, light_id);
+        },
+        [](std::string, std::string camera_name, uid_t, uid_t, uid_t, uid_t camera_id) -> void {
+            android::String8 name(camera_name.c_str());
+            BatteryNotifier::getInstance().noteStartCamera(name, camera_id);
+        },
+        [](std::string, std::string camera_name, uid_t, uid_t, uid_t, uid_t camera_id) -> void {
+            android::String8 name(camera_name.c_str());
+            BatteryNotifier::getInstance().noteStopCamera(name, camera_id);
+        },
+};
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+    FuzzedDataProvider data_provider(data, size);
+    std::string camera_name = data_provider.ConsumeRandomLengthString(kMaxStringLength);
+    std::string flashlight_name = data_provider.ConsumeRandomLengthString(kMaxStringLength);
+    uid_t video_id = data_provider.ConsumeIntegral<uid_t>();
+    uid_t audio_id = data_provider.ConsumeIntegral<uid_t>();
+    uid_t light_id = data_provider.ConsumeIntegral<uid_t>();
+    uid_t camera_id = data_provider.ConsumeIntegral<uid_t>();
+    size_t ops_run = 0;
+    while (data_provider.remaining_bytes() > 0 && ops_run++ < kMaxOperations) {
+        uint8_t op = data_provider.ConsumeIntegralInRange<uint8_t>(0, operations.size() - 1);
+        operations[op](flashlight_name, camera_name, video_id, audio_id, light_id, camera_id);
+    }
+    return 0;
+}
diff --git a/media/utils/fuzzers/SchedulingPolicyServiceFuzz.cpp b/media/utils/fuzzers/SchedulingPolicyServiceFuzz.cpp
new file mode 100644
index 0000000..4521853
--- /dev/null
+++ b/media/utils/fuzzers/SchedulingPolicyServiceFuzz.cpp
@@ -0,0 +1,61 @@
+/*
+ * Copyright 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_TAG "BatteryNotifierFuzzer"
+#include <binder/IBatteryStats.h>
+#include <binder/IServiceManager.h>
+#include <utils/String16.h>
+#include <android/log.h>
+#include <mediautils/SchedulingPolicyService.h>
+#include "fuzzer/FuzzedDataProvider.h"
+using android::IBatteryStats;
+using android::IBinder;
+using android::IInterface;
+using android::IServiceManager;
+using android::sp;
+using android::String16;
+using android::defaultServiceManager;
+using android::requestCpusetBoost;
+using android::requestPriority;
+sp<IBatteryStats> getBatteryService() {
+    sp<IBatteryStats> batteryStatService;
+    const sp<IServiceManager> sm(defaultServiceManager());
+    if (sm != nullptr) {
+        const String16 name("batterystats");
+        batteryStatService = checked_interface_cast<IBatteryStats>(sm->checkService(name));
+        if (batteryStatService == nullptr) {
+            ALOGW("batterystats service unavailable!");
+            return nullptr;
+        }
+    }
+    return batteryStatService;
+}
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+    FuzzedDataProvider data_provider(data, size);
+    sp<IBatteryStats> batteryStatService = getBatteryService();
+    // There is some state here, but it's mostly focused around thread-safety, so
+    // we won't worry about order.
+    int32_t priority = data_provider.ConsumeIntegral<int32_t>();
+    bool is_for_app = data_provider.ConsumeBool();
+    bool async = data_provider.ConsumeBool();
+    requestPriority(getpid(), gettid(), priority, is_for_app, async);
+    // TODO: Verify and re-enable in AOSP (R).
+    // bool enable = data_provider.ConsumeBool();
+    // We are just using batterystats to avoid the need
+    // to register a new service.
+    // requestCpusetBoost(enable, IInterface::asBinder(batteryStatService));
+    return 0;
+}
+
diff --git a/media/utils/fuzzers/ServiceUtilitiesFuzz.cpp b/media/utils/fuzzers/ServiceUtilitiesFuzz.cpp
new file mode 100644
index 0000000..3d141b5
--- /dev/null
+++ b/media/utils/fuzzers/ServiceUtilitiesFuzz.cpp
@@ -0,0 +1,78 @@
+/*
+ * Copyright 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.
+ */
+
+#include <fcntl.h>
+
+#include <functional>
+
+#include "fuzzer/FuzzedDataProvider.h"
+#include "mediautils/ServiceUtilities.h"
+
+static constexpr int kMaxOperations = 50;
+static constexpr int kMaxStringLen = 256;
+
+const std::vector<std::function<void(FuzzedDataProvider*, android::MediaPackageManager)>>
+    operations = {
+        [](FuzzedDataProvider* data_provider, android::MediaPackageManager pm) -> void {
+            uid_t uid = data_provider->ConsumeIntegral<uid_t>();
+            pm.allowPlaybackCapture(uid);
+        },
+        [](FuzzedDataProvider* data_provider, android::MediaPackageManager pm) -> void {
+            int spaces = data_provider->ConsumeIntegral<int>();
+
+            // Dump everything into /dev/null
+            int fd = open("/dev/null", O_WRONLY);
+            pm.dump(fd, spaces);
+            close(fd);
+        },
+};
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+    FuzzedDataProvider data_provider(data, size);
+    uid_t uid = data_provider.ConsumeIntegral<uid_t>();
+    pid_t pid = data_provider.ConsumeIntegral<pid_t>();
+
+    // There is not state here, and order is not significant,
+    // so we can simply call all of the target functions
+    android::isServiceUid(uid);
+    android::isAudioServerUid(uid);
+    android::isAudioServerOrSystemServerUid(uid);
+    android::isAudioServerOrMediaServerUid(uid);
+    std::string packageNameStr = data_provider.ConsumeRandomLengthString(kMaxStringLen);
+    android::String16 opPackageName(packageNameStr.c_str());
+    android::recordingAllowed(opPackageName, pid, uid);
+    android::startRecording(opPackageName, pid, uid);
+    android::finishRecording(opPackageName, uid);
+    android::captureAudioOutputAllowed(pid, uid);
+    android::captureMediaOutputAllowed(pid, uid);
+    android::captureHotwordAllowed(opPackageName, pid, uid);
+    android::modifyPhoneStateAllowed(uid, pid);
+    android::bypassInterruptionPolicyAllowed(uid, pid);
+    android::settingsAllowed();
+    android::modifyAudioRoutingAllowed();
+    android::modifyDefaultAudioEffectsAllowed();
+    android::dumpAllowed();
+
+    // MediaPackageManager does have state, so we need the fuzzer to decide order
+    android::MediaPackageManager packageManager;
+    size_t ops_run = 0;
+    while (data_provider.remaining_bytes() > 0 && ops_run++ < kMaxOperations) {
+        uint8_t op = data_provider.ConsumeIntegralInRange<uint8_t>(0, operations.size() - 1);
+        operations[op](&data_provider, packageManager);
+    }
+
+    return 0;
+}
diff --git a/media/utils/fuzzers/TimeCheckFuzz.cpp b/media/utils/fuzzers/TimeCheckFuzz.cpp
new file mode 100644
index 0000000..eeb6ba6
--- /dev/null
+++ b/media/utils/fuzzers/TimeCheckFuzz.cpp
@@ -0,0 +1,64 @@
+/*
+ * Copyright 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.
+ */
+#include <chrono>
+#include <thread>
+
+#include "fuzzer/FuzzedDataProvider.h"
+#include "mediautils/TimeCheck.h"
+
+static constexpr int kMaxStringLen = 256;
+
+// While it might be interesting to test long-running
+// jobs, it seems unlikely it'd lead to the types of crashes
+// we're looking for, and would mean a significant increase in fuzzer time.
+// Therefore, we are setting a low cap.
+static constexpr uint32_t kMaxTimeoutMs = 1000;
+static constexpr uint32_t kMinTimeoutMs = 200;
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+    FuzzedDataProvider data_provider(data, size);
+
+    // There's essentially 5 operations that we can access in this class
+    // 1. The time it takes to run this operation. As mentioned above,
+    //    long-running tasks are not good for fuzzing, but there will be
+    //    some change in the run time.
+    uint32_t timeoutMs =
+        data_provider.ConsumeIntegralInRange<uint32_t>(kMinTimeoutMs, kMaxTimeoutMs);
+    uint8_t pid_size = data_provider.ConsumeIntegral<uint8_t>();
+    std::vector<pid_t> pids(pid_size);
+    for (auto& pid : pids) {
+        pid = data_provider.ConsumeIntegral<pid_t>();
+    }
+
+    // 2. We also have setAudioHalPids, which is populated with the pids set
+    // above.
+    android::TimeCheck::setAudioHalPids(pids);
+    std::string name = data_provider.ConsumeRandomLengthString(kMaxStringLen);
+
+    // 3. The constructor, which is fuzzed here:
+    android::TimeCheck timeCheck(name.c_str(), timeoutMs);
+    // We will leave some buffer to avoid sleeping too long
+    uint8_t sleep_amount_ms = data_provider.ConsumeIntegralInRange<uint8_t>(0, timeoutMs / 2);
+
+    // We want to make sure we can cover the time out functionality.
+    if (sleep_amount_ms) {
+        auto ms = std::chrono::milliseconds(sleep_amount_ms);
+        std::this_thread::sleep_for(ms);
+    }
+
+    // 4. Finally, the destructor on timecheck. These seem to be the only factors
+    // in play.
+    return 0;
+}
diff --git a/services/audioflinger/FastMixer.cpp b/services/audioflinger/FastMixer.cpp
index 3eacc8c..bf2e953 100644
--- a/services/audioflinger/FastMixer.cpp
+++ b/services/audioflinger/FastMixer.cpp
@@ -40,6 +40,7 @@
 #include <audio_utils/channels.h>
 #include <audio_utils/format.h>
 #include <audio_utils/mono_blend.h>
+#include <cutils/bitops.h>
 #include <media/AudioMixer.h>
 #include "FastMixer.h"
 #include "TypedLogger.h"
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index b433cf9..c252d77 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -29,6 +29,7 @@
 #include <linux/futex.h>
 #include <sys/stat.h>
 #include <sys/syscall.h>
+#include <cutils/bitops.h>
 #include <cutils/properties.h>
 #include <media/AudioContainers.h>
 #include <media/AudioDeviceTypeAddr.h>
@@ -2091,12 +2092,6 @@
         outputFlags = (audio_output_flags_t)(outputFlags | AUDIO_OUTPUT_FLAG_FAST);
     }
 
-    // Set DIRECT flag if current thread is DirectOutputThread. This can happen when the playback is
-    // rerouted to direct output thread by dynamic audio policy.
-    if (mType == DIRECT) {
-        *flags = (audio_output_flags_t)(*flags | AUDIO_OUTPUT_FLAG_DIRECT);
-    }
-
     // Check if requested flags are compatible with output stream flags
     if ((*flags & outputFlags) != *flags) {
         ALOGW("createTrack_l(): mismatch between requested flags (%08x) and output flags (%08x)",
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index 2661c30..1fe340a 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -42,6 +42,7 @@
 #include <set>
 #include <unordered_set>
 #include <vector>
+#include <cutils/bitops.h>
 #include <cutils/properties.h>
 #include <utils/Log.h>
 #include <media/AudioParameter.h>
diff --git a/services/mediatranscoding/SimulatedTranscoder.cpp b/services/mediatranscoding/SimulatedTranscoder.cpp
index 5aa325f..97d5f5f 100644
--- a/services/mediatranscoding/SimulatedTranscoder.cpp
+++ b/services/mediatranscoding/SimulatedTranscoder.cpp
@@ -72,7 +72,9 @@
     });
 }
 
-void SimulatedTranscoder::resume(ClientIdType clientId, JobIdType jobId) {
+void SimulatedTranscoder::resume(
+        ClientIdType clientId, JobIdType jobId, const TranscodingRequestParcel& /*request*/,
+        const std::shared_ptr<ITranscodingClientCallback>& /*clientCallback*/) {
     queueEvent(Event::Resume, clientId, jobId, [=] {
         auto callback = mCallback.lock();
         if (callback != nullptr) {
diff --git a/services/mediatranscoding/SimulatedTranscoder.h b/services/mediatranscoding/SimulatedTranscoder.h
index 030222b..1c359dd 100644
--- a/services/mediatranscoding/SimulatedTranscoder.h
+++ b/services/mediatranscoding/SimulatedTranscoder.h
@@ -54,7 +54,8 @@
     void start(ClientIdType clientId, JobIdType jobId, const TranscodingRequestParcel& request,
                const std::shared_ptr<ITranscodingClientCallback>& clientCallback) override;
     void pause(ClientIdType clientId, JobIdType jobId) override;
-    void resume(ClientIdType clientId, JobIdType jobId) override;
+    void resume(ClientIdType clientId, JobIdType jobId, const TranscodingRequestParcel& request,
+                const std::shared_ptr<ITranscodingClientCallback>& clientCallback) override;
     void stop(ClientIdType clientId, JobIdType jobId) override;
     // ~TranscoderInterface
 
diff --git a/services/mediatranscoding/tests/MediaTranscodingServiceTestHelper.h b/services/mediatranscoding/tests/MediaTranscodingServiceTestHelper.h
index 4259f54..2f4e74b 100644
--- a/services/mediatranscoding/tests/MediaTranscodingServiceTestHelper.h
+++ b/services/mediatranscoding/tests/MediaTranscodingServiceTestHelper.h
@@ -66,6 +66,8 @@
 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";
+
 static status_t getUidForPackage(String16 packageName, userid_t userId, /*inout*/ uid_t& uid) {
     PermissionController pc;
     uid = pc.getPackageUid(packageName, 0);
@@ -167,20 +169,28 @@
     }
 
     // Push 1 event to back.
-    void append(const Event& event) {
+    void append(const Event& event,
+                const TranscodingErrorCode err = TranscodingErrorCode::kNoError) {
         ALOGD("%s", toString(event).c_str());
 
         std::unique_lock lock(mLock);
 
         mEventQueue.push_back(event);
+        mLastErr = err;
         mCondition.notify_one();
     }
 
+    TranscodingErrorCode getLastError() {
+        std::unique_lock lock(mLock);
+        return mLastErr;
+    }
+
 private:
     std::mutex mLock;
     std::condition_variable mCondition;
     Event mPoppedEvent;
     std::list<Event> mEventQueue;
+    TranscodingErrorCode mLastErr;
 };
 
 // Operators for GTest macros.
@@ -205,8 +215,14 @@
         ALOGD("@@@ openFileDescriptor: %s", in_fileUri.c_str());
         int fd;
         if (in_mode == "w" || in_mode == "rw") {
-            // Write-only, create file if non-existent, don't overwrite existing file.
-            constexpr int kOpenFlags = O_WRONLY | O_CREAT | O_EXCL;
+            int kOpenFlags;
+            if (in_mode == "w") {
+                // Write-only, create file if non-existent, truncate existing file.
+                kOpenFlags = O_WRONLY | O_CREAT | O_TRUNC;
+            } else {
+                // Read-Write, create if non-existent, no truncate (service will truncate if needed)
+                kOpenFlags = O_RDWR | O_CREAT;
+            }
             // User R+W permission.
             constexpr int kFileMode = S_IRUSR | S_IWUSR;
             fd = open(in_fileUri.c_str(), kOpenFlags, kFileMode);
@@ -239,10 +255,9 @@
         return Status::ok();
     }
 
-    Status onTranscodingFailed(
-            int32_t in_jobId,
-            ::aidl::android::media::TranscodingErrorCode /* in_errorCode */) override {
-        append(Failed(mClientId, in_jobId));
+    Status onTranscodingFailed(int32_t in_jobId,
+                               ::aidl::android::media::TranscodingErrorCode in_errorCode) override {
+        append(Failed(mClientId, in_jobId), in_errorCode);
         return Status::ok();
     }
 
diff --git a/services/mediatranscoding/tests/mediatranscodingservice_real_tests.cpp b/services/mediatranscoding/tests/mediatranscodingservice_real_tests.cpp
index 8cecfed..c6368a8 100644
--- a/services/mediatranscoding/tests/mediatranscodingservice_real_tests.cpp
+++ b/services/mediatranscoding/tests/mediatranscodingservice_real_tests.cpp
@@ -35,9 +35,13 @@
 
 constexpr int64_t kPaddingUs = 200000;
 constexpr int64_t kJobWithPaddingUs = 10000000 + kPaddingUs;
+constexpr int32_t kBitRate = 8 * 1000 * 1000;  // 8Mbs
 
-constexpr const char* kSrcPath =
+constexpr const char* kShortSrcPath =
         "/data/local/tmp/TranscodingTestAssets/cubicle_avc_480x240_aac_24KHz.mp4";
+constexpr const char* kLongSrcPath = "/data/local/tmp/TranscodingTestAssets/longtest_15s.mp4";
+
+#define OUTPATH(name) "/data/local/tmp/MediaTranscodingService_" #name ".MP4"
 
 class MediaTranscodingServiceRealTest : public MediaTranscodingServiceTestBase {
 public:
@@ -46,16 +50,34 @@
     void deleteFile(const char* path) { unlink(path); }
 };
 
-TEST_F(MediaTranscodingServiceRealTest, TestTranscodePassthru) {
+TEST_F(MediaTranscodingServiceRealTest, TestInvalidSource) {
     registerMultipleClients();
 
-    const char* dstPath = "/data/local/tmp/MediaTranscodingService_Passthru.MP4";
+    const char* srcPath = "bad_file_uri";
+    const char* dstPath = OUTPATH(TestInvalidSource);
     deleteFile(dstPath);
 
     // Submit one job.
-    EXPECT_TRUE(submit(mClient1, 0, kSrcPath, dstPath));
+    EXPECT_TRUE(submit(mClient1, 0, srcPath, dstPath, TranscodingJobPriority::kNormal, kBitRate));
+
+    // Check expected error.
+    EXPECT_EQ(mClientCallback1->pop(kPaddingUs), EventTracker::Failed(CLIENT(1), 0));
+    EXPECT_EQ(mClientCallback1->getLastError(), TranscodingErrorCode::kErrorIO);
+
+    unregisterMultipleClients();
+}
+
+TEST_F(MediaTranscodingServiceRealTest, TestPassthru) {
+    registerMultipleClients();
+
+    const char* dstPath = OUTPATH(TestPassthru);
+    deleteFile(dstPath);
+
+    // Submit one job.
+    EXPECT_TRUE(submit(mClient1, 0, kShortSrcPath, dstPath));
 
     // Wait for job to finish.
+    EXPECT_EQ(mClientCallback1->pop(kPaddingUs), EventTracker::Start(CLIENT(1), 0));
     EXPECT_EQ(mClientCallback1->pop(kJobWithPaddingUs), EventTracker::Finished(CLIENT(1), 0));
 
     unregisterMultipleClients();
@@ -64,20 +86,187 @@
 TEST_F(MediaTranscodingServiceRealTest, TestTranscodeVideo) {
     registerMultipleClients();
 
-    const char* dstPath = "/data/local/tmp/MediaTranscodingService_Video.MP4";
+    const char* dstPath = OUTPATH(TestTranscodeVideo);
     deleteFile(dstPath);
 
-    const int32_t kBitRate = 8 * 1000 * 1000;  // 8Mbs
     // Submit one job.
-    EXPECT_TRUE(submit(mClient1, 0, kSrcPath, dstPath, TranscodingJobPriority::kNormal, kBitRate));
+    EXPECT_TRUE(
+            submit(mClient1, 0, kShortSrcPath, dstPath, TranscodingJobPriority::kNormal, kBitRate));
 
     // Wait for job to finish.
+    EXPECT_EQ(mClientCallback1->pop(kPaddingUs), EventTracker::Start(CLIENT(1), 0));
     EXPECT_EQ(mClientCallback1->pop(kJobWithPaddingUs), EventTracker::Finished(CLIENT(1), 0));
 
-    // TODO: verify output file format.
+    unregisterMultipleClients();
+}
+
+/*
+ * Test cancel immediately after start.
+ */
+TEST_F(MediaTranscodingServiceRealTest, TestCancelImmediately) {
+    registerMultipleClients();
+
+    const char* srcPath0 = kLongSrcPath;
+    const char* srcPath1 = kShortSrcPath;
+    const char* dstPath0 = OUTPATH(TestCancelImmediately_Job0);
+    const char* dstPath1 = OUTPATH(TestCancelImmediately_Job1);
+
+    deleteFile(dstPath0);
+    deleteFile(dstPath1);
+    // Submit one job, should start immediately.
+    EXPECT_TRUE(submit(mClient1, 0, srcPath0, dstPath0, TranscodingJobPriority::kNormal, kBitRate));
+    EXPECT_EQ(mClientCallback1->pop(kPaddingUs), EventTracker::Start(CLIENT(1), 0));
+    EXPECT_TRUE(getJob(mClient1, 0, srcPath0, dstPath0));
+
+    // Test cancel job immediately, getJob should fail after cancel.
+    EXPECT_TRUE(cancel(mClient1, 0));
+    EXPECT_TRUE(getJob<fail>(mClient1, 0, "", ""));
+
+    // Submit new job, new job should start immediately and finish.
+    EXPECT_TRUE(submit(mClient1, 1, srcPath1, dstPath1, TranscodingJobPriority::kNormal, kBitRate));
+    EXPECT_EQ(mClientCallback1->pop(kPaddingUs), EventTracker::Start(CLIENT(1), 1));
+    EXPECT_EQ(mClientCallback1->pop(kJobWithPaddingUs), EventTracker::Finished(CLIENT(1), 1));
 
     unregisterMultipleClients();
 }
 
+/*
+ * Test cancel in the middle of transcoding.
+ */
+TEST_F(MediaTranscodingServiceRealTest, TestCancelWhileRunning) {
+    registerMultipleClients();
+
+    const char* srcPath0 = kLongSrcPath;
+    const char* srcPath1 = kShortSrcPath;
+    const char* dstPath0 = OUTPATH(TestCancelWhileRunning_Job0);
+    const char* dstPath1 = OUTPATH(TestCancelWhileRunning_Job1);
+
+    deleteFile(dstPath0);
+    deleteFile(dstPath1);
+    // Submit two jobs, job 0 should start immediately, job 1 should be queued.
+    EXPECT_TRUE(submit(mClient1, 0, srcPath0, dstPath0, TranscodingJobPriority::kNormal, kBitRate));
+    EXPECT_TRUE(submit(mClient1, 1, srcPath1, dstPath1, TranscodingJobPriority::kNormal, kBitRate));
+    EXPECT_EQ(mClientCallback1->pop(kPaddingUs), EventTracker::Start(CLIENT(1), 0));
+    EXPECT_TRUE(getJob(mClient1, 0, srcPath0, dstPath0));
+    EXPECT_TRUE(getJob(mClient1, 1, srcPath1, dstPath1));
+
+    // Job 0 (longtest) shouldn't finish in 1 seconds.
+    EXPECT_EQ(mClientCallback1->pop(1000000), EventTracker::NoEvent);
+
+    // Now cancel job 0. Job 1 should start immediately and finish.
+    EXPECT_TRUE(cancel(mClient1, 0));
+    EXPECT_TRUE(getJob<fail>(mClient1, 0, "", ""));
+    EXPECT_EQ(mClientCallback1->pop(kPaddingUs), EventTracker::Start(CLIENT(1), 1));
+    EXPECT_EQ(mClientCallback1->pop(kJobWithPaddingUs), EventTracker::Finished(CLIENT(1), 1));
+
+    unregisterMultipleClients();
+}
+
+TEST_F(MediaTranscodingServiceRealTest, TestPauseResumeSingleClient) {
+    registerMultipleClients();
+
+    const char* srcPath0 = kLongSrcPath;
+    const char* srcPath1 = kShortSrcPath;
+    const char* dstPath0 = OUTPATH(TestPauseResumeSingleClient_Job0);
+    const char* dstPath1 = OUTPATH(TestPauseResumeSingleClient_Job1);
+    deleteFile(dstPath0);
+    deleteFile(dstPath1);
+
+    // Submit one offline job, should start immediately.
+    EXPECT_TRUE(submit(mClient1, 0, srcPath0, dstPath0, TranscodingJobPriority::kUnspecified,
+                       kBitRate));
+    EXPECT_EQ(mClientCallback1->pop(kPaddingUs), EventTracker::Start(CLIENT(1), 0));
+    // Test get job after starts.
+    EXPECT_TRUE(getJob(mClient1, 0, srcPath0, dstPath0));
+
+    // Submit one realtime job.
+    EXPECT_TRUE(submit(mClient1, 1, srcPath1, dstPath1, TranscodingJobPriority::kNormal, kBitRate));
+
+    // Offline job should pause.
+    EXPECT_EQ(mClientCallback1->pop(kPaddingUs), EventTracker::Pause(CLIENT(1), 0));
+    EXPECT_TRUE(getJob(mClient1, 0, srcPath0, dstPath0));
+
+    // Realtime job should start immediately, and run to finish.
+    EXPECT_EQ(mClientCallback1->pop(kPaddingUs), EventTracker::Start(CLIENT(1), 1));
+    EXPECT_EQ(mClientCallback1->pop(kJobWithPaddingUs), EventTracker::Finished(CLIENT(1), 1));
+
+    // Test get job after finish fails.
+    EXPECT_TRUE(getJob<fail>(mClient1, 1, "", ""));
+
+    // Then offline job should resume.
+    EXPECT_EQ(mClientCallback1->pop(kPaddingUs), EventTracker::Resume(CLIENT(1), 0));
+    // Test get job after resume.
+    EXPECT_TRUE(getJob(mClient1, 0, srcPath0, dstPath0));
+
+    // Offline job should finish.
+    EXPECT_EQ(mClientCallback1->pop(kJobWithPaddingUs), EventTracker::Finished(CLIENT(1), 0));
+    // Test get job after finish fails.
+    EXPECT_TRUE(getJob<fail>(mClient1, 0, "", ""));
+
+    unregisterMultipleClients();
+}
+
+/*
+ * Basic test for pause/resume with two clients, with one job each.
+ * Top app's job should preempt the other app's job.
+ */
+TEST_F(MediaTranscodingServiceRealTest, TestPauseResumeMultiClients) {
+    ALOGD("TestPauseResumeMultiClients 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();
+
+    const char* srcPath0 = kLongSrcPath;
+    const char* srcPath1 = kShortSrcPath;
+    const char* dstPath0 = OUTPATH(TestPauseResumeMultiClients_Client0);
+    const char* dstPath1 = OUTPATH(TestPauseResumeMultiClients_Client1);
+    deleteFile(dstPath0);
+    deleteFile(dstPath1);
+
+    ALOGD("Moving app A to top...");
+    EXPECT_TRUE(ShellHelper::Start(kClientPackageA, kTestActivityName));
+
+    // Submit job to Client1.
+    ALOGD("Submitting job to client1 (app A) ...");
+    EXPECT_TRUE(submit(mClient1, 0, srcPath0, dstPath0, TranscodingJobPriority::kNormal, kBitRate));
+
+    // Client1's job 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));
+
+    // Client1's job should continue to run, since Client2 (app B) doesn't have any job.
+    EXPECT_EQ(mClientCallback1->pop(1000000), EventTracker::NoEvent);
+
+    // Submit job to Client2.
+    ALOGD("Submitting job to client2 (app B) ...");
+    EXPECT_TRUE(submit(mClient2, 0, srcPath1, dstPath1, TranscodingJobPriority::kNormal, kBitRate));
+
+    // Client1's job should pause, client2's job should start.
+    EXPECT_EQ(mClientCallback1->pop(kPaddingUs), EventTracker::Pause(CLIENT(1), 0));
+    EXPECT_EQ(mClientCallback2->pop(kPaddingUs), EventTracker::Start(CLIENT(2), 0));
+
+    // Client2's job should finish, then Client1's job should resume.
+    EXPECT_EQ(mClientCallback2->pop(kJobWithPaddingUs), EventTracker::Finished(CLIENT(2), 0));
+    EXPECT_EQ(mClientCallback1->pop(kPaddingUs), EventTracker::Resume(CLIENT(1), 0));
+
+    // Client1's job should finish.
+    EXPECT_EQ(mClientCallback1->pop(kJobWithPaddingUs), EventTracker::Finished(CLIENT(1), 0));
+
+    unregisterMultipleClients();
+
+    EXPECT_TRUE(ShellHelper::Stop(kClientPackageA));
+    EXPECT_TRUE(ShellHelper::Stop(kClientPackageB));
+    EXPECT_TRUE(ShellHelper::Stop(kClientPackageC));
+
+    ALOGD("TestPauseResumeMultiClients finished.");
+}
+
 }  // namespace media
 }  // namespace android
diff --git a/services/mediatranscoding/tests/mediatranscodingservice_simulated_tests.cpp b/services/mediatranscoding/tests/mediatranscodingservice_simulated_tests.cpp
index 309e39d..42b5877 100644
--- a/services/mediatranscoding/tests/mediatranscodingservice_simulated_tests.cpp
+++ b/services/mediatranscoding/tests/mediatranscodingservice_simulated_tests.cpp
@@ -56,7 +56,6 @@
 constexpr int64_t kJobWithPaddingUs = SimulatedTranscoder::kJobDurationUs + kPaddingUs;
 
 constexpr const char* kClientOpPackageName = "TestClientPackage";
-constexpr const char* kTestActivityName = "/com.android.tests.transcoding.MainActivity";
 
 class MediaTranscodingServiceSimulatedTest : public MediaTranscodingServiceTestBase {
 public: