Merge "transcoder: wait for actual encoder output format to start writer"
diff --git a/media/codec2/components/aac/C2SoftAacDec.cpp b/media/codec2/components/aac/C2SoftAacDec.cpp
index f39620e..9884733 100644
--- a/media/codec2/components/aac/C2SoftAacDec.cpp
+++ b/media/codec2/components/aac/C2SoftAacDec.cpp
@@ -89,11 +89,18 @@
         addParameter(
                 DefineParam(mChannelCount, C2_PARAMKEY_CHANNEL_COUNT)
                 .withDefault(new C2StreamChannelCountInfo::output(0u, 1))
-                .withFields({C2F(mChannelCount, value).inRange(1, 8)})
+                .withFields({C2F(mChannelCount, value).inRange(1, MAX_CHANNEL_COUNT)})
                 .withSetter(Setter<decltype(*mChannelCount)>::StrictValueWithNoDeps)
                 .build());
 
         addParameter(
+                DefineParam(mMaxChannelCount, C2_PARAMKEY_MAX_CHANNEL_COUNT)
+                .withDefault(new C2StreamMaxChannelCountInfo::input(0u, MAX_CHANNEL_COUNT))
+                .withFields({C2F(mMaxChannelCount, value).inRange(1, MAX_CHANNEL_COUNT)})
+                .withSetter(Setter<decltype(*mMaxChannelCount)>::StrictValueWithNoDeps)
+                .build());
+
+        addParameter(
                 DefineParam(mBitrate, C2_PARAMKEY_BITRATE)
                 .withDefault(new C2StreamBitrateInfo::input(0u, 64000))
                 .withFields({C2F(mBitrate, value).inRange(8000, 960000)})
@@ -225,6 +232,7 @@
     int32_t getDrcAttenuationFactor() const { return mDrcAttenuationFactor->value * 127. + 0.5; }
     int32_t getDrcEffectType() const { return mDrcEffectType->value; }
     int32_t getDrcAlbumMode() const { return mDrcAlbumMode->value; }
+    u_int32_t getMaxChannelCount() const { return mMaxChannelCount->value; }
     int32_t getDrcOutputLoudness() const { return (mDrcOutputLoudness->value <= 0 ? -mDrcOutputLoudness->value * 4. + 0.5 : -1); }
 
 private:
@@ -241,6 +249,7 @@
     std::shared_ptr<C2StreamDrcAttenuationFactorTuning::input> mDrcAttenuationFactor;
     std::shared_ptr<C2StreamDrcEffectTypeTuning::input> mDrcEffectType;
     std::shared_ptr<C2StreamDrcAlbumModeTuning::input> mDrcAlbumMode;
+    std::shared_ptr<C2StreamMaxChannelCountInfo::input> mMaxChannelCount;
     std::shared_ptr<C2StreamDrcOutputLoudnessTuning::output> mDrcOutputLoudness;
     // TODO Add : C2StreamAacSbrModeTuning
 };
@@ -366,9 +375,10 @@
     ALOGV("AAC decoder using MPEG-D DRC album mode %d", albumMode);
     aacDecoder_SetParam(mAACDecoder, AAC_UNIDRC_ALBUM_MODE, albumMode);
 
-    // By default, the decoder creates a 5.1 channel downmix signal.
-    // For seven and eight channel input streams, enable 6.1 and 7.1 channel output
-    aacDecoder_SetParam(mAACDecoder, AAC_PCM_MAX_OUTPUT_CHANNELS, -1);
+    // AAC_PCM_MAX_OUTPUT_CHANNELS
+    u_int32_t maxChannelCount = mIntf->getMaxChannelCount();
+    ALOGV("AAC decoder using maximum output channel count %d", maxChannelCount);
+    aacDecoder_SetParam(mAACDecoder, AAC_PCM_MAX_OUTPUT_CHANNELS, maxChannelCount);
 
     return status;
 }
@@ -707,6 +717,11 @@
         ALOGV("AAC decoder using MPEG-D DRC album mode %d", albumMode);
         aacDecoder_SetParam(mAACDecoder, AAC_UNIDRC_ALBUM_MODE, albumMode);
 
+        // AAC_PCM_MAX_OUTPUT_CHANNELS
+        int32_t maxChannelCount = mIntf->getMaxChannelCount();
+        ALOGV("AAC decoder using maximum output channel count %d", maxChannelCount);
+        aacDecoder_SetParam(mAACDecoder, AAC_PCM_MAX_OUTPUT_CHANNELS, maxChannelCount);
+
         mDrcWrap.update();
 
         UINT inBufferUsedLength = inBufferLength[0] - bytesValid[0];
@@ -847,6 +862,51 @@
                     ALOGE("Getting output loudness failed");
                 }
             }
+
+            // update config with values used for decoding:
+            //    Album mode, target reference level, DRC effect type, DRC attenuation and boost
+            //    factor, DRC compression mode, encoder target level and max channel count
+            // with input values as they were not modified by decoder
+
+            C2StreamDrcAttenuationFactorTuning::input currentAttenuationFactor(0u,
+                    (C2FloatValue) (attenuationFactor/127.));
+            work->worklets.front()->output.configUpdate.push_back(
+                    C2Param::Copy(currentAttenuationFactor));
+
+            C2StreamDrcBoostFactorTuning::input currentBoostFactor(0u,
+                    (C2FloatValue) (boostFactor/127.));
+            work->worklets.front()->output.configUpdate.push_back(
+                    C2Param::Copy(currentBoostFactor));
+
+            C2StreamDrcCompressionModeTuning::input currentCompressMode(0u,
+                    (C2Config::drc_compression_mode_t) compressMode);
+            work->worklets.front()->output.configUpdate.push_back(
+                    C2Param::Copy(currentCompressMode));
+
+            C2StreamDrcEncodedTargetLevelTuning::input currentEncodedTargetLevel(0u,
+                    (C2FloatValue) (encTargetLevel*-0.25));
+            work->worklets.front()->output.configUpdate.push_back(
+                    C2Param::Copy(currentEncodedTargetLevel));
+
+            C2StreamDrcAlbumModeTuning::input currentAlbumMode(0u,
+                    (C2Config::drc_album_mode_t) albumMode);
+            work->worklets.front()->output.configUpdate.push_back(
+                    C2Param::Copy(currentAlbumMode));
+
+            C2StreamDrcTargetReferenceLevelTuning::input currentTargetRefLevel(0u,
+                    (float) (targetRefLevel*-0.25));
+            work->worklets.front()->output.configUpdate.push_back(
+                    C2Param::Copy(currentTargetRefLevel));
+
+            C2StreamDrcEffectTypeTuning::input currentEffectype(0u,
+                    (C2Config::drc_effect_type_t) effectType);
+            work->worklets.front()->output.configUpdate.push_back(
+                    C2Param::Copy(currentEffectype));
+
+            C2StreamMaxChannelCountInfo::input currentMaxChannelCnt(0u, maxChannelCount);
+            work->worklets.front()->output.configUpdate.push_back(
+                    C2Param::Copy(currentMaxChannelCnt));
+
         } while (decoderErr == AAC_DEC_OK);
     }
 
diff --git a/media/codec2/sfplugin/CCodecBufferChannel.cpp b/media/codec2/sfplugin/CCodecBufferChannel.cpp
index 2cd357b..0626c8d 100644
--- a/media/codec2/sfplugin/CCodecBufferChannel.cpp
+++ b/media/codec2/sfplugin/CCodecBufferChannel.cpp
@@ -732,6 +732,9 @@
     std::shared_ptr<const C2StreamHdr10PlusInfo::output> hdr10PlusInfo =
         std::static_pointer_cast<const C2StreamHdr10PlusInfo::output>(
                 c2Buffer->getInfo(C2StreamHdr10PlusInfo::output::PARAM_TYPE));
+    if (hdr10PlusInfo && hdr10PlusInfo->flexCount() == 0) {
+        hdr10PlusInfo.reset();
+    }
 
     {
         Mutexed<OutputSurface>::Locked output(mOutputSurface);
@@ -783,7 +786,7 @@
                     .maxLuminance = hdrStaticInfo->mastering.maxLuminance,
                     .minLuminance = hdrStaticInfo->mastering.minLuminance,
                 };
-                hdr.validTypes = HdrMetadata::SMPTE2086;
+                hdr.validTypes |= HdrMetadata::SMPTE2086;
                 hdr.smpte2086 = smpte2086_meta;
             }
             // If the content light level fields are 0, do not use them, it
diff --git a/media/extractors/fuzzers/Android.bp b/media/extractors/fuzzers/Android.bp
index 0a70815..502743e 100644
--- a/media/extractors/fuzzers/Android.bp
+++ b/media/extractors/fuzzers/Android.bp
@@ -17,13 +17,8 @@
  *****************************************************************************
  * Originally developed and contributed by Ittiam Systems Pvt. Ltd, Bangalore
  */
-
-cc_library {
-    name: "libextractorfuzzerbase",
-
-    srcs: [
-        "ExtractorFuzzerBase.cpp",
-    ],
+cc_defaults {
+    name: "extractor-fuzzerbase-defaults",
 
     local_include_dirs: [
         "include",
@@ -43,16 +38,10 @@
     shared_libs: [
         "libutils",
         "libbinder",
+        "libbase",
+        "libcutils",
     ],
 
-    /* GETEXTRACTORDEF is not defined as extractor library is not linked in the
-     * base class. It will be included when the extractor fuzzer binary is
-     * generated.
-     */
-    allow_undefined_symbols: true,
-
-    host_supported: true,
-
     target: {
         darwin: {
             enabled: false,
@@ -60,8 +49,64 @@
     },
 }
 
+cc_defaults {
+    name: "extractor-fuzzer-defaults",
+    defaults: ["extractor-fuzzerbase-defaults"],
+
+    static_libs: [
+        "libextractorfuzzerbase",
+    ],
+
+    fuzz_config: {
+        cc: [
+            "android-media-fuzzing-reports@google.com",
+        ],
+        componentid: 155276,
+    },
+}
+
+cc_defaults {
+    name: "mpeg2-extractor-fuzzer-defaults",
+    defaults: ["extractor-fuzzer-defaults"],
+
+    include_dirs: [
+        "frameworks/av/media/extractors/mpeg2",
+        "frameworks/av/media/libstagefright",
+    ],
+
+    static_libs: [
+        "libstagefright_foundation_without_imemory",
+        "libstagefright_mpeg2support",
+        "libstagefright_mpeg2extractor",
+        "libstagefright_esds",
+        "libmpeg2extractor",
+    ],
+
+    shared_libs: [
+        "android.hardware.cas@1.0",
+        "android.hardware.cas.native@1.0",
+        "android.hidl.token@1.0-utils",
+        "android.hidl.allocator@1.0",
+        "libcrypto",
+        "libhidlmemory",
+        "libhidlbase",
+    ],
+}
+
+cc_library_static {
+    name: "libextractorfuzzerbase",
+    defaults: ["extractor-fuzzerbase-defaults"],
+    host_supported: true,
+
+    srcs: [
+        "ExtractorFuzzerBase.cpp",
+    ],
+}
+
 cc_fuzz {
     name: "mp4_extractor_fuzzer",
+    defaults: ["extractor-fuzzer-defaults"],
+    host_supported: true,
 
     srcs: [
         "mp4_extractor_fuzzer.cpp",
@@ -76,35 +121,17 @@
     ],
 
     static_libs: [
-        "liblog",
-        "libstagefright_foundation",
-        "libmediandk_format",
-        "libmedia_ndkformatpriv",
-        "libextractorfuzzerbase",
         "libstagefright_id3",
         "libstagefright_esds",
         "libmp4extractor",
     ],
 
-    shared_libs: [
-        "libutils",
-        "libbinder",
-    ],
-
     dictionary: "mp4_extractor_fuzzer.dict",
-
-    fuzz_config: {
-        cc: [
-            "android-media-fuzzing-reports@google.com",
-        ],
-        componentid: 155276,
-    },
-
-    host_supported: true,
 }
 
 cc_fuzz {
     name: "wav_extractor_fuzzer",
+    defaults: ["extractor-fuzzer-defaults"],
 
     srcs: [
         "wav_extractor_fuzzer.cpp",
@@ -115,32 +142,18 @@
     ],
 
     static_libs: [
-        "liblog",
-        "libstagefright_foundation",
-        "libmedia",
-        "libextractorfuzzerbase",
         "libfifo",
         "libwavextractor",
     ],
 
     shared_libs: [
-        "libutils",
-        "libmediandk",
-        "libbinder",
         "libbinder_ndk",
-        "libbase",
     ],
-
-    fuzz_config: {
-        cc: [
-            "android-media-fuzzing-reports@google.com",
-        ],
-        componentid: 155276,
-    },
 }
 
 cc_fuzz {
     name: "amr_extractor_fuzzer",
+    defaults: ["extractor-fuzzer-defaults"],
 
     srcs: [
         "amr_extractor_fuzzer.cpp",
@@ -151,31 +164,15 @@
     ],
 
     static_libs: [
-        "liblog",
-        "libstagefright_foundation",
-        "libmedia",
-        "libextractorfuzzerbase",
         "libamrextractor",
     ],
 
-    shared_libs: [
-        "libutils",
-        "libmediandk",
-        "libbinder",
-    ],
-
     dictionary: "amr_extractor_fuzzer.dict",
-
-    fuzz_config: {
-        cc: [
-            "android-media-fuzzing-reports@google.com",
-        ],
-        componentid: 155276,
-    },
 }
 
 cc_fuzz {
     name: "mkv_extractor_fuzzer",
+    defaults: ["extractor-fuzzer-defaults"],
 
     srcs: [
         "mkv_extractor_fuzzer.cpp",
@@ -186,10 +183,6 @@
     ],
 
     static_libs: [
-        "liblog",
-        "libstagefright_foundation",
-        "libmedia",
-        "libextractorfuzzerbase",
         "libwebm",
         "libstagefright_flacdec",
         "libstagefright_metadatautils",
@@ -197,24 +190,12 @@
         "libFLAC",
     ],
 
-    shared_libs: [
-        "libutils",
-        "libmediandk",
-        "libbinder",
-    ],
-
     dictionary: "mkv_extractor_fuzzer.dict",
-
-    fuzz_config: {
-        cc: [
-            "android-media-fuzzing-reports@google.com",
-        ],
-        componentid: 155276,
-    },
 }
 
 cc_fuzz {
     name: "ogg_extractor_fuzzer",
+    defaults: ["extractor-fuzzer-defaults"],
 
     srcs: [
         "ogg_extractor_fuzzer.cpp",
@@ -225,129 +206,43 @@
     ],
 
     static_libs: [
-        "liblog",
-        "libstagefright_foundation",
-        "libmedia",
-        "libextractorfuzzerbase",
         "libstagefright_metadatautils",
         "libvorbisidec",
         "liboggextractor",
     ],
 
-    shared_libs: [
-        "libutils",
-        "libmediandk",
-        "libbinder",
-    ],
-
     dictionary: "ogg_extractor_fuzzer.dict",
-
-    fuzz_config: {
-        cc: [
-            "android-media-fuzzing-reports@google.com",
-        ],
-        componentid: 155276,
-    },
 }
 
 cc_fuzz {
     name: "mpeg2ps_extractor_fuzzer",
+    defaults: ["mpeg2-extractor-fuzzer-defaults"],
 
     srcs: [
         "mpeg2_extractor_fuzzer.cpp",
     ],
 
-    include_dirs: [
-        "frameworks/av/media/extractors/mpeg2",
-        "frameworks/av/media/libstagefright",
-    ],
-
-    static_libs: [
-        "liblog",
-        "libstagefright_foundation_without_imemory",
-        "libmedia",
-        "libextractorfuzzerbase",
-        "libstagefright_mpeg2support",
-        "libstagefright_mpeg2extractor",
-        "libstagefright_esds",
-        "libmpeg2extractor",
-    ],
-
     cflags: [
         "-DMPEG2PS",
     ],
 
-    shared_libs: [
-        "libutils",
-        "libmediandk",
-        "libbinder",
-        "android.hardware.cas@1.0",
-        "android.hardware.cas.native@1.0",
-        "android.hidl.token@1.0-utils",
-        "android.hidl.allocator@1.0",
-        "libcrypto",
-        "libhidlmemory",
-        "libhidlbase",
-    ],
-
     dictionary: "mpeg2ps_extractor_fuzzer.dict",
-
-    fuzz_config: {
-        cc: [
-            "android-media-fuzzing-reports@google.com",
-        ],
-        componentid: 155276,
-    },
 }
 
 cc_fuzz {
     name: "mpeg2ts_extractor_fuzzer",
+    defaults: ["mpeg2-extractor-fuzzer-defaults"],
 
     srcs: [
         "mpeg2_extractor_fuzzer.cpp",
     ],
 
-    include_dirs: [
-        "frameworks/av/media/extractors/mpeg2",
-        "frameworks/av/media/libstagefright",
-    ],
-
-    static_libs: [
-        "liblog",
-        "libstagefright_foundation_without_imemory",
-        "libmedia",
-        "libextractorfuzzerbase",
-        "libstagefright_mpeg2support",
-        "libstagefright_mpeg2extractor",
-        "libstagefright_esds",
-        "libmpeg2extractor",
-    ],
-
-    shared_libs: [
-        "libutils",
-        "libmediandk",
-        "libbinder",
-        "android.hardware.cas@1.0",
-        "android.hardware.cas.native@1.0",
-        "android.hidl.token@1.0-utils",
-        "android.hidl.allocator@1.0",
-        "libcrypto",
-        "libhidlmemory",
-        "libhidlbase",
-    ],
-
     dictionary: "mpeg2ts_extractor_fuzzer.dict",
-
-    fuzz_config: {
-        cc: [
-            "android-media-fuzzing-reports@google.com",
-        ],
-        componentid: 155276,
-    },
 }
 
 cc_fuzz {
     name: "mp3_extractor_fuzzer",
+    defaults: ["extractor-fuzzer-defaults"],
 
     srcs: [
         "mp3_extractor_fuzzer.cpp",
@@ -358,31 +253,15 @@
     ],
 
     static_libs: [
-        "liblog",
-        "libstagefright_foundation",
-        "libmedia",
-        "libextractorfuzzerbase",
         "libfifo",
         "libmp3extractor",
         "libstagefright_id3",
     ],
-
-    shared_libs: [
-        "libutils",
-        "libmediandk",
-        "libbinder",
-    ],
-
-    fuzz_config: {
-        cc: [
-            "android-media-fuzzing-reports@google.com",
-        ],
-        componentid: 155276,
-    },
 }
 
 cc_fuzz {
     name: "aac_extractor_fuzzer",
+    defaults: ["extractor-fuzzer-defaults"],
 
     srcs: [
         "aac_extractor_fuzzer.cpp",
@@ -393,30 +272,14 @@
     ],
 
     static_libs: [
-        "liblog",
-        "libstagefright_foundation",
-        "libmedia",
-        "libextractorfuzzerbase",
         "libaacextractor",
         "libstagefright_metadatautils",
     ],
-
-    shared_libs: [
-        "libutils",
-        "libmediandk",
-        "libbinder",
-    ],
-
-    fuzz_config: {
-        cc: [
-            "android-media-fuzzing-reports@google.com",
-        ],
-        componentid: 155276,
-    },
 }
 
 cc_fuzz {
     name: "flac_extractor_fuzzer",
+    defaults: ["extractor-fuzzer-defaults"],
 
     srcs: [
         "flac_extractor_fuzzer.cpp",
@@ -427,29 +290,14 @@
     ],
 
     static_libs: [
-        "liblog",
-        "libstagefright_foundation",
-        "libmedia",
-        "libextractorfuzzerbase",
         "libstagefright_metadatautils",
         "libFLAC",
         "libflacextractor",
     ],
 
     shared_libs: [
-        "libutils",
-        "libmediandk",
-        "libbinder",
         "libbinder_ndk",
-        "libbase",
     ],
 
     dictionary: "flac_extractor_fuzzer.dict",
-
-    fuzz_config: {
-        cc: [
-            "android-media-fuzzing-reports@google.com",
-        ],
-        componentid: 155276,
-    },
 }
diff --git a/media/extractors/mp4/MPEG4Extractor.cpp b/media/extractors/mp4/MPEG4Extractor.cpp
index 7c85952..1d828df 100644
--- a/media/extractors/mp4/MPEG4Extractor.cpp
+++ b/media/extractors/mp4/MPEG4Extractor.cpp
@@ -2879,6 +2879,21 @@
             break;
         }
 
+        case FOURCC("pasp"):
+        {
+            *offset += chunk_size;
+            // this must be in a VisualSampleEntry box under the Sample Description Box ('stsd')
+            // ignore otherwise
+            if (depth >= 2 && mPath[depth - 2] == FOURCC("stsd")) {
+                status_t err = parsePaspBox(data_offset, chunk_data_size);
+                if (err != OK) {
+                    return err;
+                }
+            }
+
+            break;
+        }
+
         case FOURCC("titl"):
         case FOURCC("perf"):
         case FOURCC("auth"):
@@ -4050,6 +4065,26 @@
     return OK;
 }
 
+status_t MPEG4Extractor::parsePaspBox(off64_t offset, size_t size) {
+    if (size < 8 || size == SIZE_MAX || mLastTrack == NULL) {
+        return ERROR_MALFORMED;
+    }
+
+    uint32_t data[2]; // hSpacing, vSpacing
+    if (mDataSource->readAt(offset, data, 8) < 8) {
+        return ERROR_IO;
+    }
+    uint32_t hSpacing = ntohl(data[0]);
+    uint32_t vSpacing = ntohl(data[1]);
+
+    if (hSpacing != 0 && vSpacing != 0) {
+        AMediaFormat_setInt32(mLastTrack->meta, AMEDIAFORMAT_KEY_SAR_WIDTH, hSpacing);
+        AMediaFormat_setInt32(mLastTrack->meta, AMEDIAFORMAT_KEY_SAR_HEIGHT, vSpacing);
+    }
+
+    return OK;
+}
+
 status_t MPEG4Extractor::parse3GPPMetaData(off64_t offset, size_t size, int depth) {
     if (size < 4 || size == SIZE_MAX) {
         return ERROR_MALFORMED;
diff --git a/media/extractors/mp4/MPEG4Extractor.h b/media/extractors/mp4/MPEG4Extractor.h
index 1e49d50..bafc7f5 100644
--- a/media/extractors/mp4/MPEG4Extractor.h
+++ b/media/extractors/mp4/MPEG4Extractor.h
@@ -160,6 +160,7 @@
     status_t parseChunk(off64_t *offset, int depth);
     status_t parseITunesMetaData(off64_t offset, size_t size);
     status_t parseColorInfo(off64_t offset, size_t size);
+    status_t parsePaspBox(off64_t offset, size_t size);
     status_t parse3GPPMetaData(off64_t offset, size_t size, int depth);
     void parseID3v2MetaData(off64_t offset, uint64_t size);
     status_t parseQTMetaKey(off64_t data_offset, size_t data_size);
diff --git a/media/extractors/mpeg2/Android.bp b/media/extractors/mpeg2/Android.bp
index bc8632c..187803a 100644
--- a/media/extractors/mpeg2/Android.bp
+++ b/media/extractors/mpeg2/Android.bp
@@ -19,7 +19,7 @@
         "libaudioclient_headers",
         "libbase_headers",
         "libstagefright_headers",
-        "libmedia_headers",
+        "libmedia_datasource_headers",
     ],
 
     static_libs: [
diff --git a/media/libmedia/Android.bp b/media/libmedia/Android.bp
index 818548d..4ad780c 100644
--- a/media/libmedia/Android.bp
+++ b/media/libmedia/Android.bp
@@ -49,10 +49,8 @@
     path: "aidl",
 }
 
-aidl_interface {
-    name: "resourcemanager_aidl_interface",
-    unstable: true,
-    local_include_dir: "aidl",
+filegroup {
+    name: "resourcemanager_aidl",
     srcs: [
         "aidl/android/media/IResourceManagerClient.aidl",
         "aidl/android/media/IResourceManagerService.aidl",
@@ -61,6 +59,16 @@
         "aidl/android/media/MediaResourceParcel.aidl",
         "aidl/android/media/MediaResourcePolicyParcel.aidl",
     ],
+    path: "aidl",
+}
+
+aidl_interface {
+    name: "resourcemanager_aidl_interface",
+    unstable: true,
+    local_include_dir: "aidl",
+    srcs: [
+        ":resourcemanager_aidl",
+    ],
 }
 
 cc_library_shared {
diff --git a/media/libmediatranscoding/Android.bp b/media/libmediatranscoding/Android.bp
index b6dca8d..bd6621a 100644
--- a/media/libmediatranscoding/Android.bp
+++ b/media/libmediatranscoding/Android.bp
@@ -48,6 +48,8 @@
         "TranscodingClientManager.cpp",
         "TranscodingJobScheduler.cpp",
         "TranscodingUidPolicy.cpp",
+        "TranscoderWrapper.cpp",
+        "NdkCommon.cpp",
     ],
 
     shared_libs: [
@@ -57,6 +59,7 @@
         "libutils",
         "libmediatranscoder",
         "libbinder",
+        "libmediandk",
     ],
 
     export_include_dirs: ["include"],
diff --git a/media/libmediatranscoding/NdkCommon.cpp b/media/libmediatranscoding/NdkCommon.cpp
new file mode 100644
index 0000000..0461a90
--- /dev/null
+++ b/media/libmediatranscoding/NdkCommon.cpp
@@ -0,0 +1,40 @@
+/*
+ * 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 "NdkCommon"
+#include <log/log.h>
+#include <media/NdkCommon.h>
+
+#include <cstdio>
+#include <cstring>
+#include <utility>
+
+/* TODO(b/153592281)
+ * Note: constants used by the native media tests but not available in media ndk api
+ */
+const char* AMEDIA_MIMETYPE_VIDEO_VP8 = "video/x-vnd.on2.vp8";
+const char* AMEDIA_MIMETYPE_VIDEO_VP9 = "video/x-vnd.on2.vp9";
+const char* AMEDIA_MIMETYPE_VIDEO_AV1 = "video/av01";
+const char* AMEDIA_MIMETYPE_VIDEO_AVC = "video/avc";
+const char* AMEDIA_MIMETYPE_VIDEO_HEVC = "video/hevc";
+const char* AMEDIA_MIMETYPE_VIDEO_MPEG4 = "video/mp4v-es";
+const char* AMEDIA_MIMETYPE_VIDEO_H263 = "video/3gpp";
+
+/* TODO(b/153592281) */
+const char* TBD_AMEDIACODEC_PARAMETER_KEY_REQUEST_SYNC_FRAME = "request-sync";
+const char* TBD_AMEDIACODEC_PARAMETER_KEY_VIDEO_BITRATE = "video-bitrate";
+const char* TBD_AMEDIACODEC_PARAMETER_KEY_MAX_B_FRAMES = "max-bframes";
+const char* TBD_AMEDIAFORMAT_KEY_BIT_RATE_MODE = "bitrate-mode";
diff --git a/media/libmediatranscoding/TranscoderWrapper.cpp b/media/libmediatranscoding/TranscoderWrapper.cpp
new file mode 100644
index 0000000..86cb86a
--- /dev/null
+++ b/media/libmediatranscoding/TranscoderWrapper.cpp
@@ -0,0 +1,346 @@
+/*
+ * 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 "TranscoderWrapper"
+
+#include <aidl/android/media/TranscodingErrorCode.h>
+#include <aidl/android/media/TranscodingRequestParcel.h>
+#include <media/MediaTranscoder.h>
+#include <media/NdkCommon.h>
+#include <media/TranscoderWrapper.h>
+#include <utils/Log.h>
+
+#include <thread>
+
+namespace android {
+using Status = ::ndk::ScopedAStatus;
+using ::aidl::android::media::TranscodingErrorCode;
+using ::aidl::android::media::TranscodingVideoCodecType;
+using ::aidl::android::media::TranscodingVideoTrackFormat;
+
+static TranscodingErrorCode toTranscodingError(media_status_t status) {
+    switch (status) {
+    case AMEDIA_OK:
+        return TranscodingErrorCode::kNoError;
+    case AMEDIACODEC_ERROR_INSUFFICIENT_RESOURCE:  // FALLTHRU
+    case AMEDIACODEC_ERROR_RECLAIMED:
+        return TranscodingErrorCode::kInsufficientResources;
+    case AMEDIA_ERROR_MALFORMED:
+        return TranscodingErrorCode::kMalformed;
+    case AMEDIA_ERROR_UNSUPPORTED:
+        return TranscodingErrorCode::kUnsupported;
+    case AMEDIA_ERROR_INVALID_OBJECT:  // FALLTHRU
+    case AMEDIA_ERROR_INVALID_PARAMETER:
+        return TranscodingErrorCode::kInvalidParameter;
+    case AMEDIA_ERROR_INVALID_OPERATION:
+        return TranscodingErrorCode::kInvalidOperation;
+    case AMEDIA_ERROR_IO:
+        return TranscodingErrorCode::kErrorIO;
+    case AMEDIA_ERROR_UNKNOWN:  // FALLTHRU
+    default:
+        return TranscodingErrorCode::kUnknown;
+    }
+}
+
+//static
+const char* TranscoderWrapper::toString(Event::Type type) {
+    switch (type) {
+    case Event::Start:
+        return "Start";
+    case Event::Pause:
+        return "Pause";
+    case Event::Resume:
+        return "Resume";
+    case Event::Stop:
+        return "Stop";
+    case Event::Finish:
+        return "Finish";
+    case Event::Error:
+        return "Error";
+    default:
+        break;
+    }
+    return "(unknown)";
+}
+
+class TranscoderWrapper::CallbackImpl : public MediaTranscoder::CallbackInterface {
+public:
+    CallbackImpl(const std::shared_ptr<TranscoderWrapper>& owner, ClientIdType clientId,
+                 JobIdType jobId)
+          : mOwner(owner), mClientId(clientId), mJobId(jobId) {}
+
+    virtual void onFinished(const MediaTranscoder* transcoder __unused) override {
+        auto owner = mOwner.lock();
+        if (owner != nullptr) {
+            owner->onFinish(mClientId, mJobId);
+        }
+    }
+
+    virtual void onError(const MediaTranscoder* transcoder __unused,
+                         media_status_t error) override {
+        auto owner = mOwner.lock();
+        if (owner != nullptr) {
+            owner->onError(mClientId, mJobId, toTranscodingError(error));
+        }
+    }
+
+    virtual void onProgressUpdate(const MediaTranscoder* transcoder __unused,
+                                  int32_t progress) override {
+        ALOGV("%s: job {%lld, %d}, progress %d", __FUNCTION__, (long long)mClientId, mJobId,
+              progress);
+    }
+
+    virtual void onCodecResourceLost(const MediaTranscoder* transcoder __unused,
+                                     const std::shared_ptr<const Parcelable>& pausedState
+                                             __unused) override {
+        ALOGV("%s: job {%lld, %d}", __FUNCTION__, (long long)mClientId, mJobId);
+    }
+
+private:
+    std::weak_ptr<TranscoderWrapper> mOwner;
+    ClientIdType mClientId;
+    JobIdType mJobId;
+};
+
+TranscoderWrapper::TranscoderWrapper() : mCurrentClientId(0), mCurrentJobId(-1) {
+    std::thread(&TranscoderWrapper::threadLoop, this).detach();
+}
+
+void TranscoderWrapper::setCallback(const std::shared_ptr<TranscoderCallbackInterface>& cb) {
+    mCallback = cb;
+}
+
+void TranscoderWrapper::start(ClientIdType clientId, JobIdType jobId,
+                              const TranscodingRequestParcel& request,
+                              const std::shared_ptr<ITranscodingClientCallback>& callback) {
+    queueEvent(Event::Start, clientId, jobId, [=] {
+        TranscodingErrorCode err = handleStart(clientId, jobId, request, callback);
+
+        if (err != TranscodingErrorCode::kNoError) {
+            cleanup();
+
+            auto callback = mCallback.lock();
+            if (callback != nullptr) {
+                callback->onError(clientId, jobId, err);
+            }
+        }
+    });
+}
+
+void TranscoderWrapper::pause(ClientIdType clientId, JobIdType jobId) {
+    queueEvent(Event::Pause, clientId, jobId, [] {});
+}
+
+void TranscoderWrapper::resume(ClientIdType clientId, JobIdType jobId) {
+    queueEvent(Event::Resume, 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);
+        } else {
+            ALOGI("transcoder stopped");
+        }
+
+        cleanup();
+    });
+}
+
+void TranscoderWrapper::onFinish(ClientIdType clientId, JobIdType jobId) {
+    queueEvent(Event::Finish, clientId, jobId, [=] {
+        cleanup();
+
+        auto callback = mCallback.lock();
+        if (callback != nullptr) {
+            callback->onFinish(clientId, jobId);
+        }
+    });
+}
+
+void TranscoderWrapper::onError(ClientIdType clientId, JobIdType jobId,
+                                TranscodingErrorCode error) {
+    queueEvent(Event::Error, clientId, jobId, [=] {
+        cleanup();
+
+        auto callback = mCallback.lock();
+        if (callback != nullptr) {
+            callback->onError(clientId, jobId, error);
+        }
+    });
+}
+
+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(
+        ClientIdType clientId, JobIdType jobId, const TranscodingRequestParcel& request,
+        const std::shared_ptr<ITranscodingClientCallback>& clientCb) {
+    if (clientCb == nullptr) {
+        ALOGE("client callback is null");
+        return TranscodingErrorCode::kInvalidParameter;
+    }
+
+    if (mTranscoder != nullptr) {
+        ALOGE("transcoder already running");
+        return TranscodingErrorCode::kInvalidOperation;
+    }
+
+    Status status;
+    ::ndk::ScopedFileDescriptor srcFd, dstFd;
+    status = clientCb->openFileDescriptor(request.sourceFilePath, "r", &srcFd);
+    if (!status.isOk() || srcFd.get() < 0) {
+        ALOGE("failed to open source");
+        return TranscodingErrorCode::kErrorIO;
+    }
+
+    status = clientCb->openFileDescriptor(request.destinationFilePath, "w", &dstFd);
+    if (!status.isOk() || dstFd.get() < 0) {
+        ALOGE("failed to open destination");
+        return TranscodingErrorCode::kErrorIO;
+    }
+
+    mCurrentClientId = clientId;
+    mCurrentJobId = jobId;
+    mTranscoderCb = std::make_shared<CallbackImpl>(shared_from_this(), clientId, jobId);
+    mTranscoder = MediaTranscoder::create(mTranscoderCb, nullptr);
+    if (mTranscoder == nullptr) {
+        ALOGE("failed to create transcoder");
+        return TranscodingErrorCode::kUnknown;
+    }
+
+    media_status_t err = mTranscoder->configureSource(srcFd.get());
+    if (err != AMEDIA_OK) {
+        ALOGE("failed to configure source: %d", err);
+        return toTranscodingError(err);
+    }
+
+    std::vector<std::shared_ptr<AMediaFormat>> trackFormats = mTranscoder->getTrackFormats();
+    if (trackFormats.size() == 0) {
+        ALOGE("failed to get track formats!");
+        return TranscodingErrorCode::kMalformed;
+    }
+
+    for (int i = 0; i < trackFormats.size(); ++i) {
+        AMediaFormat* format = nullptr;
+        const char* mime = nullptr;
+        AMediaFormat_getString(trackFormats[i].get(), AMEDIAFORMAT_KEY_MIME, &mime);
+
+        if (!strncmp(mime, "video/", 6)) {
+            format = getVideoFormat(mime, request.requestedVideoTrackFormat);
+        }
+
+        err = mTranscoder->configureTrackFormat(i, format);
+        if (format != nullptr) {
+            AMediaFormat_delete(format);
+        }
+        if (err != AMEDIA_OK) {
+            ALOGE("failed to configure track format for track %d: %d", i, err);
+            return toTranscodingError(err);
+        }
+    }
+
+    err = mTranscoder->configureDestination(dstFd.get());
+    if (err != AMEDIA_OK) {
+        ALOGE("failed to configure dest: %d", err);
+        return toTranscodingError(err);
+    }
+
+    err = mTranscoder->start();
+    if (err != AMEDIA_OK) {
+        ALOGE("failed to start transcoder: %d", err);
+        return toTranscodingError(err);
+    }
+
+    ALOGI("transcoder started");
+    return TranscodingErrorCode::kNoError;
+}
+
+void TranscoderWrapper::cleanup() {
+    mCurrentClientId = 0;
+    mCurrentJobId = -1;
+    mTranscoderCb = nullptr;
+    mTranscoder = nullptr;
+}
+
+void TranscoderWrapper::queueEvent(Event::Type type, ClientIdType clientId, JobIdType jobId,
+                                   const std::function<void()> runnable) {
+    ALOGV("%s: job {%lld, %d}: %s", __FUNCTION__, (long long)clientId, jobId, toString(type));
+
+    std::scoped_lock lock{mLock};
+
+    mQueue.push_back({type, clientId, jobId, runnable});
+    mCondition.notify_one();
+}
+
+void TranscoderWrapper::threadLoop() {
+    std::unique_lock<std::mutex> lock{mLock};
+    // TranscoderWrapper currently lives in the transcoding service, as long as
+    // MediaTranscodingService itself.
+    while (true) {
+        // Wait for the next event.
+        while (mQueue.empty()) {
+            mCondition.wait(lock);
+        }
+
+        Event event = *mQueue.begin();
+        mQueue.pop_front();
+
+        ALOGD("%s: job {%lld, %d}: %s", __FUNCTION__, (long long)event.clientId, event.jobId,
+              toString(event.type));
+
+        event.runnable();
+    }
+}
+
+}  // namespace android
diff --git a/media/libmediatranscoding/TranscodingJobScheduler.cpp b/media/libmediatranscoding/TranscodingJobScheduler.cpp
index d5ae2a3..3256384 100644
--- a/media/libmediatranscoding/TranscodingJobScheduler.cpp
+++ b/media/libmediatranscoding/TranscodingJobScheduler.cpp
@@ -76,7 +76,8 @@
         // the topJob now.
         if (!mResourceLost) {
             if (topJob->state == Job::NOT_STARTED) {
-                mTranscoder->start(topJob->key.first, topJob->key.second, topJob->request);
+                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);
             }
@@ -349,7 +350,8 @@
         {
             auto clientCallback = mJobMap[jobKey].callback.lock();
             if (clientCallback != nullptr) {
-                clientCallback->onTranscodingFinished(jobId, TranscodingResultParcel({jobId, -1 /*actualBitrateBps*/}));
+                clientCallback->onTranscodingFinished(
+                        jobId, TranscodingResultParcel({jobId, -1 /*actualBitrateBps*/}));
             }
         }
 
diff --git a/media/libmediatranscoding/aidl/android/media/TranscodingErrorCode.aidl b/media/libmediatranscoding/aidl/android/media/TranscodingErrorCode.aidl
index 7f47fdc..b044d41 100644
--- a/media/libmediatranscoding/aidl/android/media/TranscodingErrorCode.aidl
+++ b/media/libmediatranscoding/aidl/android/media/TranscodingErrorCode.aidl
@@ -23,11 +23,12 @@
  */
 @Backing(type = "int")
 enum TranscodingErrorCode {
-    kUnknown = 0,
-    kUnsupported = 1,
-    kDecoderError = 2,
-    kEncoderError = 3,
-    kExtractorError = 4,
-    kMuxerError = 5,
-    kInvalidBitstream = 6
+    kNoError = 0,
+    kUnknown = 1,
+    kMalformed = 2,
+    kUnsupported = 3,
+    kInvalidParameter = 4,
+    kInvalidOperation = 5,
+    kErrorIO = 6,
+    kInsufficientResources = 7,
 }
\ No newline at end of file
diff --git a/media/libmediatranscoding/aidl/android/media/TranscodingRequestParcel.aidl b/media/libmediatranscoding/aidl/android/media/TranscodingRequestParcel.aidl
index b130a88..8e32269 100644
--- a/media/libmediatranscoding/aidl/android/media/TranscodingRequestParcel.aidl
+++ b/media/libmediatranscoding/aidl/android/media/TranscodingRequestParcel.aidl
@@ -74,7 +74,7 @@
     boolean isForTesting = false;
 
     /**
-     * Test configuration. This is only valid when isForTesting is set to true.
+     * Test configuration. This will be available only when isForTesting is set to true.
      */
-    TranscodingTestConfig testConfig;
+     @nullable TranscodingTestConfig testConfig;
 }
diff --git a/media/libmediatranscoding/include/media/NdkCommon.h b/media/libmediatranscoding/include/media/NdkCommon.h
new file mode 100644
index 0000000..63bee58
--- /dev/null
+++ b/media/libmediatranscoding/include/media/NdkCommon.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_MEDIA_TRANSCODING_NDK_COMMON_H
+#define ANDROID_MEDIA_TRANSCODING_NDK_COMMON_H
+
+#include <media/NdkMediaFormat.h>
+
+extern const char* AMEDIA_MIMETYPE_VIDEO_VP8;
+extern const char* AMEDIA_MIMETYPE_VIDEO_VP9;
+extern const char* AMEDIA_MIMETYPE_VIDEO_AV1;
+extern const char* AMEDIA_MIMETYPE_VIDEO_AVC;
+extern const char* AMEDIA_MIMETYPE_VIDEO_HEVC;
+extern const char* AMEDIA_MIMETYPE_VIDEO_MPEG4;
+extern const char* AMEDIA_MIMETYPE_VIDEO_H263;
+
+// TODO(b/146420990)
+// TODO: make MediaTranscoder use the consts from this header.
+typedef enum {
+    OUTPUT_FORMAT_START = 0,
+    OUTPUT_FORMAT_MPEG_4 = OUTPUT_FORMAT_START,
+    OUTPUT_FORMAT_WEBM = OUTPUT_FORMAT_START + 1,
+    OUTPUT_FORMAT_THREE_GPP = OUTPUT_FORMAT_START + 2,
+    OUTPUT_FORMAT_HEIF = OUTPUT_FORMAT_START + 3,
+    OUTPUT_FORMAT_OGG = OUTPUT_FORMAT_START + 4,
+    OUTPUT_FORMAT_LIST_END = OUTPUT_FORMAT_START + 4,
+} MuxerFormat;
+
+// Color formats supported by encoder - should mirror supportedColorList
+// from MediaCodecConstants.h (are these going to be deprecated)
+static constexpr int COLOR_FormatYUV420SemiPlanar = 21;
+static constexpr int COLOR_FormatYUV420Flexible = 0x7F420888;
+static constexpr int COLOR_FormatSurface = 0x7f000789;
+
+// constants not defined in NDK
+extern const char* TBD_AMEDIACODEC_PARAMETER_KEY_REQUEST_SYNC_FRAME;
+extern const char* TBD_AMEDIACODEC_PARAMETER_KEY_VIDEO_BITRATE;
+extern const char* TBD_AMEDIACODEC_PARAMETER_KEY_MAX_B_FRAMES;
+extern const char* TBD_AMEDIAFORMAT_KEY_BIT_RATE_MODE;
+static constexpr int TBD_AMEDIACODEC_BUFFER_FLAG_KEY_FRAME = 0x1;
+
+static constexpr int kBitrateModeConstant = 2;
+
+#endif  // ANDROID_MEDIA_TRANSCODING_NDK_COMMON_H
diff --git a/media/libmediatranscoding/include/media/TranscoderInterface.h b/media/libmediatranscoding/include/media/TranscoderInterface.h
index 97b7126..85e0164 100644
--- a/media/libmediatranscoding/include/media/TranscoderInterface.h
+++ b/media/libmediatranscoding/include/media/TranscoderInterface.h
@@ -17,12 +17,14 @@
 #ifndef ANDROID_MEDIA_TRANSCODER_INTERFACE_H
 #define ANDROID_MEDIA_TRANSCODER_INTERFACE_H
 
+#include <aidl/android/media/ITranscodingClientCallback.h>
 #include <aidl/android/media/TranscodingErrorCode.h>
 #include <aidl/android/media/TranscodingRequestParcel.h>
 #include <media/TranscodingDefs.h>
 
 namespace android {
 
+using ::aidl::android::media::ITranscodingClientCallback;
 using ::aidl::android::media::TranscodingErrorCode;
 using ::aidl::android::media::TranscodingRequestParcel;
 class TranscoderCallbackInterface;
@@ -34,7 +36,8 @@
     // 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) = 0;
+                       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 stop(ClientIdType clientId, JobIdType jobId) = 0;
diff --git a/media/libmediatranscoding/include/media/TranscoderWrapper.h b/media/libmediatranscoding/include/media/TranscoderWrapper.h
new file mode 100644
index 0000000..3da05b2
--- /dev/null
+++ b/media/libmediatranscoding/include/media/TranscoderWrapper.h
@@ -0,0 +1,79 @@
+/*
+ * 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_TRANSCODER_WRAPPER_H
+#define ANDROID_TRANSCODER_WRAPPER_H
+
+#include <android-base/thread_annotations.h>
+#include <media/TranscoderInterface.h>
+
+#include <list>
+#include <mutex>
+
+namespace android {
+
+class MediaTranscoder;
+
+/*
+ * Wrapper class around MediaTranscoder.
+ * Implements TranscoderInterface for TranscodingJobScheduler to use.
+ */
+class TranscoderWrapper : public TranscoderInterface,
+                          public std::enable_shared_from_this<TranscoderWrapper> {
+public:
+    TranscoderWrapper();
+
+    virtual void setCallback(const std::shared_ptr<TranscoderCallbackInterface>& cb) override;
+    virtual void start(ClientIdType clientId, JobIdType jobId,
+                       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 stop(ClientIdType clientId, JobIdType jobId) override;
+
+private:
+    class CallbackImpl;
+    struct Event {
+        enum Type { NoEvent, Start, Pause, Resume, Stop, Finish, Error } type;
+        ClientIdType clientId;
+        JobIdType jobId;
+        std::function<void()> runnable;
+    };
+    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);
+    ClientIdType mCurrentClientId;
+    JobIdType mCurrentJobId;
+
+    static const char* toString(Event::Type type);
+    void onFinish(ClientIdType clientId, JobIdType jobId);
+    void onError(ClientIdType clientId, JobIdType jobId, TranscodingErrorCode error);
+
+    TranscodingErrorCode handleStart(ClientIdType clientId, JobIdType jobId,
+                                     const TranscodingRequestParcel& request,
+                                     const std::shared_ptr<ITranscodingClientCallback>& callback);
+
+    void cleanup();
+    void queueEvent(Event::Type type, ClientIdType clientId, JobIdType jobId,
+                    const std::function<void()> runnable);
+    void threadLoop();
+};
+
+}  // namespace android
+#endif  // ANDROID_TRANSCODER_WRAPPER_H
diff --git a/media/libmediatranscoding/include/media/TranscodingRequest.h b/media/libmediatranscoding/include/media/TranscodingRequest.h
index 6bc9db6..63de1fb 100644
--- a/media/libmediatranscoding/include/media/TranscodingRequest.h
+++ b/media/libmediatranscoding/include/media/TranscodingRequest.h
@@ -38,6 +38,7 @@
         sourceFilePath = parcel.sourceFilePath;
         destinationFilePath = parcel.destinationFilePath;
         transcodingType = parcel.transcodingType;
+        requestedVideoTrackFormat = parcel.requestedVideoTrackFormat;
         priority = parcel.priority;
         requestProgressUpdate = parcel.requestProgressUpdate;
         requestJobEventUpdate = parcel.requestJobEventUpdate;
diff --git a/media/libmediatranscoding/tests/TranscodingJobScheduler_tests.cpp b/media/libmediatranscoding/tests/TranscodingJobScheduler_tests.cpp
index b9bebac..1931a0e 100644
--- a/media/libmediatranscoding/tests/TranscodingJobScheduler_tests.cpp
+++ b/media/libmediatranscoding/tests/TranscodingJobScheduler_tests.cpp
@@ -87,8 +87,8 @@
     // TranscoderInterface
     void setCallback(const std::shared_ptr<TranscoderCallbackInterface>& /*cb*/) override {}
 
-    void start(ClientIdType clientId, JobIdType jobId,
-               const TranscodingRequestParcel& /*request*/) override {
+    void start(ClientIdType clientId, JobIdType jobId, const TranscodingRequestParcel& /*request*/,
+               const std::shared_ptr<ITranscodingClientCallback>& /*clientCallback*/) override {
         mEventQueue.push_back(Start(clientId, jobId));
     }
     void pause(ClientIdType clientId, JobIdType jobId) override {
@@ -433,9 +433,9 @@
     EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Resume(CLIENT(0), JOB(0)));
 
     // Fail running offline job, and test error code propagation.
-    mScheduler->onError(CLIENT(0), JOB(0), TranscodingErrorCode::kInvalidBitstream);
+    mScheduler->onError(CLIENT(0), JOB(0), TranscodingErrorCode::kInvalidOperation);
     EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Failed(CLIENT(0), JOB(0)));
-    EXPECT_EQ(mTranscoder->getLastError(), TranscodingErrorCode::kInvalidBitstream);
+    EXPECT_EQ(mTranscoder->getLastError(), TranscodingErrorCode::kInvalidOperation);
 
     // Duplicate fail for last job, should be ignored.
     mScheduler->onError(CLIENT(0), JOB(0), TranscodingErrorCode::kUnknown);
diff --git a/media/libmediatranscoding/tests/assets/backyard_hevc_1920x1080_20Mbps.mp4 b/media/libmediatranscoding/tests/assets/backyard_hevc_1920x1080_20Mbps.mp4
new file mode 100644
index 0000000..80d1ec3
--- /dev/null
+++ b/media/libmediatranscoding/tests/assets/backyard_hevc_1920x1080_20Mbps.mp4
Binary files differ
diff --git a/media/libmediatranscoding/transcoder/tests/assets/cubicle_avc_480x240_aac_24KHz.mp4 b/media/libmediatranscoding/tests/assets/cubicle_avc_480x240_aac_24KHz.mp4
similarity index 100%
rename from media/libmediatranscoding/transcoder/tests/assets/cubicle_avc_480x240_aac_24KHz.mp4
rename to media/libmediatranscoding/tests/assets/cubicle_avc_480x240_aac_24KHz.mp4
Binary files differ
diff --git a/media/libmediatranscoding/tests/assets/jets_hevc_1280x720_20Mbps.mp4 b/media/libmediatranscoding/tests/assets/jets_hevc_1280x720_20Mbps.mp4
new file mode 100644
index 0000000..7794b99
--- /dev/null
+++ b/media/libmediatranscoding/tests/assets/jets_hevc_1280x720_20Mbps.mp4
Binary files differ
diff --git a/media/libmediatranscoding/tests/assets/plex_hevc_3840x2160_12Mbps.mp4 b/media/libmediatranscoding/tests/assets/plex_hevc_3840x2160_12Mbps.mp4
new file mode 100644
index 0000000..92dda3b
--- /dev/null
+++ b/media/libmediatranscoding/tests/assets/plex_hevc_3840x2160_12Mbps.mp4
Binary files differ
diff --git a/media/libmediatranscoding/tests/assets/plex_hevc_3840x2160_20Mbps.mp4 b/media/libmediatranscoding/tests/assets/plex_hevc_3840x2160_20Mbps.mp4
new file mode 100644
index 0000000..2fe37bd
--- /dev/null
+++ b/media/libmediatranscoding/tests/assets/plex_hevc_3840x2160_20Mbps.mp4
Binary files differ
diff --git a/media/libmediatranscoding/tests/assets/push_assets.sh b/media/libmediatranscoding/tests/assets/push_assets.sh
new file mode 100755
index 0000000..2fcb233
--- /dev/null
+++ b/media/libmediatranscoding/tests/assets/push_assets.sh
@@ -0,0 +1,25 @@
+#!/bin/bash
+#
+# Pushes the assets to the /data/local/tmp.
+#
+
+if [ "$SYNC_FINISHED" != true ]; then
+  if [ -z "$ANDROID_BUILD_TOP" ]; then
+      echo "Android build environment not set"
+      exit -1
+  fi
+
+  # ensure we have mm
+  . $ANDROID_BUILD_TOP/build/envsetup.sh
+
+  mm
+
+  echo "waiting for device"
+
+  adb root && adb wait-for-device remount
+fi
+
+#TODO(hkuang): Check if the destination folder already exists. If so, skip the copying.
+echo "Copying files to device"
+adb push $ANDROID_BUILD_TOP/frameworks/av/media/libmediatranscoding/tests/assets /data/local/tmp/TranscodingTestAssets
+echo "Copy done"
diff --git a/media/libmediatranscoding/transcoder/tests/AndroidTestTemplate.xml b/media/libmediatranscoding/transcoder/tests/AndroidTestTemplate.xml
index 23d1bab..a9a7e2e 100644
--- a/media/libmediatranscoding/transcoder/tests/AndroidTestTemplate.xml
+++ b/media/libmediatranscoding/transcoder/tests/AndroidTestTemplate.xml
@@ -19,7 +19,7 @@
         <option name="cleanup" value="false" />
         <option name="push-file"
             key="assets"
-            value="/data/local/tmp/TranscoderTestAssets" />
+            value="/data/local/tmp/TranscodingTestAssets" />
     </target_preparer>
 
     <test class="com.android.tradefed.testtype.GTest" >
diff --git a/media/libmediatranscoding/transcoder/tests/MediaSampleReaderNDKTests.cpp b/media/libmediatranscoding/transcoder/tests/MediaSampleReaderNDKTests.cpp
index 858fcb3..805095e 100644
--- a/media/libmediatranscoding/transcoder/tests/MediaSampleReaderNDKTests.cpp
+++ b/media/libmediatranscoding/transcoder/tests/MediaSampleReaderNDKTests.cpp
@@ -40,7 +40,7 @@
     void SetUp() override {
         LOG(DEBUG) << "MediaSampleReaderNDKTests set up";
         const char* sourcePath =
-                "/data/local/tmp/TranscoderTestAssets/cubicle_avc_480x240_aac_24KHz.mp4";
+                "/data/local/tmp/TranscodingTestAssets/cubicle_avc_480x240_aac_24KHz.mp4";
 
         mExtractor = AMediaExtractor_new();
         ASSERT_NE(mExtractor, nullptr);
diff --git a/media/libmediatranscoding/transcoder/tests/MediaSampleWriterTests.cpp b/media/libmediatranscoding/transcoder/tests/MediaSampleWriterTests.cpp
index 752f0c9..9ffb32b 100644
--- a/media/libmediatranscoding/transcoder/tests/MediaSampleWriterTests.cpp
+++ b/media/libmediatranscoding/transcoder/tests/MediaSampleWriterTests.cpp
@@ -131,7 +131,7 @@
 public:
     void init() {
         static const char* sourcePath =
-                "/data/local/tmp/TranscoderTestAssets/cubicle_avc_480x240_aac_24KHz.mp4";
+                "/data/local/tmp/TranscodingTestAssets/cubicle_avc_480x240_aac_24KHz.mp4";
 
         mExtractor = AMediaExtractor_new();
         ASSERT_NE(mExtractor, nullptr);
diff --git a/media/libmediatranscoding/transcoder/tests/MediaTranscoderTests.cpp b/media/libmediatranscoding/transcoder/tests/MediaTranscoderTests.cpp
index 09c24b5..7e10558 100644
--- a/media/libmediatranscoding/transcoder/tests/MediaTranscoderTests.cpp
+++ b/media/libmediatranscoding/transcoder/tests/MediaTranscoderTests.cpp
@@ -67,7 +67,7 @@
 };
 
 static const char* SOURCE_PATH =
-        "/data/local/tmp/TranscoderTestAssets/cubicle_avc_480x240_aac_24KHz.mp4";
+        "/data/local/tmp/TranscodingTestAssets/cubicle_avc_480x240_aac_24KHz.mp4";
 // Write-only, create file if non-existent, don't overwrite existing file.
 static constexpr int kOpenFlags = O_WRONLY | O_CREAT | O_EXCL;
 // User R+W permission.
diff --git a/media/libmediatranscoding/transcoder/tests/PassthroughTrackTranscoderTests.cpp b/media/libmediatranscoding/transcoder/tests/PassthroughTrackTranscoderTests.cpp
index 316793a..b79f58c 100644
--- a/media/libmediatranscoding/transcoder/tests/PassthroughTrackTranscoderTests.cpp
+++ b/media/libmediatranscoding/transcoder/tests/PassthroughTrackTranscoderTests.cpp
@@ -41,7 +41,7 @@
 
     void initSourceAndExtractor() {
         const char* sourcePath =
-                "/data/local/tmp/TranscoderTestAssets/cubicle_avc_480x240_aac_24KHz.mp4";
+                "/data/local/tmp/TranscodingTestAssets/cubicle_avc_480x240_aac_24KHz.mp4";
 
         mExtractor = AMediaExtractor_new();
         ASSERT_NE(mExtractor, nullptr);
diff --git a/media/libmediatranscoding/transcoder/tests/VideoTrackTranscoderTests.cpp b/media/libmediatranscoding/transcoder/tests/VideoTrackTranscoderTests.cpp
index 6b1f640..1eb9e5a 100644
--- a/media/libmediatranscoding/transcoder/tests/VideoTrackTranscoderTests.cpp
+++ b/media/libmediatranscoding/transcoder/tests/VideoTrackTranscoderTests.cpp
@@ -42,7 +42,7 @@
     void SetUp() override {
         LOG(DEBUG) << "VideoTrackTranscoderTests set up";
         const char* sourcePath =
-                "/data/local/tmp/TranscoderTestAssets/cubicle_avc_480x240_aac_24KHz.mp4";
+                "/data/local/tmp/TranscodingTestAssets/cubicle_avc_480x240_aac_24KHz.mp4";
 
         const int sourceFd = open(sourcePath, O_RDONLY);
         ASSERT_GT(sourceFd, 0);
diff --git a/media/libmediatranscoding/transcoder/tests/build_and_run_all_unit_tests.sh b/media/libmediatranscoding/transcoder/tests/build_and_run_all_unit_tests.sh
index 01beeee..241178d 100755
--- a/media/libmediatranscoding/transcoder/tests/build_and_run_all_unit_tests.sh
+++ b/media/libmediatranscoding/transcoder/tests/build_and_run_all_unit_tests.sh
@@ -18,7 +18,9 @@
 
   adb root && adb wait-for-device remount && adb sync
 fi
-adb push assets /data/local/tmp/TranscoderTestAssets
+
+# Push the files onto the device.
+. $ANDROID_BUILD_TOP/frameworks/av/media/libmediatranscoding/tests/assets/push_assets.sh
 
 echo "========================================"
 
diff --git a/media/libstagefright/Android.bp b/media/libstagefright/Android.bp
index fac6350..2f149de 100644
--- a/media/libstagefright/Android.bp
+++ b/media/libstagefright/Android.bp
@@ -144,8 +144,10 @@
 
     header_libs: [
         "libaudioclient_headers",
-        "libmedia_headers",
+        "libbase_headers",
+        "libmedia_datasource_headers",
         "media_ndk_headers",
+        "media_plugin_headers",
     ],
 
     cflags: [
@@ -162,6 +164,18 @@
             "signed-integer-overflow",
         ],
     },
+
+    host_supported: true,
+    target: {
+        darwin: {
+            enabled: false,
+        },
+        linux: {
+            cflags: [
+                "-DDISABLE_AUDIO_SYSTEM_OFFLOAD",
+            ],
+        }
+    },
 }
 
 cc_library_shared {
diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp
index 39423c7..15f47be 100644
--- a/media/libstagefright/MPEG4Writer.cpp
+++ b/media/libstagefright/MPEG4Writer.cpp
@@ -4687,10 +4687,18 @@
 
 // This is useful if the pixel is not square
 void MPEG4Writer::Track::writePaspBox() {
-    mOwner->beginBox("pasp");
-    mOwner->writeInt32(1 << 16);  // hspacing
-    mOwner->writeInt32(1 << 16);  // vspacing
-    mOwner->endBox();  // pasp
+    // Do not write 'pasp' box unless the track format specifies it.
+    // According to ISO/IEC 14496-12 (ISO base media file format), 'pasp' box
+    // is optional. If present, it overrides the SAR from the video CSD. Only
+    // set it if the track format specifically requests that.
+    int32_t hSpacing, vSpacing;
+    if (mMeta->findInt32(kKeySARWidth, &hSpacing) && (hSpacing > 0)
+            && mMeta->findInt32(kKeySARHeight, &vSpacing) && (vSpacing > 0)) {
+        mOwner->beginBox("pasp");
+        mOwner->writeInt32(hSpacing);  // hspacing
+        mOwner->writeInt32(vSpacing);  // vspacing
+        mOwner->endBox();  // pasp
+    }
 }
 
 int64_t MPEG4Writer::Track::getStartTimeOffsetTimeUs() const {
diff --git a/media/libstagefright/Utils.cpp b/media/libstagefright/Utils.cpp
index ef69827..d67874f 100644
--- a/media/libstagefright/Utils.cpp
+++ b/media/libstagefright/Utils.cpp
@@ -2178,7 +2178,11 @@
     }
     // Check if offload is possible for given format, stream type, sample rate,
     // bit rate, duration, video and streaming
+#ifdef DISABLE_AUDIO_SYSTEM_OFFLOAD
+    return false;
+#else
     return AudioSystem::isOffloadSupported(info);
+#endif
 }
 
 HLSTime::HLSTime(const sp<AMessage>& meta) :
diff --git a/media/libstagefright/data/media_codecs_sw.xml b/media/libstagefright/data/media_codecs_sw.xml
index dd2eed3..6571162 100644
--- a/media/libstagefright/data/media_codecs_sw.xml
+++ b/media/libstagefright/data/media_codecs_sw.xml
@@ -295,12 +295,12 @@
             <Feature name="bitrate-modes" value="VBR,CBR" />
         </MediaCodec>
         <MediaCodec name="c2.android.hevc.encoder" type="video/hevc" variant="!slow-cpu">
-            <!-- profiles and levels:  ProfileMain : MainTierLevel51 -->
-            <Limit name="size" min="2x2" max="512x512" />
+            <!-- profiles and levels:  ProfileMain : MainTierLevel3 -->
+            <Limit name="size" min="2x2" max="960x544" />
             <Limit name="alignment" value="2x2" />
             <Limit name="block-size" value="8x8" />
-            <Limit name="block-count" range="1-4096" /> <!-- max 512x512 -->
-            <Limit name="blocks-per-second" range="1-122880" />
+            <Limit name="block-count" range="1-8160" /> <!-- max 960x544 -->
+            <Limit name="blocks-per-second" range="1-244880" />
             <Limit name="frame-rate" range="1-120" />
             <Limit name="bitrate" range="1-10000000" />
             <Limit name="complexity" range="0-10"  default="0" />
diff --git a/media/libstagefright/tests/writer/WriterTest.cpp b/media/libstagefright/tests/writer/WriterTest.cpp
index f1c599f..4c0add4 100644
--- a/media/libstagefright/tests/writer/WriterTest.cpp
+++ b/media/libstagefright/tests/writer/WriterTest.cpp
@@ -49,8 +49,32 @@
     int32_t channelCount;
 };
 
+enum inputId {
+    // audio streams
+    AAC_1,
+    AAC_ADTS_1,
+    AMR_NB_1,
+    AMR_WB_1,
+    FLAC_1,
+    OPUS_1,
+    VORBIS_1,
+    // video streams
+    AV1_1,
+    AVC_1,
+    H263_1,
+    HEVC_1,
+    MPEG4_1,
+    VP8_1,
+    VP9_1,
+    // heif stream
+    HEIC_1,
+    UNUSED_ID,
+    UNKNOWN_ID,
+};
+
 // LookUpTable of clips and metadata for component testing
 static const struct InputData {
+    inputId inpId;
     const char *mime;
     string inputFile;
     string info;
@@ -58,61 +82,67 @@
     int32_t secondParam;
     bool isAudio;
 } kInputData[] = {
-        {MEDIA_MIMETYPE_AUDIO_OPUS, "bbb_opus_stereo_128kbps_48000hz.opus",
-         "bbb_opus_stereo_128kbps_48000hz.info", 48000, 2, true},
-        {MEDIA_MIMETYPE_AUDIO_AAC, "bbb_aac_stereo_128kbps_48000hz.aac",
+        {AAC_1, MEDIA_MIMETYPE_AUDIO_AAC, "bbb_aac_stereo_128kbps_48000hz.aac",
          "bbb_aac_stereo_128kbps_48000hz.info", 48000, 2, true},
-        {MEDIA_MIMETYPE_AUDIO_AAC_ADTS, "Mps_2_c2_fr1_Sc1_Dc2_0x03_raw.adts",
+        {AAC_ADTS_1, MEDIA_MIMETYPE_AUDIO_AAC_ADTS, "Mps_2_c2_fr1_Sc1_Dc2_0x03_raw.adts",
          "Mps_2_c2_fr1_Sc1_Dc2_0x03_raw.info", 48000, 2, true},
-        {MEDIA_MIMETYPE_AUDIO_AMR_NB, "sine_amrnb_1ch_12kbps_8000hz.amrnb",
+        {AMR_NB_1, MEDIA_MIMETYPE_AUDIO_AMR_NB, "sine_amrnb_1ch_12kbps_8000hz.amrnb",
          "sine_amrnb_1ch_12kbps_8000hz.info", 8000, 1, true},
-        {MEDIA_MIMETYPE_AUDIO_AMR_WB, "bbb_amrwb_1ch_14kbps_16000hz.amrwb",
+        {AMR_WB_1, MEDIA_MIMETYPE_AUDIO_AMR_WB, "bbb_amrwb_1ch_14kbps_16000hz.amrwb",
          "bbb_amrwb_1ch_14kbps_16000hz.info", 16000, 1, true},
-        {MEDIA_MIMETYPE_AUDIO_VORBIS, "bbb_vorbis_stereo_128kbps_48000hz.vorbis",
-         "bbb_vorbis_stereo_128kbps_48000hz.info", 48000, 2, true},
-        {MEDIA_MIMETYPE_AUDIO_FLAC, "bbb_flac_stereo_680kbps_48000hz.flac",
+        {FLAC_1, MEDIA_MIMETYPE_AUDIO_FLAC, "bbb_flac_stereo_680kbps_48000hz.flac",
          "bbb_flac_stereo_680kbps_48000hz.info", 48000, 2, true},
-        {MEDIA_MIMETYPE_VIDEO_VP9, "bbb_vp9_176x144_285kbps_60fps.vp9",
-         "bbb_vp9_176x144_285kbps_60fps.info", 176, 144, false},
-        {MEDIA_MIMETYPE_VIDEO_VP8, "bbb_vp8_176x144_240kbps_60fps.vp8",
-         "bbb_vp8_176x144_240kbps_60fps.info", 176, 144, false},
-        {MEDIA_MIMETYPE_VIDEO_AVC, "bbb_avc_176x144_300kbps_60fps.h264",
+        {OPUS_1, MEDIA_MIMETYPE_AUDIO_OPUS, "bbb_opus_stereo_128kbps_48000hz.opus",
+         "bbb_opus_stereo_128kbps_48000hz.info", 48000, 2, true},
+        {VORBIS_1, MEDIA_MIMETYPE_AUDIO_VORBIS, "bbb_vorbis_stereo_128kbps_48000hz.vorbis",
+         "bbb_vorbis_stereo_128kbps_48000hz.info", 48000, 2, true},
+
+        {AV1_1, MEDIA_MIMETYPE_VIDEO_AV1, "bbb_av1_176_144.av1", "bbb_av1_176_144.info", 176, 144,
+         false},
+        {AVC_1, MEDIA_MIMETYPE_VIDEO_AVC, "bbb_avc_176x144_300kbps_60fps.h264",
          "bbb_avc_176x144_300kbps_60fps.info", 176, 144, false},
-        {MEDIA_MIMETYPE_VIDEO_HEVC, "bbb_hevc_176x144_176kbps_60fps.hevc",
-         "bbb_hevc_176x144_176kbps_60fps.info", 176, 144, false},
-        {MEDIA_MIMETYPE_VIDEO_AV1, "bbb_av1_176_144.av1", "bbb_av1_176_144.info", 176, 144, false},
-        {MEDIA_MIMETYPE_VIDEO_H263, "bbb_h263_352x288_300kbps_12fps.h263",
+        {H263_1, MEDIA_MIMETYPE_VIDEO_H263, "bbb_h263_352x288_300kbps_12fps.h263",
          "bbb_h263_352x288_300kbps_12fps.info", 352, 288, false},
-        {MEDIA_MIMETYPE_VIDEO_MPEG4, "bbb_mpeg4_352x288_512kbps_30fps.m4v",
+        {HEVC_1, MEDIA_MIMETYPE_VIDEO_HEVC, "bbb_hevc_176x144_176kbps_60fps.hevc",
+         "bbb_hevc_176x144_176kbps_60fps.info", 176, 144, false},
+        {MPEG4_1, MEDIA_MIMETYPE_VIDEO_MPEG4, "bbb_mpeg4_352x288_512kbps_30fps.m4v",
          "bbb_mpeg4_352x288_512kbps_30fps.info", 352, 288, false},
+        {VP8_1, MEDIA_MIMETYPE_VIDEO_VP8, "bbb_vp8_176x144_240kbps_60fps.vp8",
+         "bbb_vp8_176x144_240kbps_60fps.info", 176, 144, false},
+        {VP9_1, MEDIA_MIMETYPE_VIDEO_VP9, "bbb_vp9_176x144_285kbps_60fps.vp9",
+         "bbb_vp9_176x144_285kbps_60fps.info", 176, 144, false},
+
+        {HEIC_1, MEDIA_MIMETYPE_IMAGE_ANDROID_HEIC, "bbb_hevc_176x144_176kbps_60fps.hevc",
+         "bbb_heic_176x144_176kbps_60fps.info", 176, 144, false},
 };
 
 class WriterTest {
   public:
-    WriterTest() : mWriter(nullptr), mFileMeta(nullptr), mCurrentTrack(nullptr) {}
+    WriterTest() : mWriter(nullptr), mFileMeta(nullptr) {}
 
     ~WriterTest() {
         if (mFileMeta) {
             mFileMeta.clear();
             mFileMeta = nullptr;
         }
-        if (mCurrentTrack) {
-            mCurrentTrack->stop();
-            mCurrentTrack.clear();
-            mCurrentTrack = nullptr;
-        }
         if (mWriter) {
             mWriter.clear();
             mWriter = nullptr;
         }
-        mBufferInfo.clear();
-        if (mInputStream.is_open()) mInputStream.close();
         if (gEnv->cleanUp()) remove(OUTPUT_FILE_NAME);
+
+        for (int32_t idx = 0; idx < kMaxTrackCount; idx++) {
+            mBufferInfo[idx].clear();
+            if (mCurrentTrack[idx]) {
+                mCurrentTrack[idx]->stop();
+                mCurrentTrack[idx].clear();
+                mCurrentTrack[idx] = nullptr;
+            }
+            if (mInputStream[idx].is_open()) mInputStream[idx].close();
+        }
     }
 
     void setupWriterType(string writerFormat) {
-        mNumCsds = 0;
-        mInputFrameId = 0;
         mWriterName = unknown_comp;
         mDisableTest = false;
         static const std::map<std::string, standardWriters> mapWriter = {
@@ -128,11 +158,11 @@
         }
     }
 
-    void getInputBufferInfo(string inputFileName, string inputInfo);
+    void getInputBufferInfo(string inputFileName, string inputInfo, int32_t idx = 0);
 
     int32_t createWriter(int32_t fd);
 
-    int32_t addWriterSource(bool isAudio, configFormat params);
+    int32_t addWriterSource(bool isAudio, configFormat params, int32_t idx = 0);
 
     enum standardWriters {
         OGG,
@@ -149,38 +179,42 @@
     standardWriters mWriterName;
     sp<MediaWriter> mWriter;
     sp<MetaData> mFileMeta;
-    sp<MediaAdapter> mCurrentTrack;
+    sp<MediaAdapter> mCurrentTrack[kMaxTrackCount]{};
 
     bool mDisableTest;
-    int32_t mNumCsds;
-    int32_t mInputFrameId;
-    ifstream mInputStream;
-    vector<BufferInfo> mBufferInfo;
+    int32_t mNumCsds[kMaxTrackCount]{};
+    int32_t mInputFrameId[kMaxTrackCount]{};
+    ifstream mInputStream[kMaxTrackCount]{};
+    vector<BufferInfo> mBufferInfo[kMaxTrackCount];
 };
 
-class WriteFunctionalityTest : public WriterTest,
-                               public ::testing::TestWithParam<pair<string, int32_t>> {
+class WriteFunctionalityTest
+    : public WriterTest,
+      public ::testing::TestWithParam<tuple<string /* writerFormat*/, inputId /* inputId0*/,
+                                            inputId /* inputId1*/, float /* BufferInterval*/>> {
   public:
-    virtual void SetUp() override { setupWriterType(GetParam().first); }
+    virtual void SetUp() override { setupWriterType(get<0>(GetParam())); }
 };
 
-void WriterTest::getInputBufferInfo(string inputFileName, string inputInfo) {
+void WriterTest::getInputBufferInfo(string inputFileName, string inputInfo, int32_t idx) {
     std::ifstream eleInfo;
     eleInfo.open(inputInfo.c_str());
     ASSERT_EQ(eleInfo.is_open(), true);
     int32_t bytesCount = 0;
     uint32_t flags = 0;
     int64_t timestamp = 0;
+    int32_t numCsds = 0;
     while (1) {
         if (!(eleInfo >> bytesCount)) break;
         eleInfo >> flags;
         eleInfo >> timestamp;
-        mBufferInfo.push_back({bytesCount, flags, timestamp});
-        if (flags == CODEC_CONFIG_FLAG) mNumCsds++;
+        mBufferInfo[idx].push_back({bytesCount, flags, timestamp});
+        if (flags == CODEC_CONFIG_FLAG) numCsds++;
     }
     eleInfo.close();
-    mInputStream.open(inputFileName.c_str(), std::ifstream::binary);
-    ASSERT_EQ(mInputStream.is_open(), true);
+    mNumCsds[idx] = numCsds;
+    mInputStream[idx].open(inputFileName.c_str(), std::ifstream::binary);
+    ASSERT_EQ(mInputStream[idx].is_open(), true);
 }
 
 int32_t WriterTest::createWriter(int32_t fd) {
@@ -226,10 +260,10 @@
     return 0;
 }
 
-int32_t WriterTest::addWriterSource(bool isAudio, configFormat params) {
-    if (mInputFrameId) return -1;
+int32_t WriterTest::addWriterSource(bool isAudio, configFormat params, int32_t idx) {
+    if (mInputFrameId[idx]) return -1;
     sp<AMessage> format = new AMessage;
-    if (mInputStream.is_open()) {
+    if (mInputStream[idx].is_open()) {
         format->setString("mime", params.mime);
         if (isAudio) {
             format->setInt32("channel-count", params.channelCount);
@@ -238,25 +272,34 @@
             format->setInt32("width", params.width);
             format->setInt32("height", params.height);
         }
-
-        int32_t status =
-                writeHeaderBuffers(mInputStream, mBufferInfo, mInputFrameId, format, mNumCsds);
-        if (status != 0) return -1;
+        if (mNumCsds[idx]) {
+            int32_t status = writeHeaderBuffers(mInputStream[idx], mBufferInfo[idx],
+                                                mInputFrameId[idx], format, mNumCsds[idx]);
+            if (status != 0) return -1;
+        }
     }
+
     sp<MetaData> trackMeta = new MetaData;
     convertMessageToMetaData(format, trackMeta);
-    mCurrentTrack = new MediaAdapter(trackMeta);
-    if (mCurrentTrack == nullptr) {
+    mCurrentTrack[idx] = new MediaAdapter(trackMeta);
+    if (mCurrentTrack[idx] == nullptr) {
         ALOGE("MediaAdapter returned nullptr");
         return -1;
     }
-    status_t result = mWriter->addSource(mCurrentTrack);
+    status_t result = mWriter->addSource(mCurrentTrack[idx]);
     return result;
 }
 
 void getFileDetails(string &inputFilePath, string &info, configFormat &params, bool &isAudio,
-                    int32_t streamIndex = 0) {
-    if (streamIndex >= sizeof(kInputData) / sizeof(kInputData[0])) {
+                    inputId inpId) {
+    int32_t inputDataSize = sizeof(kInputData) / sizeof(kInputData[0]);
+    int32_t streamIndex = 0;
+    for (; streamIndex < inputDataSize; streamIndex++) {
+        if (inpId == kInputData[streamIndex].inpId) {
+            break;
+        }
+    }
+    if (streamIndex == inputDataSize) {
         return;
     }
     inputFilePath += kInputData[streamIndex].inputFile;
@@ -284,14 +327,14 @@
 
     // Creating writer within a test scope. Destructor should be called when the test ends
     ASSERT_EQ((status_t)OK, createWriter(fd))
-            << "Failed to create writer for output format:" << GetParam().first;
+            << "Failed to create writer for output format:" << get<0>(GetParam());
 }
 
 TEST_P(WriteFunctionalityTest, WriterTest) {
     if (mDisableTest) return;
     ALOGV("Checks if for a given input, a valid muxed file has been created or not");
 
-    string writerFormat = GetParam().first;
+    string writerFormat = get<0>(GetParam());
     string outputFile = OUTPUT_FILE_NAME;
     int32_t fd =
             open(outputFile.c_str(), O_CREAT | O_LARGEFILE | O_TRUNC | O_RDWR, S_IRUSR | S_IWUSR);
@@ -300,25 +343,49 @@
     int32_t status = createWriter(fd);
     ASSERT_EQ((status_t)OK, status) << "Failed to create writer for output format:" << writerFormat;
 
-    string inputFile = gEnv->getRes();
-    string inputInfo = gEnv->getRes();
-    configFormat param;
-    bool isAudio;
-    int32_t inputFileIdx = GetParam().second;
-    getFileDetails(inputFile, inputInfo, param, isAudio, inputFileIdx);
-    ASSERT_NE(inputFile.compare(gEnv->getRes()), 0) << "No input file specified";
+    inputId inpId[] = {get<1>(GetParam()), get<2>(GetParam())};
+    ASSERT_NE(inpId[0], UNUSED_ID) << "Test expects first inputId to be a valid id";
 
-    ASSERT_NO_FATAL_FAILURE(getInputBufferInfo(inputFile, inputInfo));
-    status = addWriterSource(isAudio, param);
-    ASSERT_EQ((status_t)OK, status) << "Failed to add source for " << writerFormat << "Writer";
+    int32_t numTracks = 1;
+    if (inpId[1] != UNUSED_ID) {
+        numTracks++;
+    }
+    for (int32_t idx = 0; idx < numTracks; idx++) {
+        string inputFile = gEnv->getRes();
+        string inputInfo = gEnv->getRes();
+        configFormat param;
+        bool isAudio;
+        getFileDetails(inputFile, inputInfo, param, isAudio, inpId[idx]);
+        ASSERT_NE(inputFile.compare(gEnv->getRes()), 0) << "No input file specified";
+
+        ASSERT_NO_FATAL_FAILURE(getInputBufferInfo(inputFile, inputInfo, idx));
+        status = addWriterSource(isAudio, param, idx);
+        ASSERT_EQ((status_t)OK, status) << "Failed to add source for " << writerFormat << "Writer";
+    }
 
     status = mWriter->start(mFileMeta.get());
     ASSERT_EQ((status_t)OK, status);
-    status = sendBuffersToWriter(mInputStream, mBufferInfo, mInputFrameId, mCurrentTrack, 0,
-                                 mBufferInfo.size());
-    ASSERT_EQ((status_t)OK, status) << writerFormat << " writer failed";
-    mCurrentTrack->stop();
+    float interval = get<3>(GetParam());
+    ASSERT_LE(interval, 1.0f) << "Buffer interval invalid. Should be less than or equal to 1.0";
 
+    size_t range = 0;
+    int32_t loopCount = 0;
+    int32_t offset[kMaxTrackCount]{};
+    while (loopCount < ceil(1.0 / interval)) {
+        for (int32_t idx = 0; idx < numTracks; idx++) {
+            range = mBufferInfo[idx].size() * interval;
+            status = sendBuffersToWriter(mInputStream[idx], mBufferInfo[idx], mInputFrameId[idx],
+                                         mCurrentTrack[idx], offset[idx], range);
+            ASSERT_EQ((status_t)OK, status) << writerFormat << " writer failed";
+            offset[idx] += range;
+        }
+        loopCount++;
+    }
+    for (int32_t idx = 0; idx < kMaxTrackCount; idx++) {
+        if (mCurrentTrack[idx]) {
+            mCurrentTrack[idx]->stop();
+        }
+    }
     status = mWriter->stop();
     ASSERT_EQ((status_t)OK, status) << "Failed to stop the writer";
     close(fd);
@@ -328,7 +395,7 @@
     if (mDisableTest) return;
     ALOGV("Validates the pause() api of writers");
 
-    string writerFormat = GetParam().first;
+    string writerFormat = get<0>(GetParam());
     string outputFile = OUTPUT_FILE_NAME;
     int32_t fd =
             open(outputFile.c_str(), O_CREAT | O_LARGEFILE | O_TRUNC | O_RDWR, S_IRUSR | S_IWUSR);
@@ -341,8 +408,10 @@
     string inputInfo = gEnv->getRes();
     configFormat param;
     bool isAudio;
-    int32_t inputFileIdx = GetParam().second;
-    getFileDetails(inputFile, inputInfo, param, isAudio, inputFileIdx);
+    inputId inpId = get<1>(GetParam());
+    ASSERT_NE(inpId, UNUSED_ID) << "Test expects first inputId to be a valid id";
+
+    getFileDetails(inputFile, inputInfo, param, isAudio, inpId);
     ASSERT_NE(inputFile.compare(gEnv->getRes()), 0) << "No input file specified";
 
     ASSERT_NO_FATAL_FAILURE(getInputBufferInfo(inputFile, inputInfo));
@@ -351,8 +420,8 @@
 
     status = mWriter->start(mFileMeta.get());
     ASSERT_EQ((status_t)OK, status);
-    status = sendBuffersToWriter(mInputStream, mBufferInfo, mInputFrameId, mCurrentTrack, 0,
-                                 mBufferInfo.size() / 4);
+    status = sendBuffersToWriter(mInputStream[0], mBufferInfo[0], mInputFrameId[0],
+                                 mCurrentTrack[0], 0, mBufferInfo[0].size() / 4);
     ASSERT_EQ((status_t)OK, status) << writerFormat << " writer failed";
 
     bool isPaused = false;
@@ -362,19 +431,19 @@
         isPaused = true;
     }
     // In the pause state, writers shouldn't write anything. Testing the writers for the same
-    int32_t numFramesPaused = mBufferInfo.size() / 4;
-    status = sendBuffersToWriter(mInputStream, mBufferInfo, mInputFrameId, mCurrentTrack,
-                                  mInputFrameId, numFramesPaused, isPaused);
+    int32_t numFramesPaused = mBufferInfo[0].size() / 4;
+    status = sendBuffersToWriter(mInputStream[0], mBufferInfo[0], mInputFrameId[0],
+                                 mCurrentTrack[0], mInputFrameId[0], numFramesPaused, isPaused);
     ASSERT_EQ((status_t)OK, status) << writerFormat << " writer failed";
 
     if (isPaused) {
         status = mWriter->start(mFileMeta.get());
         ASSERT_EQ((status_t)OK, status);
     }
-    status = sendBuffersToWriter(mInputStream, mBufferInfo, mInputFrameId, mCurrentTrack,
-                                  mInputFrameId, mBufferInfo.size());
+    status = sendBuffersToWriter(mInputStream[0], mBufferInfo[0], mInputFrameId[0],
+                                 mCurrentTrack[0], mInputFrameId[0], mBufferInfo[0].size());
     ASSERT_EQ((status_t)OK, status) << writerFormat << " writer failed";
-    mCurrentTrack->stop();
+    mCurrentTrack[0]->stop();
 
     status = mWriter->stop();
     ASSERT_EQ((status_t)OK, status) << "Failed to stop the writer";
@@ -392,7 +461,7 @@
             open(outputFile.c_str(), O_CREAT | O_LARGEFILE | O_TRUNC | O_RDWR, S_IRUSR | S_IWUSR);
     ASSERT_GE(fd, 0) << "Failed to open output file to dump writer's data";
 
-    string writerFormat = GetParam().first;
+    string writerFormat = get<0>(GetParam());
     int32_t status = createWriter(fd);
     ASSERT_EQ(status, (status_t)OK) << "Failed to create writer for output format:" << writerFormat;
 
@@ -400,8 +469,10 @@
     string inputInfo = gEnv->getRes();
     configFormat param;
     bool isAudio;
-    int32_t inputFileIdx = GetParam().second;
-    getFileDetails(inputFile, inputInfo, param, isAudio, inputFileIdx);
+    inputId inpId = get<1>(GetParam());
+    ASSERT_NE(inpId, UNUSED_ID) << "Test expects first inputId to be a valid id";
+
+    getFileDetails(inputFile, inputInfo, param, isAudio, inpId);
     ASSERT_NE(inputFile.compare(gEnv->getRes()), 0) << "No input file specified";
 
     ASSERT_NO_FATAL_FAILURE(getInputBufferInfo(inputFile, inputInfo));
@@ -418,8 +489,8 @@
         mWriter->start(mFileMeta.get());
     }
 
-    status = sendBuffersToWriter(mInputStream, mBufferInfo, mInputFrameId, mCurrentTrack, 0,
-                              mBufferInfo.size() / 4);
+    status = sendBuffersToWriter(mInputStream[0], mBufferInfo[0], mInputFrameId[0],
+                                 mCurrentTrack[0], 0, mBufferInfo[0].size() / 4);
     ASSERT_EQ((status_t)OK, status) << writerFormat << " writer failed";
 
     for (int32_t count = 0; count < kMaxCount; count++) {
@@ -428,20 +499,20 @@
     }
 
     mWriter->pause();
-    int32_t numFramesPaused = mBufferInfo.size() / 4;
-    status = sendBuffersToWriter(mInputStream, mBufferInfo, mInputFrameId, mCurrentTrack,
-                              mInputFrameId, numFramesPaused, true);
+    int32_t numFramesPaused = mBufferInfo[0].size() / 4;
+    status = sendBuffersToWriter(mInputStream[0], mBufferInfo[0], mInputFrameId[0],
+                                 mCurrentTrack[0], mInputFrameId[0], numFramesPaused, true);
     ASSERT_EQ((status_t)OK, status) << writerFormat << " writer failed";
 
     for (int32_t count = 0; count < kMaxCount; count++) {
         mWriter->start(mFileMeta.get());
     }
 
-    status = sendBuffersToWriter(mInputStream, mBufferInfo, mInputFrameId, mCurrentTrack,
-                              mInputFrameId, mBufferInfo.size());
+    status = sendBuffersToWriter(mInputStream[0], mBufferInfo[0], mInputFrameId[0],
+                                 mCurrentTrack[0], mInputFrameId[0], mBufferInfo[0].size());
     ASSERT_EQ((status_t)OK, status) << writerFormat << " writer failed";
 
-    mCurrentTrack->stop();
+    mCurrentTrack[0]->stop();
 
     // first stop should succeed.
     status = mWriter->stop();
@@ -454,15 +525,13 @@
     close(fd);
 }
 
-class ListenerTest : public WriterTest,
-                     public ::testing::TestWithParam<
-                             tuple<string /* writerFormat*/, int32_t /* inputFileIdx*/,
-                                   float /* FileSizeLimit*/, float /* FileDurationLimit*/>> {
+class ListenerTest
+    : public WriterTest,
+      public ::testing::TestWithParam<tuple<
+              string /* writerFormat*/, inputId /* inputId0*/, inputId /* inputId1*/,
+              float /* FileSizeLimit*/, float /* FileDurationLimit*/, float /* BufferInterval*/>> {
   public:
-    virtual void SetUp() override {
-        tuple<string, int32_t, float, float> params = GetParam();
-        setupWriterType(get<0>(params));
-    }
+    virtual void SetUp() override { setupWriterType(get<0>(GetParam())); }
 };
 
 TEST_P(ListenerTest, SetMaxFileLimitsTest) {
@@ -470,8 +539,7 @@
     if (mDisableTest || mWriterName != MPEG4) return;
     ALOGV("Validates writer when max file limits are set");
 
-    tuple<string, int32_t, float, float> params = GetParam();
-    string writerFormat = get<0>(params);
+    string writerFormat = get<0>(GetParam());
     string outputFile = OUTPUT_FILE_NAME;
     int32_t fd =
             open(outputFile.c_str(), O_CREAT | O_LARGEFILE | O_TRUNC | O_RDWR, S_IRUSR | S_IWUSR);
@@ -480,30 +548,42 @@
     int32_t status = createWriter(fd);
     ASSERT_EQ((status_t)OK, status) << "Failed to create writer for output format:" << writerFormat;
 
-    string inputFile = gEnv->getRes();
-    string inputInfo = gEnv->getRes();
-    configFormat param;
-    bool isAudio;
-    int32_t inputFileIdx = get<1>(params);
-    getFileDetails(inputFile, inputInfo, param, isAudio, inputFileIdx);
-    ASSERT_NE(inputFile.compare(gEnv->getRes()), 0) << "No input file specified";
+    inputId inpId[] = {get<1>(GetParam()), get<2>(GetParam())};
+    ASSERT_NE(inpId[0], UNUSED_ID) << "Test expects first inputId to be a valid id";
 
-    ASSERT_NO_FATAL_FAILURE(getInputBufferInfo(inputFile, inputInfo));
-    status = addWriterSource(isAudio, param);
-    ASSERT_EQ((status_t)OK, status) << "Failed to add source for " << writerFormat << "Writer";
+    size_t inputFileSize = 0;
+    int64_t lastFrameTimeStampUs = INT_MAX;
+    int32_t numTracks = 1;
+    if (inpId[1] != UNUSED_ID) {
+        numTracks++;
+    }
+    for (int32_t idx = 0; idx < numTracks; idx++) {
+        string inputFile = gEnv->getRes();
+        string inputInfo = gEnv->getRes();
+        configFormat param;
+        bool isAudio;
+        getFileDetails(inputFile, inputInfo, param, isAudio, inpId[idx]);
+        ASSERT_NE(inputFile.compare(gEnv->getRes()), 0) << "No input file specified";
 
-    // Read file properties
-    struct stat buf;
-    status = stat(inputFile.c_str(), &buf);
-    ASSERT_EQ(0, status);
+        ASSERT_NO_FATAL_FAILURE(getInputBufferInfo(inputFile, inputInfo, idx));
+        status = addWriterSource(isAudio, param, idx);
+        ASSERT_EQ((status_t)OK, status) << "Failed to add source for " << writerFormat << "Writer";
 
-    float fileSizeLimit = get<2>(params);
-    float fileDurationLimit = get<3>(params);
+        // Read file properties
+        struct stat buf;
+        status = stat(inputFile.c_str(), &buf);
+        ASSERT_EQ(0, status);
+
+        inputFileSize += buf.st_size;
+        if (lastFrameTimeStampUs > mBufferInfo[idx][mBufferInfo[idx].size() - 1].timeUs) {
+            lastFrameTimeStampUs = mBufferInfo[idx][mBufferInfo[idx].size() - 1].timeUs;
+        }
+    }
+
+    float fileSizeLimit = get<3>(GetParam());
+    float fileDurationLimit = get<4>(GetParam());
     int64_t maxFileSize = 0;
     int64_t maxFileDuration = 0;
-
-    size_t inputFileSize = buf.st_size;
-    int64_t lastFrameTimeStampUs = mBufferInfo[mBufferInfo.size() - 1].timeUs;
     if (fileSizeLimit > 0) {
         maxFileSize = (int64_t)(fileSizeLimit * inputFileSize);
         mWriter->setMaxFileSize(maxFileSize);
@@ -518,14 +598,33 @@
 
     mWriter->setListener(listener);
     status = mWriter->start(mFileMeta.get());
-
     ASSERT_EQ((status_t)OK, status);
-    status = sendBuffersToWriter(mInputStream, mBufferInfo, mInputFrameId, mCurrentTrack, 0,
-                                 mBufferInfo.size(), false, listener);
-    ASSERT_EQ((status_t)OK, status) << writerFormat << " writer failed";
+
+    float interval = get<5>(GetParam());
+    ASSERT_LE(interval, 1.0f) << "Buffer interval invalid. Should be less than or equal to 1.0";
+
+    size_t range = 0;
+    int32_t loopCount = 0;
+    int32_t offset[kMaxTrackCount]{};
+    while (loopCount < ceil(1.0 / interval)) {
+        for (int32_t idx = 0; idx < numTracks; idx++) {
+            range = mBufferInfo[idx].size() * interval;
+            status = sendBuffersToWriter(mInputStream[idx], mBufferInfo[idx], mInputFrameId[idx],
+                                         mCurrentTrack[idx], offset[idx], range, false, listener);
+            ASSERT_EQ((status_t)OK, status) << writerFormat << " writer failed";
+            offset[idx] += range;
+        }
+        loopCount++;
+    }
+
     ASSERT_TRUE(mWriter->reachedEOS()) << "EOS not signalled.";
 
-    mCurrentTrack->stop();
+    for (int32_t idx = 0; idx < kMaxTrackCount; idx++) {
+        if (mCurrentTrack[idx]) {
+            mCurrentTrack[idx]->stop();
+        }
+    }
+
     status = mWriter->stop();
     ASSERT_EQ((status_t)OK, status) << "Failed to stop the writer";
     close(fd);
@@ -552,23 +651,57 @@
 
 // TODO: (b/150923387)
 // Add WEBM input
-INSTANTIATE_TEST_SUITE_P(
-        ListenerTestAll, ListenerTest,
-        ::testing::Values(make_tuple("ogg", 0, 0.7, 0.3), make_tuple("aac", 1, 0.6, 0.7),
-                          make_tuple("mpeg4", 1, 0.4, 0.3), make_tuple("amrnb", 3, 0.2, 0.6),
-                          make_tuple("amrwb", 4, 0.5, 0.5), make_tuple("mpeg2Ts", 1, 0.2, 1)));
+INSTANTIATE_TEST_SUITE_P(ListenerTestAll, ListenerTest,
+                         ::testing::Values(make_tuple("aac", AAC_1, UNUSED_ID, 0.6, 0.7, 1),
+                                           make_tuple("amrnb", AMR_NB_1, UNUSED_ID, 0.2, 0.6, 1),
+                                           make_tuple("amrwb", AMR_WB_1, UNUSED_ID, 0.5, 0.5, 1),
+                                           make_tuple("mpeg2Ts", AAC_1, UNUSED_ID, 0.2, 1, 1),
+                                           make_tuple("mpeg4", AAC_1, UNUSED_ID, 0.4, 0.3, 0.25),
+                                           make_tuple("ogg", OPUS_1, UNUSED_ID, 0.7, 0.3, 1)));
 
 // TODO: (b/144476164)
 // Add AAC_ADTS, FLAC, AV1 input
-INSTANTIATE_TEST_SUITE_P(WriterTestAll, WriteFunctionalityTest,
-                         ::testing::Values(make_pair("ogg", 0), make_pair("webm", 0),
-                                           make_pair("aac", 1), make_pair("mpeg4", 1),
-                                           make_pair("amrnb", 3), make_pair("amrwb", 4),
-                                           make_pair("webm", 5), make_pair("webm", 7),
-                                           make_pair("webm", 8), make_pair("mpeg4", 9),
-                                           make_pair("mpeg4", 10), make_pair("mpeg4", 12),
-                                           make_pair("mpeg4", 13), make_pair("mpeg2Ts", 1),
-                                           make_pair("mpeg2Ts", 9)));
+INSTANTIATE_TEST_SUITE_P(
+        WriterTestAll, WriteFunctionalityTest,
+        ::testing::Values(
+                make_tuple("aac", AAC_1, UNUSED_ID, 1),
+
+                make_tuple("amrnb", AMR_NB_1, UNUSED_ID, 1),
+                make_tuple("amrwb", AMR_WB_1, UNUSED_ID, 1),
+
+                // TODO(b/144902018): Enable test for mpeg2ts
+                // make_tuple("mpeg2Ts", AAC_1, UNUSED_ID, 1),
+                // make_tuple("mpeg2Ts", AVC_1, UNUSED_ID, 1),
+                // TODO(b/156355857): Add multitrack for mpeg2ts
+                // make_tuple("mpeg2Ts", AAC_1, AVC_1, 0.50),
+                // make_tuple("mpeg2Ts", AVC_1, AAC_1, 0.25),
+
+                make_tuple("mpeg4", AAC_1, UNUSED_ID, 1),
+                make_tuple("mpeg4", AMR_NB_1, UNUSED_ID, 1),
+                make_tuple("mpeg4", AMR_WB_1, UNUSED_ID, 1),
+                make_tuple("mpeg4", AVC_1, UNUSED_ID, 1),
+                make_tuple("mpeg4", H263_1, UNUSED_ID, 1),
+                make_tuple("mpeg4", HEIC_1, UNUSED_ID, 1),
+                make_tuple("mpeg4", HEVC_1, UNUSED_ID, 1),
+                make_tuple("mpeg4", MPEG4_1, UNUSED_ID, 1),
+                make_tuple("mpeg4", AAC_1, AVC_1, 0.25),
+                make_tuple("mpeg4", AVC_1, AAC_1, 0.75),
+                make_tuple("mpeg4", AMR_WB_1, AAC_1, 0.75),
+                make_tuple("mpeg4", HEVC_1, AMR_WB_1, 0.25),
+                make_tuple("mpeg4", H263_1, AMR_NB_1, 0.50),
+                make_tuple("mpeg4", MPEG4_1, AAC_1, 0.75),
+                make_tuple("mpeg4", AMR_NB_1, AMR_WB_1, 0.25),
+                make_tuple("mpeg4", H263_1, AMR_NB_1, 0.50),
+                make_tuple("mpeg4", MPEG4_1, HEVC_1, 0.75),
+
+                make_tuple("ogg", OPUS_1, UNUSED_ID, 1),
+
+                make_tuple("webm", OPUS_1, UNUSED_ID, 1),
+                make_tuple("webm", VORBIS_1, UNUSED_ID, 1),
+                make_tuple("webm", VP8_1, UNUSED_ID, 1),
+                make_tuple("webm", VP9_1, UNUSED_ID, 1),
+                make_tuple("webm", VP8_1, OPUS_1, 0.50),
+                make_tuple("webm", VORBIS_1, VP8_1, 0.25)));
 
 int main(int argc, char **argv) {
     gEnv = new WriterTestEnvironment();
diff --git a/media/libstagefright/tests/writer/WriterUtility.h b/media/libstagefright/tests/writer/WriterUtility.h
index 5e19973..5e79298 100644
--- a/media/libstagefright/tests/writer/WriterUtility.h
+++ b/media/libstagefright/tests/writer/WriterUtility.h
@@ -31,6 +31,7 @@
 
 #define CODEC_CONFIG_FLAG 32
 
+constexpr uint32_t kMaxTrackCount = 2;
 constexpr uint32_t kMaxCSDStrlen = 16;
 constexpr uint32_t kMaxCount = 20;
 
diff --git a/services/audioflinger/AudioStreamOut.cpp b/services/audioflinger/AudioStreamOut.cpp
index d13cb8f..7e06096 100644
--- a/services/audioflinger/AudioStreamOut.cpp
+++ b/services/audioflinger/AudioStreamOut.cpp
@@ -164,6 +164,10 @@
         stream = outStream;
         mHalFormatHasProportionalFrames = audio_has_proportional_frames(config->format);
         status = stream->getFrameSize(&mHalFrameSize);
+        LOG_ALWAYS_FATAL_IF(status != OK, "Error retrieving frame size from HAL: %d", status);
+        LOG_ALWAYS_FATAL_IF(mHalFrameSize <= 0, "Error frame size was %zu but must be greater than"
+                " zero", mHalFrameSize);
+
     }
 
     return status;
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index 7e63851..b433cf9 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -6061,10 +6061,6 @@
     bool trackPaused = false;
     bool trackStopped = false;
 
-    if ((mType == DIRECT) && audio_is_linear_pcm(mFormat) && !usesHwAvSync()) {
-        return !mStandby;
-    }
-
     // do not put the HAL in standby when paused. AwesomePlayer clear the offloaded AudioTrack
     // after a timeout and we will enter standby then.
     if (mTracks.size() > 0) {
@@ -8435,13 +8431,14 @@
     }
     result = mInput->stream->getFrameSize(&mFrameSize);
     LOG_ALWAYS_FATAL_IF(result != OK, "Error retrieving frame size from HAL: %d", result);
+    LOG_ALWAYS_FATAL_IF(mFrameSize <= 0, "Error frame size was %zu but must be greater than zero",
+            mFrameSize);
     result = mInput->stream->getBufferSize(&mBufferSize);
     LOG_ALWAYS_FATAL_IF(result != OK, "Error retrieving buffer size from HAL: %d", result);
     mFrameCount = mBufferSize / mFrameSize;
-    ALOGV("%p RecordThread params: mChannelCount=%u, mFormat=%#x, mFrameSize=%lld, "
-            "mBufferSize=%lld, mFrameCount=%lld",
-            this, mChannelCount, mFormat, (long long)mFrameSize, (long long)mBufferSize,
-            (long long)mFrameCount);
+    ALOGV("%p RecordThread params: mChannelCount=%u, mFormat=%#x, mFrameSize=%zu, "
+            "mBufferSize=%zu, mFrameCount=%zu",
+            this, mChannelCount, mFormat, mFrameSize, mBufferSize, mFrameCount);
     // This is the formula for calculating the temporary buffer size.
     // With 7 HAL buffers, we can guarantee ability to down-sample the input by ratio of 6:1 to
     // 1 full output buffer, regardless of the alignment of the available input.
@@ -9017,6 +9014,8 @@
     LOG_ALWAYS_FATAL_IF(!audio_is_linear_pcm(mFormat), "HAL format %#x is not linear pcm", mFormat);
     result = mHalStream->getFrameSize(&mFrameSize);
     LOG_ALWAYS_FATAL_IF(result != OK, "Error retrieving frame size from HAL: %d", result);
+    LOG_ALWAYS_FATAL_IF(mFrameSize <= 0, "Error frame size was %zu but must be greater than zero",
+            mFrameSize);
     result = mHalStream->getBufferSize(&mBufferSize);
     LOG_ALWAYS_FATAL_IF(result != OK, "Error retrieving buffer size from HAL: %d", result);
     mFrameCount = mBufferSize / mFrameSize;
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index 3730c34..c5c13e9 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -5770,15 +5770,6 @@
     DeviceVector filteredDevices = outputDesc->filterSupportedDevices(devices);
     DeviceVector prevDevices = outputDesc->devices();
 
-    // no need to proceed if new device is not AUDIO_DEVICE_NONE and not supported by current
-    // output profile or if new device is not supported AND previous device(s) is(are) still
-    // available (otherwise reset device must be done on the output)
-    if (!devices.isEmpty() && filteredDevices.isEmpty() &&
-            !mAvailableOutputDevices.filter(prevDevices).empty()) {
-        ALOGV("%s: unsupported device %s for output", __func__, devices.toString().c_str());
-        return 0;
-    }
-
     ALOGV("setOutputDevices() prevDevice %s", prevDevices.toString().c_str());
 
     if (!filteredDevices.isEmpty()) {
@@ -5793,6 +5784,17 @@
         muteWaitMs = 0;
     }
 
+    // no need to proceed if new device is not AUDIO_DEVICE_NONE and not supported by current
+    // output profile or if new device is not supported AND previous device(s) is(are) still
+    // available (otherwise reset device must be done on the output)
+    if (!devices.isEmpty() && filteredDevices.isEmpty() &&
+            !mAvailableOutputDevices.filter(prevDevices).empty()) {
+        ALOGV("%s: unsupported device %s for output", __func__, devices.toString().c_str());
+        // restore previous device after evaluating strategy mute state
+        outputDesc->setDevices(prevDevices);
+        return muteWaitMs;
+    }
+
     // Do not change the routing if:
     //      the requested device is AUDIO_DEVICE_NONE
     //      OR the requested device is the same as current device
diff --git a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
index 9577160..34d07b6 100644
--- a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
+++ b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
@@ -593,7 +593,7 @@
     }
 
     // including successes gets very verbose
-    // but once we cut over to westworld, log them all.
+    // but once we cut over to statsd, log them all.
     if (status != NO_ERROR) {
 
         static constexpr char kAudioPolicy[] = "audiopolicy";
diff --git a/services/camera/libcameraservice/api2/HeicCompositeStream.cpp b/services/camera/libcameraservice/api2/HeicCompositeStream.cpp
index 1a0881f..a63f402 100644
--- a/services/camera/libcameraservice/api2/HeicCompositeStream.cpp
+++ b/services/camera/libcameraservice/api2/HeicCompositeStream.cpp
@@ -620,7 +620,8 @@
         if (mPendingInputFrames.find(mAppSegmentFrameNumbers.front()) == mPendingInputFrames.end()) {
             ALOGE("%s: mPendingInputFrames doesn't contain frameNumber %" PRId64, __FUNCTION__,
                     mAppSegmentFrameNumbers.front());
-            mInputYuvBuffers.erase(it);
+            mInputAppSegmentBuffers.erase(it);
+            mAppSegmentFrameNumbers.pop();
             continue;
         }
 
@@ -664,6 +665,7 @@
             ALOGE("%s: mPendingInputFrames doesn't contain frameNumber %" PRId64, __FUNCTION__,
                     mMainImageFrameNumbers.front());
             mInputYuvBuffers.erase(it);
+            mMainImageFrameNumbers.pop();
             continue;
         }
 
diff --git a/services/mediacodec/Android.bp b/services/mediacodec/Android.bp
index f4c1924..775582a 100644
--- a/services/mediacodec/Android.bp
+++ b/services/mediacodec/Android.bp
@@ -15,18 +15,6 @@
         "libmedia_codecserviceregistrant",
     ],
 
-    target: {
-        android: {
-            product_variables: {
-                malloc_not_svelte: {
-                    // Scudo increases memory footprint, so only enable on
-                    // non-svelte devices.
-                    shared_libs: ["libc_scudo"],
-                },
-            },
-        },
-    },
-
     header_libs: [
         "libmedia_headers",
     ],
diff --git a/services/mediaextractor/Android.bp b/services/mediaextractor/Android.bp
index 05b7d22..03e1e41 100644
--- a/services/mediaextractor/Android.bp
+++ b/services/mediaextractor/Android.bp
@@ -35,17 +35,6 @@
         "liblog",
         "libavservices_minijail",
     ],
-    target: {
-        android: {
-            product_variables: {
-                malloc_not_svelte: {
-                    // Scudo increases memory footprint, so only enable on
-                    // non-svelte devices.
-                    shared_libs: ["libc_scudo"],
-                },
-            },
-        },
-    },
     init_rc: ["mediaextractor.rc"],
 
     cflags: [
diff --git a/services/mediametrics/AudioPowerUsage.cpp b/services/mediametrics/AudioPowerUsage.cpp
index c441110..cca6b41 100644
--- a/services/mediametrics/AudioPowerUsage.cpp
+++ b/services/mediametrics/AudioPowerUsage.cpp
@@ -34,7 +34,7 @@
 #define PROP_AUDIO_METRICS_DISABLED "persist.media.audio_metrics.power_usage_disabled"
 #define AUDIO_METRICS_DISABLED_DEFAULT (false)
 
-// property to set how long to send audio power use metrics data to westworld, default is 24hrs
+// property to set how long to send audio power use metrics data to statsd, default is 24hrs
 #define PROP_AUDIO_METRICS_INTERVAL_HR "persist.media.audio_metrics.interval_hr"
 #define INTERVAL_HR_DEFAULT (24)
 
diff --git a/services/mediametrics/MediaMetricsService.cpp b/services/mediametrics/MediaMetricsService.cpp
index d682fed..48e766e 100644
--- a/services/mediametrics/MediaMetricsService.cpp
+++ b/services/mediametrics/MediaMetricsService.cpp
@@ -185,7 +185,7 @@
     }
 
     if (!isTrusted || item->getTimestamp() == 0) {
-        // Westworld logs two times for events: ElapsedRealTimeNs (BOOTTIME) and
+        // Statsd logs two times for events: ElapsedRealTimeNs (BOOTTIME) and
         // WallClockTimeNs (REALTIME), but currently logs REALTIME to cloud.
         //
         // For consistency and correlation with other logging mechanisms
diff --git a/services/mediametrics/MediaMetricsService.h b/services/mediametrics/MediaMetricsService.h
index d152264..792b7f0 100644
--- a/services/mediametrics/MediaMetricsService.h
+++ b/services/mediametrics/MediaMetricsService.h
@@ -65,7 +65,7 @@
     static nsecs_t roundTime(nsecs_t timeNs);
 
     /**
-     * Returns true if we should use uid for package name when uploading to WestWorld.
+     * Returns true if we should use uid for package name when uploading to statsd.
      */
     static bool useUidForPackage(const std::string& package, const std::string& installer);
 
diff --git a/services/mediatranscoding/Android.bp b/services/mediatranscoding/Android.bp
index 498ce09..bfcf01f 100644
--- a/services/mediatranscoding/Android.bp
+++ b/services/mediatranscoding/Android.bp
@@ -10,6 +10,7 @@
     shared_libs: [
         "libbase",
         "libbinder_ndk",
+        "libcutils",
         "liblog",
         "libmediatranscoding",
         "libutils",
@@ -51,18 +52,6 @@
         "mediatranscoding_aidl_interface-ndk_platform",
     ],
 
-    target: {
-        android: {
-            product_variables: {
-                malloc_not_svelte: {
-                    // Scudo increases memory footprint, so only enable on
-                    // non-svelte devices.
-                    shared_libs: ["libc_scudo"],
-                },
-            },
-        },
-    },
-
     init_rc: ["mediatranscoding.rc"],
 
     cflags: [
diff --git a/services/mediatranscoding/MediaTranscodingService.cpp b/services/mediatranscoding/MediaTranscodingService.cpp
index b843967..2b97c2e 100644
--- a/services/mediatranscoding/MediaTranscodingService.cpp
+++ b/services/mediatranscoding/MediaTranscodingService.cpp
@@ -20,6 +20,8 @@
 
 #include <android/binder_manager.h>
 #include <android/binder_process.h>
+#include <cutils/properties.h>
+#include <media/TranscoderWrapper.h>
 #include <media/TranscodingClientManager.h>
 #include <media/TranscodingJobScheduler.h>
 #include <media/TranscodingUidPolicy.h>
@@ -51,19 +53,14 @@
     }
 }
 
-MediaTranscodingService::MediaTranscodingService()
-      : MediaTranscodingService(std::make_shared<SimulatedTranscoder>(),
-                                std::make_shared<TranscodingUidPolicy>()) {}
-
 MediaTranscodingService::MediaTranscodingService(
-        const std::shared_ptr<TranscoderInterface>& transcoder,
-        const std::shared_ptr<UidPolicyInterface>& uidPolicy)
-      : mJobScheduler(new TranscodingJobScheduler(transcoder, uidPolicy)),
+        const std::shared_ptr<TranscoderInterface>& transcoder)
+      : mUidPolicy(new TranscodingUidPolicy()),
+        mJobScheduler(new TranscodingJobScheduler(transcoder, mUidPolicy)),
         mClientManager(new TranscodingClientManager(mJobScheduler)) {
     ALOGV("MediaTranscodingService is created");
-
     transcoder->setCallback(mJobScheduler);
-    uidPolicy->setCallback(mJobScheduler);
+    mUidPolicy->setCallback(mJobScheduler);
 }
 
 MediaTranscodingService::~MediaTranscodingService() {
@@ -86,8 +83,15 @@
 
 //static
 void MediaTranscodingService::instantiate() {
+    std::shared_ptr<TranscoderInterface> transcoder;
+    if (property_get_bool("debug.transcoding.simulated_transcoder", true)) {
+        transcoder = std::make_shared<SimulatedTranscoder>();
+    } else {
+        transcoder = std::make_shared<TranscoderWrapper>();
+    }
+
     std::shared_ptr<MediaTranscodingService> service =
-            ::ndk::SharedRefBase::make<MediaTranscodingService>();
+            ::ndk::SharedRefBase::make<MediaTranscodingService>(transcoder);
     binder_status_t status =
             AServiceManager_addService(service->asBinder().get(), getServiceName());
     if (status != STATUS_OK) {
diff --git a/services/mediatranscoding/MediaTranscodingService.h b/services/mediatranscoding/MediaTranscodingService.h
index f7ac336..505239c 100644
--- a/services/mediatranscoding/MediaTranscodingService.h
+++ b/services/mediatranscoding/MediaTranscodingService.h
@@ -38,9 +38,7 @@
     static constexpr int32_t kInvalidJobId = -1;
     static constexpr int32_t kInvalidClientId = -1;
 
-    MediaTranscodingService();
-    MediaTranscodingService(const std::shared_ptr<TranscoderInterface>& transcoder,
-                            const std::shared_ptr<UidPolicyInterface>& uidPolicy);
+    MediaTranscodingService(const std::shared_ptr<TranscoderInterface>& transcoder);
     virtual ~MediaTranscodingService();
 
     static void instantiate();
@@ -61,6 +59,7 @@
 
     mutable std::mutex mServiceLock;
 
+    std::shared_ptr<UidPolicyInterface> mUidPolicy;
     std::shared_ptr<TranscodingJobScheduler> mJobScheduler;
     std::shared_ptr<TranscodingClientManager> mClientManager;
 };
diff --git a/services/mediatranscoding/SimulatedTranscoder.cpp b/services/mediatranscoding/SimulatedTranscoder.cpp
index 380d757..5aa325f 100644
--- a/services/mediatranscoding/SimulatedTranscoder.cpp
+++ b/services/mediatranscoding/SimulatedTranscoder.cpp
@@ -47,10 +47,11 @@
     mCallback = cb;
 }
 
-void SimulatedTranscoder::start(ClientIdType clientId, JobIdType jobId,
-                                const TranscodingRequestParcel& request) {
-    if (request.testConfig.processingTotalTimeMs > 0) {
-        mJobProcessingTimeMs = request.testConfig.processingTotalTimeMs;
+void SimulatedTranscoder::start(
+        ClientIdType clientId, JobIdType jobId, const TranscodingRequestParcel& request,
+        const std::shared_ptr<ITranscodingClientCallback>& /*clientCallback*/) {
+    if (request.testConfig.has_value() && request.testConfig->processingTotalTimeMs > 0) {
+        mJobProcessingTimeMs = request.testConfig->processingTotalTimeMs;
     }
     ALOGV("%s: job {%d}: processingTime: %lld", __FUNCTION__, jobId,
           (long long)mJobProcessingTimeMs);
diff --git a/services/mediatranscoding/SimulatedTranscoder.h b/services/mediatranscoding/SimulatedTranscoder.h
index 0eb74c8..030222b 100644
--- a/services/mediatranscoding/SimulatedTranscoder.h
+++ b/services/mediatranscoding/SimulatedTranscoder.h
@@ -51,8 +51,8 @@
 
     // TranscoderInterface
     void setCallback(const std::shared_ptr<TranscoderCallbackInterface>& cb) override;
-    void start(ClientIdType clientId, JobIdType jobId,
-               const TranscodingRequestParcel& request) override;
+    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 stop(ClientIdType clientId, JobIdType jobId) override;
diff --git a/services/mediatranscoding/tests/Android.bp b/services/mediatranscoding/tests/Android.bp
index f37b39e..cec2dc1 100644
--- a/services/mediatranscoding/tests/Android.bp
+++ b/services/mediatranscoding/tests/Android.bp
@@ -27,16 +27,24 @@
     ],
 }
 
-// MediaTranscodingService unit test
+// MediaTranscodingService unit test using simulated transcoder
 cc_test {
-    name: "mediatranscodingservice_tests",
+    name: "mediatranscodingservice_simulated_tests",
     defaults: ["mediatranscodingservice_test_defaults"],
 
-    srcs: ["mediatranscodingservice_tests.cpp"],
+    srcs: ["mediatranscodingservice_simulated_tests.cpp"],
 
     required: [
         ":TranscodingUidPolicy_TestAppA",
         ":TranscodingUidPolicy_TestAppB",
         ":TranscodingUidPolicy_TestAppC",
     ],
+}
+
+// MediaTranscodingService unit test using real transcoder
+cc_test {
+    name: "mediatranscodingservice_real_tests",
+    defaults: ["mediatranscodingservice_test_defaults"],
+
+    srcs: ["mediatranscodingservice_real_tests.cpp"],
 }
\ No newline at end of file
diff --git a/services/mediatranscoding/tests/MediaTranscodingServiceTestHelper.h b/services/mediatranscoding/tests/MediaTranscodingServiceTestHelper.h
new file mode 100644
index 0000000..4259f54
--- /dev/null
+++ b/services/mediatranscoding/tests/MediaTranscodingServiceTestHelper.h
@@ -0,0 +1,415 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+// Unit Test for MediaTranscodingService.
+
+#include <aidl/android/media/BnTranscodingClientCallback.h>
+#include <aidl/android/media/IMediaTranscodingService.h>
+#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/binder_manager.h>
+#include <android/binder_process.h>
+#include <binder/PermissionController.h>
+#include <cutils/multiuser.h>
+#include <fcntl.h>
+#include <gtest/gtest.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <utils/Log.h>
+
+#include <iostream>
+#include <list>
+
+#include "SimulatedTranscoder.h"
+
+namespace android {
+
+namespace media {
+
+using Status = ::ndk::ScopedAStatus;
+using aidl::android::media::BnTranscodingClientCallback;
+using aidl::android::media::IMediaTranscodingService;
+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;
+using aidl::android::media::TranscodingVideoTrackFormat;
+
+constexpr int32_t kClientUseCallingPid = IMediaTranscodingService::USE_CALLING_PID;
+
+constexpr uid_t kClientUid = 5000;
+#define UID(n) (kClientUid + (n))
+
+constexpr int32_t kClientId = 0;
+#define CLIENT(n) (kClientId + (n))
+
+constexpr const char* kClientName = "TestClient";
+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";
+
+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;
+    }
+
+    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 openFileDescriptor(const std::string& in_fileUri, const std::string& in_mode,
+                              ::ndk::ScopedFileDescriptor* _aidl_return) override {
+        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;
+            // User R+W permission.
+            constexpr int kFileMode = S_IRUSR | S_IWUSR;
+            fd = open(in_fileUri.c_str(), kOpenFlags, kFileMode);
+        } else {
+            fd = open(in_fileUri.c_str(), O_RDONLY);
+        }
+        _aidl_return->set(fd);
+        return Status::ok();
+    }
+
+    Status onTranscodingStarted(int32_t in_jobId) override {
+        append(EventTracker::Start(mClientId, in_jobId));
+        return Status::ok();
+    }
+
+    Status onTranscodingPaused(int32_t in_jobId) override {
+        append(EventTracker::Pause(mClientId, in_jobId));
+        return Status::ok();
+    }
+
+    Status onTranscodingResumed(int32_t in_jobId) override {
+        append(EventTracker::Resume(mClientId, in_jobId));
+        return Status::ok();
+    }
+
+    Status onTranscodingFinished(
+            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 {
+        append(Failed(mClientId, in_jobId));
+        return Status::ok();
+    }
+
+    Status onAwaitNumberOfJobsChanged(int32_t /* in_jobId */, int32_t /* in_oldAwaitNumber */,
+                                      int32_t /* in_newAwaitNumber */) override {
+        return Status::ok();
+    }
+
+    Status onProgressUpdate(int32_t /* in_jobId */, int32_t /* in_progress */) override {
+        return Status::ok();
+    }
+
+    int32_t mClientId;
+};
+
+class MediaTranscodingServiceTestBase : public ::testing::Test {
+public:
+    MediaTranscodingServiceTestBase() { ALOGI("MediaTranscodingServiceTestBase created"); }
+
+    virtual ~MediaTranscodingServiceTestBase() {
+        ALOGI("MediaTranscodingServiceTestBase 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;
+        }
+        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.
+        mClient1 = registerOneClient(kClientPackageA, mClientCallback1, UID(1));
+        EXPECT_TRUE(mClient1 != nullptr);
+
+        mClient2 = registerOneClient(kClientPackageB, mClientCallback2, UID(2));
+        EXPECT_TRUE(mClient2 != nullptr);
+
+        mClient3 = registerOneClient(kClientPackageC, mClientCallback3, UID(3));
+        EXPECT_TRUE(mClient3 != nullptr);
+
+        // Check the number of clients.
+        int32_t numOfClients;
+        Status status = mService->getNumOfClients(&numOfClients);
+        EXPECT_TRUE(status.isOk());
+        EXPECT_EQ(3, numOfClients);
+    }
+
+    void unregisterMultipleClients() {
+        Status status;
+
+        // Unregister the clients.
+        status = mClient1->unregister();
+        EXPECT_TRUE(status.isOk());
+
+        status = mClient2->unregister();
+        EXPECT_TRUE(status.isOk());
+
+        status = mClient3->unregister();
+        EXPECT_TRUE(status.isOk());
+
+        // Check the number of clients.
+        int32_t numOfClients;
+        status = mService->getNumOfClients(&numOfClients);
+        EXPECT_TRUE(status.isOk());
+        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* sourceFilePath, const char* destinationFilePath,
+                TranscodingJobPriority priority = TranscodingJobPriority::kNormal,
+                int bitrateBps = -1) {
+        constexpr bool shouldSucceed = (expectation == success);
+        bool result;
+        TranscodingRequestParcel request;
+        TranscodingJobParcel job;
+
+        request.sourceFilePath = sourceFilePath;
+        request.destinationFilePath = destinationFilePath;
+        request.priority = priority;
+        if (bitrateBps > 0) {
+            request.requestedVideoTrackFormat.emplace(TranscodingVideoTrackFormat());
+            request.requestedVideoTrackFormat->bitrateBps = bitrateBps;
+        }
+        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* sourceFilePath, const char* destinationFilePath) {
+        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.sourceFilePath, sourceFilePath);
+        }
+
+        return status.isOk() && (result == shouldSucceed) &&
+               (!shouldSucceed ||
+                (job.jobId == jobId && job.request.sourceFilePath == sourceFilePath &&
+                 job.request.destinationFilePath == destinationFilePath));
+    }
+
+    void deleteFile(const char* path) { unlink(path); }
+
+    std::shared_ptr<IMediaTranscodingService> mService;
+    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;
+    const char* mTestName;
+};
+
+}  // namespace media
+}  // namespace android
diff --git a/services/mediatranscoding/tests/README.txt b/services/mediatranscoding/tests/README.txt
new file mode 100644
index 0000000..cde465e
--- /dev/null
+++ b/services/mediatranscoding/tests/README.txt
@@ -0,0 +1,8 @@
+mediatranscodingservice_simulated_tests:
+	Tests media transcoding service with simulated transcoder.
+
+mediatranscodingservice_real_tests:
+	Tests media transcoding service with real transcoder. Uses the same test assets
+	as the MediaTranscoder unit tests. Before running the test, please make sure
+	to push the test assets to /sdcard:
+	adb push $TOP/frameworks/av/media/libmediatranscoding/tests/assets /data/local/tmp/TranscodingTestAssets
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 bdc0394..d66b340 100644
--- a/services/mediatranscoding/tests/build_and_run_all_unit_tests.sh
+++ b/services/mediatranscoding/tests/build_and_run_all_unit_tests.sh
@@ -13,6 +13,9 @@
 
 mm
 
+# Push the files onto the device.
+. $ANDROID_BUILD_TOP/frameworks/av/media/libmediatranscoding/tests/assets/push_assets.sh
+
 echo "[==========] installing test apps"
 adb root
 adb install -t -r -g -d $ANDROID_TARGET_OUT_TESTCASES/TranscodingUidPolicy_TestAppA/arm64/TranscodingUidPolicy_TestAppA.apk
@@ -21,7 +24,19 @@
 
 echo "[==========] waiting for device and sync"
 adb wait-for-device remount && adb sync
-adb shell kill -9 `pid media.transcoding`
 
-#adb shell /data/nativetest64/mediatranscodingservice_tests/mediatranscodingservice_tests
-adb shell /data/nativetest/mediatranscodingservice_tests/mediatranscodingservice_tests
+echo "[==========] running simulated tests"
+adb shell setprop debug.transcoding.simulated_transcoder true
+adb shell kill -9 `pid media.transcoding`
+#adb shell /data/nativetest64/mediatranscodingservice_simulated_tests/mediatranscodingservice_simulated_tests
+adb shell /data/nativetest/mediatranscodingservice_simulated_tests/mediatranscodingservice_simulated_tests
+
+echo "[==========] running real tests"
+adb shell setprop debug.transcoding.simulated_transcoder false
+adb shell kill -9 `pid media.transcoding`
+#adb shell /data/nativetest64/mediatranscodingservice_real_tests/mediatranscodingservice_real_tests
+adb shell /data/nativetest/mediatranscodingservice_real_tests/mediatranscodingservice_real_tests
+
+echo "[==========] removing debug properties"
+adb shell setprop debug.transcoding.simulated_transcoder \"\"
+adb shell kill -9 `pid media.transcoding`
diff --git a/services/mediatranscoding/tests/mediatranscodingservice_real_tests.cpp b/services/mediatranscoding/tests/mediatranscodingservice_real_tests.cpp
new file mode 100644
index 0000000..8cecfed
--- /dev/null
+++ b/services/mediatranscoding/tests/mediatranscodingservice_real_tests.cpp
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+// Unit Test for MediaTranscodingService.
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "MediaTranscodingServiceRealTest"
+
+#include "MediaTranscodingServiceTestHelper.h"
+
+/*
+ * Tests media transcoding service with real transcoder.
+ *
+ * Uses the same test assets as the MediaTranscoder unit tests. Before running the test,
+ * please make sure to push the test assets to /sdcard:
+ *
+ * adb push $TOP/frameworks/av/media/libmediatranscoding/transcoder/tests/assets /data/local/tmp/TranscodingTestAssets
+ */
+namespace android {
+
+namespace media {
+
+constexpr int64_t kPaddingUs = 200000;
+constexpr int64_t kJobWithPaddingUs = 10000000 + kPaddingUs;
+
+constexpr const char* kSrcPath =
+        "/data/local/tmp/TranscodingTestAssets/cubicle_avc_480x240_aac_24KHz.mp4";
+
+class MediaTranscodingServiceRealTest : public MediaTranscodingServiceTestBase {
+public:
+    MediaTranscodingServiceRealTest() {}
+
+    void deleteFile(const char* path) { unlink(path); }
+};
+
+TEST_F(MediaTranscodingServiceRealTest, TestTranscodePassthru) {
+    registerMultipleClients();
+
+    const char* dstPath = "/data/local/tmp/MediaTranscodingService_Passthru.MP4";
+    deleteFile(dstPath);
+
+    // Submit one job.
+    EXPECT_TRUE(submit(mClient1, 0, kSrcPath, dstPath));
+
+    // Wait for job to finish.
+    EXPECT_EQ(mClientCallback1->pop(kJobWithPaddingUs), EventTracker::Finished(CLIENT(1), 0));
+
+    unregisterMultipleClients();
+}
+
+TEST_F(MediaTranscodingServiceRealTest, TestTranscodeVideo) {
+    registerMultipleClients();
+
+    const char* dstPath = "/data/local/tmp/MediaTranscodingService_Video.MP4";
+    deleteFile(dstPath);
+
+    const int32_t kBitRate = 8 * 1000 * 1000;  // 8Mbs
+    // Submit one job.
+    EXPECT_TRUE(submit(mClient1, 0, kSrcPath, dstPath, TranscodingJobPriority::kNormal, kBitRate));
+
+    // Wait for job to finish.
+    EXPECT_EQ(mClientCallback1->pop(kJobWithPaddingUs), EventTracker::Finished(CLIENT(1), 0));
+
+    // TODO: verify output file format.
+
+    unregisterMultipleClients();
+}
+
+}  // namespace media
+}  // namespace android
diff --git a/services/mediatranscoding/tests/mediatranscodingservice_simulated_tests.cpp b/services/mediatranscoding/tests/mediatranscodingservice_simulated_tests.cpp
new file mode 100644
index 0000000..309e39d
--- /dev/null
+++ b/services/mediatranscoding/tests/mediatranscodingservice_simulated_tests.cpp
@@ -0,0 +1,373 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+// Unit Test for MediaTranscodingService.
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "MediaTranscodingServiceSimulatedTest"
+
+#include <aidl/android/media/BnTranscodingClientCallback.h>
+#include <aidl/android/media/IMediaTranscodingService.h>
+#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/binder_manager.h>
+#include <android/binder_process.h>
+#include <binder/PermissionController.h>
+#include <cutils/multiuser.h>
+#include <gtest/gtest.h>
+#include <utils/Log.h>
+
+#include <iostream>
+#include <list>
+
+#include "MediaTranscodingServiceTestHelper.h"
+#include "SimulatedTranscoder.h"
+
+namespace android {
+
+namespace media {
+
+// Note that -1 is valid and means using calling pid/uid for the service. But only privilege caller could
+// use them. This test is not a privilege caller.
+constexpr int32_t kInvalidClientPid = -5;
+constexpr const char* kInvalidClientName = "";
+constexpr const char* kInvalidClientOpPackageName = "";
+
+constexpr int32_t kClientUseCallingUid = IMediaTranscodingService::USE_CALLING_UID;
+
+constexpr int64_t kPaddingUs = 1000000;
+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:
+    MediaTranscodingServiceSimulatedTest() {}
+};
+
+TEST_F(MediaTranscodingServiceSimulatedTest, TestRegisterNullClient) {
+    std::shared_ptr<ITranscodingClient> client;
+
+    // Register the client with null callback.
+    Status status = mService->registerClient(nullptr, kClientName, kClientOpPackageName,
+                                             kClientUseCallingUid, kClientUseCallingPid, &client);
+    EXPECT_FALSE(status.isOk());
+}
+
+TEST_F(MediaTranscodingServiceSimulatedTest, TestRegisterClientWithInvalidClientPid) {
+    std::shared_ptr<ITranscodingClient> client;
+
+    // Register the client with the service.
+    Status status = mService->registerClient(mClientCallback1, kClientName, kClientOpPackageName,
+                                             kClientUseCallingUid, kInvalidClientPid, &client);
+    EXPECT_FALSE(status.isOk());
+}
+
+TEST_F(MediaTranscodingServiceSimulatedTest, TestRegisterClientWithInvalidClientName) {
+    std::shared_ptr<ITranscodingClient> client;
+
+    // Register the client with the service.
+    Status status = mService->registerClient(mClientCallback1, kInvalidClientName,
+                                             kInvalidClientOpPackageName, kClientUseCallingUid,
+                                             kClientUseCallingPid, &client);
+    EXPECT_FALSE(status.isOk());
+}
+
+TEST_F(MediaTranscodingServiceSimulatedTest, TestRegisterClientWithInvalidClientPackageName) {
+    std::shared_ptr<ITranscodingClient> client;
+
+    // Register the client with the service.
+    Status status =
+            mService->registerClient(mClientCallback1, kClientName, kInvalidClientOpPackageName,
+                                     kClientUseCallingUid, kClientUseCallingPid, &client);
+    EXPECT_FALSE(status.isOk());
+}
+
+TEST_F(MediaTranscodingServiceSimulatedTest, TestRegisterOneClient) {
+    std::shared_ptr<ITranscodingClient> client;
+
+    Status status = mService->registerClient(mClientCallback1, kClientName, kClientOpPackageName,
+                                             kClientUseCallingUid, kClientUseCallingPid, &client);
+    EXPECT_TRUE(status.isOk());
+
+    // Validate the client.
+    EXPECT_TRUE(client != nullptr);
+
+    // Check the number of Clients.
+    int32_t numOfClients;
+    status = mService->getNumOfClients(&numOfClients);
+    EXPECT_TRUE(status.isOk());
+    EXPECT_EQ(1, numOfClients);
+
+    // Unregister the client.
+    status = client->unregister();
+    EXPECT_TRUE(status.isOk());
+
+    // Check the number of Clients.
+    status = mService->getNumOfClients(&numOfClients);
+    EXPECT_TRUE(status.isOk());
+    EXPECT_EQ(0, numOfClients);
+}
+
+TEST_F(MediaTranscodingServiceSimulatedTest, TestRegisterClientTwice) {
+    std::shared_ptr<ITranscodingClient> client;
+
+    Status status = mService->registerClient(mClientCallback1, kClientName, kClientOpPackageName,
+                                             kClientUseCallingUid, kClientUseCallingPid, &client);
+    EXPECT_TRUE(status.isOk());
+
+    // Validate the client.
+    EXPECT_TRUE(client != nullptr);
+
+    // Register the client again and expects failure.
+    std::shared_ptr<ITranscodingClient> client1;
+    status = mService->registerClient(mClientCallback1, kClientName, kClientOpPackageName,
+                                      kClientUseCallingUid, kClientUseCallingPid, &client1);
+    EXPECT_FALSE(status.isOk());
+
+    // Unregister the client.
+    status = client->unregister();
+    EXPECT_TRUE(status.isOk());
+}
+
+TEST_F(MediaTranscodingServiceSimulatedTest, TestRegisterMultipleClients) {
+    registerMultipleClients();
+    unregisterMultipleClients();
+}
+
+TEST_F(MediaTranscodingServiceSimulatedTest, TestJobIdIndependence) {
+    registerMultipleClients();
+
+    // Submit 2 requests on client1 first.
+    EXPECT_TRUE(submit(mClient1, 0, "test_source_file", "test_destination_file"));
+    EXPECT_TRUE(submit(mClient1, 1, "test_source_file", "test_destination_file"));
+
+    // Submit 2 requests on client2, jobId should be independent for each client.
+    EXPECT_TRUE(submit(mClient2, 0, "test_source_file", "test_destination_file"));
+    EXPECT_TRUE(submit(mClient2, 1, "test_source_file", "test_destination_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(MediaTranscodingServiceSimulatedTest, TestSubmitCancelJobs) {
+    registerMultipleClients();
+
+    // Test jobId assignment.
+    EXPECT_TRUE(submit(mClient1, 0, "test_source_file_0", "test_destination_file"));
+    EXPECT_TRUE(submit(mClient1, 1, "test_source_file_1", "test_destination_file"));
+    EXPECT_TRUE(submit(mClient1, 2, "test_source_file_2", "test_destination_file"));
+
+    // Test submit bad request (no valid sourceFilePath) fails.
+    EXPECT_TRUE(submit<fail>(mClient1, 0, "", ""));
+
+    // Test cancel non-existent job fails.
+    EXPECT_TRUE(cancel<fail>(mClient1, 100));
+
+    // 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 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(cancel<fail>(mClient1, 1));
+
+    // Test no more events arriving after cancel.
+    EXPECT_EQ(mClientCallback1->pop(kJobWithPaddingUs), EventTracker::NoEvent);
+
+    unregisterMultipleClients();
+}
+
+TEST_F(MediaTranscodingServiceSimulatedTest, TestGetJobs) {
+    registerMultipleClients();
+
+    // Submit 3 requests.
+    EXPECT_TRUE(submit(mClient1, 0, "test_source_file_0", "test_destination_file_0"));
+    EXPECT_TRUE(submit(mClient1, 1, "test_source_file_1", "test_destination_file_1"));
+    EXPECT_TRUE(submit(mClient1, 2, "test_source_file_2", "test_destination_file_2"));
+
+    // Test get jobs by id.
+    EXPECT_TRUE(getJob(mClient1, 2, "test_source_file_2", "test_destination_file_2"));
+    EXPECT_TRUE(getJob(mClient1, 1, "test_source_file_1", "test_destination_file_1"));
+    EXPECT_TRUE(getJob(mClient1, 0, "test_source_file_0", "test_destination_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_source_file_1", "test_destination_file_1"));
+
+    // Cancel remaining job 1.
+    EXPECT_TRUE(cancel(mClient1, 1));
+
+    unregisterMultipleClients();
+}
+
+TEST_F(MediaTranscodingServiceSimulatedTest, TestSubmitCancelWithOfflineJobs) {
+    registerMultipleClients();
+
+    // Submit some offline jobs first.
+    EXPECT_TRUE(submit(mClient1, 0, "test_source_file_0", "test_destination_file_0",
+                       TranscodingJobPriority::kUnspecified));
+    EXPECT_TRUE(submit(mClient1, 1, "test_source_file_1", "test_destination_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_source_file_2", "test_destination_file_2"));
+    EXPECT_TRUE(submit(mClient1, 3, "test_source_file_3", "test_destination_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(MediaTranscodingServiceSimulatedTest, TestClientUseAfterUnregister) {
+    std::shared_ptr<ITranscodingClient> client;
+
+    // Register a client, then unregister.
+    Status status = mService->registerClient(mClientCallback1, kClientName, kClientOpPackageName,
+                                             kClientUseCallingUid, kClientUseCallingPid, &client);
+    EXPECT_TRUE(status.isOk());
+
+    status = client->unregister();
+    EXPECT_TRUE(status.isOk());
+
+    // Test various operations on the client, should fail with ERROR_DISCONNECTED.
+    TranscodingJobParcel job;
+    bool result;
+    status = client->getJobWithId(0, &job, &result);
+    EXPECT_EQ(status.getServiceSpecificError(), IMediaTranscodingService::ERROR_DISCONNECTED);
+
+    status = client->cancelJob(0, &result);
+    EXPECT_EQ(status.getServiceSpecificError(), IMediaTranscodingService::ERROR_DISCONNECTED);
+
+    TranscodingRequestParcel request;
+    status = client->submitRequest(request, &job, &result);
+    EXPECT_EQ(status.getServiceSpecificError(), IMediaTranscodingService::ERROR_DISCONNECTED);
+}
+
+TEST_F(MediaTranscodingServiceSimulatedTest, 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_source_file_0", "test_destination_file_0"));
+    EXPECT_TRUE(submit(mClient1, 1, "test_source_file_1", "test_destination_file_1"));
+    EXPECT_TRUE(submit(mClient1, 2, "test_source_file_2", "test_destination_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_source_file_0", "test_destination_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
diff --git a/services/mediatranscoding/tests/mediatranscodingservice_tests.cpp b/services/mediatranscoding/tests/mediatranscodingservice_tests.cpp
deleted file mode 100644
index 3bc7b15..0000000
--- a/services/mediatranscoding/tests/mediatranscodingservice_tests.cpp
+++ /dev/null
@@ -1,710 +0,0 @@
-/*
- * Copyright (C) 2019 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.
- */
-
-// Unit Test for MediaTranscodingService.
-
-//#define LOG_NDEBUG 0
-#define LOG_TAG "MediaTranscodingServiceTest"
-
-#include <aidl/android/media/BnTranscodingClientCallback.h>
-#include <aidl/android/media/IMediaTranscodingService.h>
-#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/binder_manager.h>
-#include <android/binder_process.h>
-#include <binder/PermissionController.h>
-#include <cutils/multiuser.h>
-#include <gtest/gtest.h>
-#include <utils/Log.h>
-
-#include <iostream>
-#include <list>
-
-#include "SimulatedTranscoder.h"
-
-namespace android {
-
-namespace media {
-
-using Status = ::ndk::ScopedAStatus;
-using aidl::android::media::BnTranscodingClientCallback;
-using aidl::android::media::IMediaTranscodingService;
-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
-// use them. This test is not a privilege caller.
-constexpr int32_t kInvalidClientPid = -5;
-constexpr const char* kInvalidClientName = "";
-constexpr const char* kInvalidClientOpPackageName = "";
-
-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 = 1000000;
-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";
-
-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;
-    }
-
-    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 openFileDescriptor(const std::string& /*in_fileUri*/, const std::string& /*in_mode*/,
-                              ::ndk::ScopedFileDescriptor* /*_aidl_return*/) override {
-        return Status::ok();
-    }
-
-    Status onTranscodingStarted(int32_t in_jobId) override {
-        append(Start(mClientId, in_jobId));
-        return Status::ok();
-    }
-
-    Status onTranscodingPaused(int32_t in_jobId) override {
-        append(Pause(mClientId, in_jobId));
-        return Status::ok();
-    }
-
-    Status onTranscodingResumed(int32_t in_jobId) override {
-        append(Resume(mClientId, in_jobId));
-        return Status::ok();
-    }
-
-    Status onTranscodingFinished(
-            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 {
-        append(Failed(mClientId, in_jobId));
-        return Status::ok();
-    }
-
-    Status onAwaitNumberOfJobsChanged(int32_t /* in_jobId */, int32_t /* in_oldAwaitNumber */,
-                                      int32_t /* in_newAwaitNumber */) override {
-        return Status::ok();
-    }
-
-    Status onProgressUpdate(int32_t /*in_jobId*/, int32_t /*in_progress*/) override {
-        return Status::ok();
-    }
-
-    int32_t mClientId;
-};
-
-class MediaTranscodingServiceTest : public ::testing::Test {
-public:
-    MediaTranscodingServiceTest() { ALOGI("MediaTranscodingServiceTest created"); }
-
-    ~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;
-        }
-        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.
-        mClient1 = registerOneClient(kClientPackageA, mClientCallback1, UID(1));
-        EXPECT_TRUE(mClient1 != nullptr);
-
-        mClient2 = registerOneClient(kClientPackageB, mClientCallback2, UID(2));
-        EXPECT_TRUE(mClient2 != nullptr);
-
-        mClient3 = registerOneClient(kClientPackageC, mClientCallback3, UID(3));
-        EXPECT_TRUE(mClient3 != nullptr);
-
-        // Check the number of clients.
-        int32_t numOfClients;
-        Status status = mService->getNumOfClients(&numOfClients);
-        EXPECT_TRUE(status.isOk());
-        EXPECT_EQ(3, numOfClients);
-    }
-
-    void unregisterMultipleClients() {
-        Status status;
-
-        // Unregister the clients.
-        status = mClient1->unregister();
-        EXPECT_TRUE(status.isOk());
-
-        status = mClient2->unregister();
-        EXPECT_TRUE(status.isOk());
-
-        status = mClient3->unregister();
-        EXPECT_TRUE(status.isOk());
-
-        // Check the number of clients.
-        int32_t numOfClients;
-        status = mService->getNumOfClients(&numOfClients);
-        EXPECT_TRUE(status.isOk());
-        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* sourceFilePath, const char* destinationFilePath,
-                TranscodingJobPriority priority = TranscodingJobPriority::kNormal) {
-        constexpr bool shouldSucceed = (expectation == success);
-        bool result;
-        TranscodingRequestParcel request;
-        TranscodingJobParcel job;
-
-        request.sourceFilePath = sourceFilePath;
-        request.destinationFilePath = destinationFilePath;
-        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* sourceFilePath, const char* destinationFilePath) {
-        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.sourceFilePath, sourceFilePath);
-        }
-
-        return status.isOk() && (result == shouldSucceed) &&
-               (!shouldSucceed ||
-                (job.jobId == jobId && job.request.sourceFilePath == sourceFilePath &&
-                 job.request.destinationFilePath == destinationFilePath));
-    }
-
-    std::shared_ptr<IMediaTranscodingService> mService;
-    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;
-};
-
-TEST_F(MediaTranscodingServiceTest, TestRegisterNullClient) {
-    std::shared_ptr<ITranscodingClient> client;
-
-    // Register the client with null callback.
-    Status status = mService->registerClient(nullptr, kClientName, kClientOpPackageName,
-                                             kClientUseCallingUid, kClientUseCallingPid, &client);
-    EXPECT_FALSE(status.isOk());
-}
-
-TEST_F(MediaTranscodingServiceTest, TestRegisterClientWithInvalidClientPid) {
-    std::shared_ptr<ITranscodingClient> client;
-
-    // Register the client with the service.
-    Status status = mService->registerClient(mClientCallback1, kClientName, kClientOpPackageName,
-                                             kClientUseCallingUid, kInvalidClientPid, &client);
-    EXPECT_FALSE(status.isOk());
-}
-
-TEST_F(MediaTranscodingServiceTest, TestRegisterClientWithInvalidClientName) {
-    std::shared_ptr<ITranscodingClient> client;
-
-    // Register the client with the service.
-    Status status = mService->registerClient(mClientCallback1, kInvalidClientName,
-                                             kInvalidClientOpPackageName, kClientUseCallingUid,
-                                             kClientUseCallingPid, &client);
-    EXPECT_FALSE(status.isOk());
-}
-
-TEST_F(MediaTranscodingServiceTest, TestRegisterClientWithInvalidClientPackageName) {
-    std::shared_ptr<ITranscodingClient> client;
-
-    // Register the client with the service.
-    Status status =
-            mService->registerClient(mClientCallback1, kClientName, kInvalidClientOpPackageName,
-                                     kClientUseCallingUid, kClientUseCallingPid, &client);
-    EXPECT_FALSE(status.isOk());
-}
-
-TEST_F(MediaTranscodingServiceTest, TestRegisterOneClient) {
-    std::shared_ptr<ITranscodingClient> client;
-
-    Status status = mService->registerClient(mClientCallback1, kClientName, kClientOpPackageName,
-                                             kClientUseCallingUid, kClientUseCallingPid, &client);
-    EXPECT_TRUE(status.isOk());
-
-    // Validate the client.
-    EXPECT_TRUE(client != nullptr);
-
-    // Check the number of Clients.
-    int32_t numOfClients;
-    status = mService->getNumOfClients(&numOfClients);
-    EXPECT_TRUE(status.isOk());
-    EXPECT_EQ(1, numOfClients);
-
-    // Unregister the client.
-    status = client->unregister();
-    EXPECT_TRUE(status.isOk());
-
-    // Check the number of Clients.
-    status = mService->getNumOfClients(&numOfClients);
-    EXPECT_TRUE(status.isOk());
-    EXPECT_EQ(0, numOfClients);
-}
-
-TEST_F(MediaTranscodingServiceTest, TestRegisterClientTwice) {
-    std::shared_ptr<ITranscodingClient> client;
-
-    Status status = mService->registerClient(mClientCallback1, kClientName, kClientOpPackageName,
-                                             kClientUseCallingUid, kClientUseCallingPid, &client);
-    EXPECT_TRUE(status.isOk());
-
-    // Validate the client.
-    EXPECT_TRUE(client != nullptr);
-
-    // Register the client again and expects failure.
-    std::shared_ptr<ITranscodingClient> client1;
-    status = mService->registerClient(mClientCallback1, kClientName, kClientOpPackageName,
-                                      kClientUseCallingUid, kClientUseCallingPid, &client1);
-    EXPECT_FALSE(status.isOk());
-
-    // Unregister the client.
-    status = client->unregister();
-    EXPECT_TRUE(status.isOk());
-}
-
-TEST_F(MediaTranscodingServiceTest, TestRegisterMultipleClients) {
-    registerMultipleClients();
-    unregisterMultipleClients();
-}
-
-TEST_F(MediaTranscodingServiceTest, TestJobIdIndependence) {
-    registerMultipleClients();
-
-    // Submit 2 requests on client1 first.
-    EXPECT_TRUE(submit(mClient1, 0, "test_source_file", "test_destination_file"));
-    EXPECT_TRUE(submit(mClient1, 1, "test_source_file", "test_destination_file"));
-
-    // Submit 2 requests on client2, jobId should be independent for each client.
-    EXPECT_TRUE(submit(mClient2, 0, "test_source_file", "test_destination_file"));
-    EXPECT_TRUE(submit(mClient2, 1, "test_source_file", "test_destination_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.
-    EXPECT_TRUE(submit(mClient1, 0, "test_source_file_0", "test_destination_file"));
-    EXPECT_TRUE(submit(mClient1, 1, "test_source_file_1", "test_destination_file"));
-    EXPECT_TRUE(submit(mClient1, 2, "test_source_file_2", "test_destination_file"));
-
-    // Test submit bad request (no valid sourceFilePath) fails.
-    EXPECT_TRUE(submit<fail>(mClient1, 0, "", ""));
-
-    // Test cancel non-existent job fails.
-    EXPECT_TRUE(cancel<fail>(mClient1, 100));
-
-    // 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 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(cancel<fail>(mClient1, 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_source_file_0", "test_destination_file_0"));
-    EXPECT_TRUE(submit(mClient1, 1, "test_source_file_1", "test_destination_file_1"));
-    EXPECT_TRUE(submit(mClient1, 2, "test_source_file_2", "test_destination_file_2"));
-
-    // Test get jobs by id.
-    EXPECT_TRUE(getJob(mClient1, 2, "test_source_file_2", "test_destination_file_2"));
-    EXPECT_TRUE(getJob(mClient1, 1, "test_source_file_1", "test_destination_file_1"));
-    EXPECT_TRUE(getJob(mClient1, 0, "test_source_file_0", "test_destination_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_source_file_1", "test_destination_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_source_file_0", "test_destination_file_0",
-                       TranscodingJobPriority::kUnspecified));
-    EXPECT_TRUE(submit(mClient1, 1, "test_source_file_1", "test_destination_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_source_file_2", "test_destination_file_2"));
-    EXPECT_TRUE(submit(mClient1, 3, "test_source_file_3", "test_destination_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, TestClientUseAfterUnregister) {
-    std::shared_ptr<ITranscodingClient> client;
-
-    // Register a client, then unregister.
-    Status status = mService->registerClient(mClientCallback1, kClientName, kClientOpPackageName,
-                                             kClientUseCallingUid, kClientUseCallingPid, &client);
-    EXPECT_TRUE(status.isOk());
-
-    status = client->unregister();
-    EXPECT_TRUE(status.isOk());
-
-    // Test various operations on the client, should fail with ERROR_DISCONNECTED.
-    TranscodingJobParcel job;
-    bool result;
-    status = client->getJobWithId(0, &job, &result);
-    EXPECT_EQ(status.getServiceSpecificError(), IMediaTranscodingService::ERROR_DISCONNECTED);
-
-    status = client->cancelJob(0, &result);
-    EXPECT_EQ(status.getServiceSpecificError(), IMediaTranscodingService::ERROR_DISCONNECTED);
-
-    TranscodingRequestParcel request;
-    status = client->submitRequest(request, &job, &result);
-    EXPECT_EQ(status.getServiceSpecificError(), IMediaTranscodingService::ERROR_DISCONNECTED);
-}
-
-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_source_file_0", "test_destination_file_0"));
-    EXPECT_TRUE(submit(mClient1, 1, "test_source_file_1", "test_destination_file_1"));
-    EXPECT_TRUE(submit(mClient1, 2, "test_source_file_2", "test_destination_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_source_file_0", "test_destination_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