Merge "Revert "Call getAudioPort to get supported attributes for audio devices.""
diff --git a/apex/Android.bp b/apex/Android.bp
index ef296d6..fac3831 100644
--- a/apex/Android.bp
+++ b/apex/Android.bp
@@ -31,6 +31,8 @@
                 "libmpeg2extractor",
                 "liboggextractor",
                 "libwavextractor",
+                // JNI
+                "libmediaparser-jni"
             ],
         },
     },
@@ -64,10 +66,9 @@
     ],
 }
 
-prebuilt_etc {
+linker_config {
     name: "media-linker-config",
-    src: "linker.config.txt",
-    filename: "linker.config.txt",
+    src: "linker.config.json",
     installable: false,
 }
 
diff --git a/apex/linker.config.json b/apex/linker.config.json
new file mode 100644
index 0000000..67c076e
--- /dev/null
+++ b/apex/linker.config.json
@@ -0,0 +1,3 @@
+{
+    "visible": true
+}
diff --git a/apex/linker.config.txt b/apex/linker.config.txt
deleted file mode 100644
index d1c815b..0000000
--- a/apex/linker.config.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-# Extra linker configurations for media APEX
-# See https://android.googlesource.com/platform/system/linkerconfig/+/master/README.md#apex_etc_linker_config_txt
-
-[properties]
-
-# Set media APEX as force visible so media APEX namespace is accessible via android_get_exported_namespace
-visible = true
diff --git a/apex/manifest.json b/apex/manifest.json
index ddd642e..f1f69f4 100644
--- a/apex/manifest.json
+++ b/apex/manifest.json
@@ -1,4 +1,4 @@
 {
   "name": "com.android.media",
-  "version": 300000000
+  "version": 309999900
 }
diff --git a/apex/manifest_codec.json b/apex/manifest_codec.json
index 1f05d2e..e20d867 100644
--- a/apex/manifest_codec.json
+++ b/apex/manifest_codec.json
@@ -1,6 +1,6 @@
 {
   "name": "com.android.media.swcodec",
-  "version": 300000000,
+  "version": 309999900,
   "requireNativeLibs": [
     ":sphal"
   ]
diff --git a/camera/ndk/impl/ACameraMetadata.cpp b/camera/ndk/impl/ACameraMetadata.cpp
index 631f6cd..895514e 100644
--- a/camera/ndk/impl/ACameraMetadata.cpp
+++ b/camera/ndk/impl/ACameraMetadata.cpp
@@ -426,6 +426,7 @@
             camera_metadata_ro_entry_t entry;
             int ret = get_camera_metadata_ro_entry(rawMetadata, i, &entry);
             if (ret != 0) {
+                mData->unlock(rawMetadata);
                 ALOGE("%s: error reading metadata index %zu", __FUNCTION__, i);
                 return ACAMERA_ERROR_UNKNOWN;
             }
diff --git a/camera/ndk/include/camera/NdkCameraMetadataTags.h b/camera/ndk/include/camera/NdkCameraMetadataTags.h
index a2c2ca7..2d54bd1 100644
--- a/camera/ndk/include/camera/NdkCameraMetadataTags.h
+++ b/camera/ndk/include/camera/NdkCameraMetadataTags.h
@@ -4881,7 +4881,7 @@
      * rectangle, and cropping to the rectangle given in ACAMERA_SENSOR_INFO_ACTIVE_ARRAY_SIZE.</p>
      * <p>E.g. to calculate position of a pixel, (x,y), in a processed YUV output image with the
      * dimensions in ACAMERA_SENSOR_INFO_ACTIVE_ARRAY_SIZE given the position of a pixel,
-     * (x', y'), in the raw pixel array with dimensions give in
+     * (x', y'), in the raw pixel array with dimensions given in
      * ACAMERA_SENSOR_INFO_PIXEL_ARRAY_SIZE:</p>
      * <ol>
      * <li>Choose a pixel (x', y') within the active array region of the raw buffer given in
diff --git a/media/codec2/components/flac/C2SoftFlacEnc.cpp b/media/codec2/components/flac/C2SoftFlacEnc.cpp
index 408db7e..72910c5 100644
--- a/media/codec2/components/flac/C2SoftFlacEnc.cpp
+++ b/media/codec2/components/flac/C2SoftFlacEnc.cpp
@@ -262,9 +262,10 @@
         work->result = C2_NO_MEMORY;
         return;
     }
-    C2WriteView wView = mOutputBlock->map().get();
-    if (wView.error()) {
-        ALOGE("write view map failed %d", wView.error());
+
+    err = mOutputBlock->map().get().error();
+    if (err) {
+        ALOGE("write view map failed %d", err);
         work->result = C2_CORRUPTED;
         return;
     }
diff --git a/media/codec2/core/include/C2Config.h b/media/codec2/core/include/C2Config.h
index ebe7b40..38f7389 100644
--- a/media/codec2/core/include/C2Config.h
+++ b/media/codec2/core/include/C2Config.h
@@ -254,6 +254,8 @@
     kParamIndexTunneledMode, // struct
     kParamIndexTunnelHandle, // int32[]
     kParamIndexTunnelSystemTime, // int64
+
+    kParamIndexStoreDmaBufUsage,  // store, struct
 };
 
 }
@@ -2041,6 +2043,33 @@
         C2StoreIonUsageInfo;
 
 /**
+ * This structure describes the preferred DMA-Buf allocation parameters for a given memory usage.
+ */
+struct C2StoreDmaBufUsageStruct {
+    inline C2StoreDmaBufUsageStruct() { memset(this, 0, sizeof(*this)); }
+
+    inline C2StoreDmaBufUsageStruct(size_t flexCount, uint64_t usage_, uint32_t capacity_)
+        : usage(usage_), capacity(capacity_), allocFlags(0) {
+        memset(heapName, 0, flexCount);
+    }
+
+    uint64_t usage;                         ///< C2MemoryUsage
+    uint32_t capacity;                      ///< capacity
+    int32_t allocFlags;                     ///< ion allocation flags
+    char heapName[];                        ///< dmabuf heap name
+
+    DEFINE_AND_DESCRIBE_FLEX_C2STRUCT(StoreDmaBufUsage, heapName)
+    C2FIELD(usage, "usage")
+    C2FIELD(capacity, "capacity")
+    C2FIELD(allocFlags, "alloc-flags")
+    C2FIELD(heapName, "heap-name")
+};
+
+// store, private
+typedef C2GlobalParam<C2Info, C2StoreDmaBufUsageStruct, kParamIndexStoreDmaBufUsage>
+        C2StoreDmaBufUsageInfo;
+
+/**
  * Flexible pixel format descriptors
  */
 struct C2FlexiblePixelFormatDescriptorStruct {
diff --git a/media/codec2/hidl/services/vendor.cpp b/media/codec2/hidl/services/vendor.cpp
index 81bffeb..3ddb039 100644
--- a/media/codec2/hidl/services/vendor.cpp
+++ b/media/codec2/hidl/services/vendor.cpp
@@ -122,6 +122,18 @@
                 })
                 .withSetter(SetIonUsage)
                 .build());
+
+            addParameter(
+                DefineParam(mDmaBufUsageInfo, "dmabuf-usage")
+                .withDefault(new C2StoreDmaBufUsageInfo())
+                .withFields({
+                    C2F(mDmaBufUsageInfo, usage).flags({C2MemoryUsage::CPU_READ | C2MemoryUsage::CPU_WRITE}),
+                    C2F(mDmaBufUsageInfo, capacity).inRange(0, UINT32_MAX, 1024),
+                    C2F(mDmaBufUsageInfo, heapName).any(),
+                    C2F(mDmaBufUsageInfo, allocFlags).flags({}),
+                })
+                .withSetter(SetDmaBufUsage)
+                .build());
         }
 
         virtual ~Interface() = default;
@@ -135,7 +147,16 @@
             return C2R::Ok();
         }
 
+        static C2R SetDmaBufUsage(bool /* mayBlock */, C2P<C2StoreDmaBufUsageInfo> &me) {
+            // Vendor's TODO: put appropriate mapping logic
+            strncpy(me.set().m.heapName, "system", me.v.flexCount());
+            me.set().m.allocFlags = 0;
+            return C2R::Ok();
+        }
+
+
         std::shared_ptr<C2StoreIonUsageInfo> mIonUsageInfo;
+        std::shared_ptr<C2StoreDmaBufUsageInfo> mDmaBufUsageInfo;
     };
     std::shared_ptr<C2ReflectorHelper> mReflectorHelper;
     Interface mInterface;
diff --git a/media/codec2/sfplugin/CCodec.cpp b/media/codec2/sfplugin/CCodec.cpp
index 1405b97..f816778 100644
--- a/media/codec2/sfplugin/CCodec.cpp
+++ b/media/codec2/sfplugin/CCodec.cpp
@@ -2168,15 +2168,17 @@
             return OK;
         }
     }
-    uint64_t minUsage = usage.expected;
-    uint64_t maxUsage = ~0ull;
     std::set<C2Allocator::id_t> allocators;
     GetCommonAllocatorIds(names, C2Allocator::LINEAR, &allocators);
     if (allocators.empty()) {
         *isCompatible = false;
         return OK;
     }
+
+    uint64_t minUsage = 0;
+    uint64_t maxUsage = ~0ull;
     CalculateMinMaxUsage(names, &minUsage, &maxUsage);
+    minUsage |= usage.expected;
     *isCompatible = ((maxUsage & minUsage) == minUsage);
     return OK;
 }
@@ -2203,14 +2205,16 @@
 // static
 std::shared_ptr<C2LinearBlock> CCodec::FetchLinearBlock(
         size_t capacity, const C2MemoryUsage &usage, const std::vector<std::string> &names) {
-    uint64_t minUsage = usage.expected;
-    uint64_t maxUsage = ~0ull;
     std::set<C2Allocator::id_t> allocators;
     GetCommonAllocatorIds(names, C2Allocator::LINEAR, &allocators);
     if (allocators.empty()) {
         allocators.insert(C2PlatformAllocatorStore::DEFAULT_LINEAR);
     }
+
+    uint64_t minUsage = 0;
+    uint64_t maxUsage = ~0ull;
     CalculateMinMaxUsage(names, &minUsage, &maxUsage);
+    minUsage |= usage.expected;
     if ((maxUsage & minUsage) != minUsage) {
         allocators.clear();
         allocators.insert(C2PlatformAllocatorStore::DEFAULT_LINEAR);
diff --git a/media/codec2/vndk/Android.bp b/media/codec2/vndk/Android.bp
index 6f7acce..60f4736 100644
--- a/media/codec2/vndk/Android.bp
+++ b/media/codec2/vndk/Android.bp
@@ -26,6 +26,7 @@
         "C2AllocatorGralloc.cpp",
         "C2Buffer.cpp",
         "C2Config.cpp",
+        "C2DmaBufAllocator.cpp",
         "C2PlatformStorePluginLoader.cpp",
         "C2Store.cpp",
         "platform/C2BqBuffer.cpp",
@@ -64,6 +65,7 @@
         "libhardware",
         "libhidlbase",
         "libion",
+        "libdmabufheap",
         "libfmq",
         "liblog",
         "libnativewindow",
diff --git a/media/codec2/vndk/C2AllocatorGralloc.cpp b/media/codec2/vndk/C2AllocatorGralloc.cpp
index 4d7e619..59471a2 100644
--- a/media/codec2/vndk/C2AllocatorGralloc.cpp
+++ b/media/codec2/vndk/C2AllocatorGralloc.cpp
@@ -25,6 +25,7 @@
 #include <hardware/gralloc.h>
 #include <ui/GraphicBufferAllocator.h>
 #include <ui/GraphicBufferMapper.h>
+#include <ui/Rect.h>
 
 #include <C2AllocatorGralloc.h>
 #include <C2Buffer.h>
@@ -253,7 +254,7 @@
     virtual ~C2AllocationGralloc() override;
 
     virtual c2_status_t map(
-            C2Rect rect, C2MemoryUsage usage, C2Fence *fence,
+            C2Rect c2Rect, C2MemoryUsage usage, C2Fence *fence,
             C2PlanarLayout *layout /* nonnull */, uint8_t **addr /* nonnull */) override;
     virtual c2_status_t unmap(
             uint8_t **addr /* nonnull */, C2Rect rect, C2Fence *fence /* nullable */) override;
@@ -336,8 +337,12 @@
 }
 
 c2_status_t C2AllocationGralloc::map(
-        C2Rect rect, C2MemoryUsage usage, C2Fence *fence,
+        C2Rect c2Rect, C2MemoryUsage usage, C2Fence *fence,
         C2PlanarLayout *layout /* nonnull */, uint8_t **addr /* nonnull */) {
+    const Rect rect{(int32_t)c2Rect.left, (int32_t)c2Rect.top,
+                    (int32_t)(c2Rect.left + c2Rect.width) /* right */,
+                    (int32_t)(c2Rect.top + c2Rect.height) /* bottom */};
+
     uint64_t grallocUsage = static_cast<C2AndroidMemoryUsage>(usage).asGrallocUsage();
     ALOGV("mapping buffer with usage %#llx => %#llx",
           (long long)usage.expected, (long long)grallocUsage);
@@ -386,10 +391,7 @@
             void *pointer = nullptr;
             // TODO: fence
             status_t err = GraphicBufferMapper::get().lock(
-                                const_cast<native_handle_t *>(mBuffer), grallocUsage,
-                                { (int32_t)rect.left, (int32_t)rect.top,
-                                  (int32_t)rect.width, (int32_t)rect.height },
-                                &pointer);
+                    const_cast<native_handle_t *>(mBuffer), grallocUsage, rect, &pointer);
             if (err) {
                 ALOGE("failed transaction: lock(RGBA_1010102)");
                 return C2_CORRUPTED;
@@ -464,10 +466,7 @@
             void *pointer = nullptr;
             // TODO: fence
             status_t err = GraphicBufferMapper::get().lock(
-                                const_cast<native_handle_t*>(mBuffer), grallocUsage,
-                                { (int32_t)rect.left, (int32_t)rect.top,
-                                  (int32_t)rect.width, (int32_t)rect.height },
-                                &pointer);
+                    const_cast<native_handle_t*>(mBuffer), grallocUsage, rect, &pointer);
             if (err) {
                 ALOGE("failed transaction: lock(RGBA_8888)");
                 return C2_CORRUPTED;
@@ -524,10 +523,7 @@
             void *pointer = nullptr;
             // TODO: fence
             status_t err = GraphicBufferMapper::get().lock(
-                                const_cast<native_handle_t*>(mBuffer), grallocUsage,
-                                { (int32_t)rect.left, (int32_t)rect.top,
-                                  (int32_t)rect.width, (int32_t)rect.height },
-                                &pointer);
+                    const_cast<native_handle_t*>(mBuffer), grallocUsage, rect, &pointer);
             if (err) {
                 ALOGE("failed transaction: lock(BLOB)");
                 return C2_CORRUPTED;
@@ -544,10 +540,7 @@
             android_ycbcr ycbcrLayout;
 
             status_t err = GraphicBufferMapper::get().lockYCbCr(
-                        const_cast<native_handle_t*>(mBuffer), grallocUsage,
-                        { (int32_t)rect.left, (int32_t)rect.top,
-                          (int32_t)rect.width, (int32_t)rect.height },
-                        &ycbcrLayout);
+                    const_cast<native_handle_t*>(mBuffer), grallocUsage, rect, &ycbcrLayout);
             if (err) {
                 ALOGE("failed transaction: lockYCbCr");
                 return C2_CORRUPTED;
diff --git a/media/codec2/vndk/C2DmaBufAllocator.cpp b/media/codec2/vndk/C2DmaBufAllocator.cpp
new file mode 100644
index 0000000..59e82e2
--- /dev/null
+++ b/media/codec2/vndk/C2DmaBufAllocator.cpp
@@ -0,0 +1,401 @@
+/*
+ * 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 "C2DmaBufAllocator"
+#include <BufferAllocator/BufferAllocator.h>
+#include <C2Buffer.h>
+#include <C2Debug.h>
+#include <C2DmaBufAllocator.h>
+#include <C2ErrnoUtils.h>
+#include <linux/ion.h>
+#include <sys/mman.h>
+#include <unistd.h>  // getpagesize, size_t, close, dup
+#include <utils/Log.h>
+
+#include <list>
+
+#ifdef __ANDROID_APEX__
+#include <android-base/properties.h>
+#endif
+
+namespace android {
+
+namespace {
+constexpr size_t USAGE_LRU_CACHE_SIZE = 1024;
+}
+
+/* =========================== BUFFER HANDLE =========================== */
+/**
+ * Buffer handle
+ *
+ * Stores dmabuf fd & metadata
+ *
+ * This handle will not capture mapped fd-s as updating that would require a
+ * global mutex.
+ */
+
+struct C2HandleBuf : public C2Handle {
+    C2HandleBuf(int bufferFd, size_t size)
+        : C2Handle(cHeader),
+          mFds{bufferFd},
+          mInts{int(size & 0xFFFFFFFF), int((uint64_t(size) >> 32) & 0xFFFFFFFF), kMagic} {}
+
+    static bool IsValid(const C2Handle* const o);
+
+    int bufferFd() const { return mFds.mBuffer; }
+    size_t size() const {
+        return size_t(unsigned(mInts.mSizeLo)) | size_t(uint64_t(unsigned(mInts.mSizeHi)) << 32);
+    }
+
+   protected:
+    struct {
+        int mBuffer;  // dmabuf fd
+    } mFds;
+    struct {
+        int mSizeLo;  // low 32-bits of size
+        int mSizeHi;  // high 32-bits of size
+        int mMagic;
+    } mInts;
+
+   private:
+    typedef C2HandleBuf _type;
+    enum {
+        kMagic = '\xc2io\x00',
+        numFds = sizeof(mFds) / sizeof(int),
+        numInts = sizeof(mInts) / sizeof(int),
+        version = sizeof(C2Handle)
+    };
+    // constexpr static C2Handle cHeader = { version, numFds, numInts, {} };
+    const static C2Handle cHeader;
+};
+
+const C2Handle C2HandleBuf::cHeader = {
+        C2HandleBuf::version, C2HandleBuf::numFds, C2HandleBuf::numInts, {}};
+
+// static
+bool C2HandleBuf::IsValid(const C2Handle* const o) {
+    if (!o || memcmp(o, &cHeader, sizeof(cHeader))) {
+        return false;
+    }
+    const C2HandleBuf* other = static_cast<const C2HandleBuf*>(o);
+    return other->mInts.mMagic == kMagic;
+}
+
+/* =========================== DMABUF ALLOCATION =========================== */
+class C2DmaBufAllocation : public C2LinearAllocation {
+   public:
+    /* Interface methods */
+    virtual c2_status_t map(size_t offset, size_t size, C2MemoryUsage usage, C2Fence* fence,
+                            void** addr /* nonnull */) override;
+    virtual c2_status_t unmap(void* addr, size_t size, C2Fence* fenceFd) override;
+    virtual ~C2DmaBufAllocation() override;
+    virtual const C2Handle* handle() const override;
+    virtual id_t getAllocatorId() const override;
+    virtual bool equals(const std::shared_ptr<C2LinearAllocation>& other) const override;
+
+    // internal methods
+    C2DmaBufAllocation(BufferAllocator& alloc, size_t size, C2String heap_name, unsigned flags,
+                       C2Allocator::id_t id);
+    C2DmaBufAllocation(size_t size, int shareFd, C2Allocator::id_t id);
+
+    c2_status_t status() const;
+
+   protected:
+    virtual c2_status_t mapInternal(size_t mapSize, size_t mapOffset, size_t alignmentBytes,
+                                    int prot, int flags, void** base, void** addr) {
+        c2_status_t err = C2_OK;
+        *base = mmap(nullptr, mapSize, prot, flags, mHandle.bufferFd(), mapOffset);
+        ALOGV("mmap(size = %zu, prot = %d, flags = %d, mapFd = %d, offset = %zu) "
+              "returned (%d)",
+              mapSize, prot, flags, mHandle.bufferFd(), mapOffset, errno);
+        if (*base == MAP_FAILED) {
+            *base = *addr = nullptr;
+            err = c2_map_errno<EINVAL>(errno);
+        } else {
+            *addr = (uint8_t*)*base + alignmentBytes;
+        }
+        return err;
+    }
+
+    C2Allocator::id_t mId;
+    C2HandleBuf mHandle;
+    c2_status_t mInit;
+    struct Mapping {
+        void* addr;
+        size_t alignmentBytes;
+        size_t size;
+    };
+    std::list<Mapping> mMappings;
+
+    // TODO: we could make this encapsulate shared_ptr and copiable
+    C2_DO_NOT_COPY(C2DmaBufAllocation);
+};
+
+c2_status_t C2DmaBufAllocation::map(size_t offset, size_t size, C2MemoryUsage usage, C2Fence* fence,
+                                    void** addr) {
+    (void)fence;  // TODO: wait for fence
+    *addr = nullptr;
+    if (!mMappings.empty()) {
+        ALOGV("multiple map");
+        // TODO: technically we should return DUPLICATE here, but our block views
+        // don't actually unmap, so we end up remapping the buffer multiple times.
+        //
+        // return C2_DUPLICATE;
+    }
+    if (size == 0) {
+        return C2_BAD_VALUE;
+    }
+
+    int prot = PROT_NONE;
+    int flags = MAP_SHARED;
+    if (usage.expected & C2MemoryUsage::CPU_READ) {
+        prot |= PROT_READ;
+    }
+    if (usage.expected & C2MemoryUsage::CPU_WRITE) {
+        prot |= PROT_WRITE;
+    }
+
+    size_t alignmentBytes = offset % PAGE_SIZE;
+    size_t mapOffset = offset - alignmentBytes;
+    size_t mapSize = size + alignmentBytes;
+    Mapping map = {nullptr, alignmentBytes, mapSize};
+
+    c2_status_t err =
+            mapInternal(mapSize, mapOffset, alignmentBytes, prot, flags, &(map.addr), addr);
+    if (map.addr) {
+        mMappings.push_back(map);
+    }
+    return err;
+}
+
+c2_status_t C2DmaBufAllocation::unmap(void* addr, size_t size, C2Fence* fence) {
+    if (mMappings.empty()) {
+        ALOGD("tried to unmap unmapped buffer");
+        return C2_NOT_FOUND;
+    }
+    for (auto it = mMappings.begin(); it != mMappings.end(); ++it) {
+        if (addr != (uint8_t*)it->addr + it->alignmentBytes ||
+            size + it->alignmentBytes != it->size) {
+            continue;
+        }
+        int err = munmap(it->addr, it->size);
+        if (err != 0) {
+            ALOGD("munmap failed");
+            return c2_map_errno<EINVAL>(errno);
+        }
+        if (fence) {
+            *fence = C2Fence();  // not using fences
+        }
+        (void)mMappings.erase(it);
+        ALOGV("successfully unmapped: %d", mHandle.bufferFd());
+        return C2_OK;
+    }
+    ALOGD("unmap failed to find specified map");
+    return C2_BAD_VALUE;
+}
+
+c2_status_t C2DmaBufAllocation::status() const {
+    return mInit;
+}
+
+C2Allocator::id_t C2DmaBufAllocation::getAllocatorId() const {
+    return mId;
+}
+
+bool C2DmaBufAllocation::equals(const std::shared_ptr<C2LinearAllocation>& other) const {
+    if (!other || other->getAllocatorId() != getAllocatorId()) {
+        return false;
+    }
+    // get user handle to compare objects
+    std::shared_ptr<C2DmaBufAllocation> otherAsBuf =
+            std::static_pointer_cast<C2DmaBufAllocation>(other);
+    return mHandle.bufferFd() == otherAsBuf->mHandle.bufferFd();
+}
+
+const C2Handle* C2DmaBufAllocation::handle() const {
+    return &mHandle;
+}
+
+C2DmaBufAllocation::~C2DmaBufAllocation() {
+    if (!mMappings.empty()) {
+        ALOGD("Dangling mappings!");
+        for (const Mapping& map : mMappings) {
+            int err = munmap(map.addr, map.size);
+            if (err) ALOGD("munmap failed");
+        }
+    }
+    if (mInit == C2_OK) {
+        native_handle_close(&mHandle);
+    }
+}
+
+C2DmaBufAllocation::C2DmaBufAllocation(BufferAllocator& alloc, size_t size, C2String heap_name,
+                                       unsigned flags, C2Allocator::id_t id)
+    : C2LinearAllocation(size), mHandle(-1, 0) {
+    int bufferFd = -1;
+    int ret = 0;
+
+    bufferFd = alloc.Alloc(heap_name, size, flags);
+    if (bufferFd < 0) ret = bufferFd;
+
+    mHandle = C2HandleBuf(bufferFd, size);
+    mId = id;
+    mInit = c2_status_t(c2_map_errno<ENOMEM, EACCES, EINVAL>(ret));
+}
+
+C2DmaBufAllocation::C2DmaBufAllocation(size_t size, int shareFd, C2Allocator::id_t id)
+    : C2LinearAllocation(size), mHandle(-1, 0) {
+    mHandle = C2HandleBuf(shareFd, size);
+    mId = id;
+    mInit = c2_status_t(c2_map_errno<ENOMEM, EACCES, EINVAL>(0));
+}
+
+/* =========================== DMABUF ALLOCATOR =========================== */
+C2DmaBufAllocator::C2DmaBufAllocator(id_t id) : mInit(C2_OK) {
+    C2MemoryUsage minUsage = {0, 0};
+    C2MemoryUsage maxUsage = {C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE};
+    Traits traits = {"android.allocator.dmabuf", id, LINEAR, minUsage, maxUsage};
+    mTraits = std::make_shared<Traits>(traits);
+}
+
+C2Allocator::id_t C2DmaBufAllocator::getId() const {
+    std::lock_guard<std::mutex> lock(mUsageMapperLock);
+    return mTraits->id;
+}
+
+C2String C2DmaBufAllocator::getName() const {
+    std::lock_guard<std::mutex> lock(mUsageMapperLock);
+    return mTraits->name;
+}
+
+std::shared_ptr<const C2Allocator::Traits> C2DmaBufAllocator::getTraits() const {
+    std::lock_guard<std::mutex> lock(mUsageMapperLock);
+    return mTraits;
+}
+
+void C2DmaBufAllocator::setUsageMapper(const UsageMapperFn& mapper __unused, uint64_t minUsage,
+                                       uint64_t maxUsage, uint64_t blockSize) {
+    std::lock_guard<std::mutex> lock(mUsageMapperLock);
+    mUsageMapperCache.clear();
+    mUsageMapperLru.clear();
+    mUsageMapper = mapper;
+    Traits traits = {mTraits->name, mTraits->id, LINEAR, C2MemoryUsage(minUsage),
+                     C2MemoryUsage(maxUsage)};
+    mTraits = std::make_shared<Traits>(traits);
+    mBlockSize = blockSize;
+}
+
+std::size_t C2DmaBufAllocator::MapperKeyHash::operator()(const MapperKey& k) const {
+    return std::hash<uint64_t>{}(k.first) ^ std::hash<size_t>{}(k.second);
+}
+
+c2_status_t C2DmaBufAllocator::mapUsage(C2MemoryUsage usage, size_t capacity, C2String* heap_name,
+                                        unsigned* flags) {
+    std::lock_guard<std::mutex> lock(mUsageMapperLock);
+    c2_status_t res = C2_OK;
+    // align capacity
+    capacity = (capacity + mBlockSize - 1) & ~(mBlockSize - 1);
+    MapperKey key = std::make_pair(usage.expected, capacity);
+    auto entry = mUsageMapperCache.find(key);
+    if (entry == mUsageMapperCache.end()) {
+        if (mUsageMapper) {
+            res = mUsageMapper(usage, capacity, heap_name, flags);
+        } else {
+            // No system-uncached yet, so disabled for now
+            if (0 && !(usage.expected & (C2MemoryUsage::CPU_READ | C2MemoryUsage::CPU_WRITE)))
+                *heap_name = "system-uncached";
+            else
+                *heap_name = "system";
+            *flags = 0;
+            res = C2_NO_INIT;
+        }
+        // add usage to cache
+        MapperValue value = std::make_tuple(*heap_name, *flags, res);
+        mUsageMapperLru.emplace_front(key, value);
+        mUsageMapperCache.emplace(std::make_pair(key, mUsageMapperLru.begin()));
+        if (mUsageMapperCache.size() > USAGE_LRU_CACHE_SIZE) {
+            // remove LRU entry
+            MapperKey lruKey = mUsageMapperLru.front().first;
+            mUsageMapperCache.erase(lruKey);
+            mUsageMapperLru.pop_back();
+        }
+    } else {
+        // move entry to MRU
+        mUsageMapperLru.splice(mUsageMapperLru.begin(), mUsageMapperLru, entry->second);
+        const MapperValue& value = entry->second->second;
+        std::tie(*heap_name, *flags, res) = value;
+    }
+    return res;
+}
+
+c2_status_t C2DmaBufAllocator::newLinearAllocation(
+        uint32_t capacity, C2MemoryUsage usage, std::shared_ptr<C2LinearAllocation>* allocation) {
+    if (allocation == nullptr) {
+        return C2_BAD_VALUE;
+    }
+
+    allocation->reset();
+    if (mInit != C2_OK) {
+        return mInit;
+    }
+
+    C2String heap_name;
+    unsigned flags = 0;
+    c2_status_t ret = mapUsage(usage, capacity, &heap_name, &flags);
+    if (ret && ret != C2_NO_INIT) {
+        return ret;
+    }
+
+    std::shared_ptr<C2DmaBufAllocation> alloc = std::make_shared<C2DmaBufAllocation>(
+            mBufferAllocator, capacity, heap_name, flags, getId());
+    ret = alloc->status();
+    if (ret == C2_OK) {
+        *allocation = alloc;
+    }
+    return ret;
+}
+
+c2_status_t C2DmaBufAllocator::priorLinearAllocation(
+        const C2Handle* handle, std::shared_ptr<C2LinearAllocation>* allocation) {
+    *allocation = nullptr;
+    if (mInit != C2_OK) {
+        return mInit;
+    }
+
+    if (!C2HandleBuf::IsValid(handle)) {
+        return C2_BAD_VALUE;
+    }
+
+    // TODO: get capacity and validate it
+    const C2HandleBuf* h = static_cast<const C2HandleBuf*>(handle);
+    std::shared_ptr<C2DmaBufAllocation> alloc =
+            std::make_shared<C2DmaBufAllocation>(h->size(), h->bufferFd(), getId());
+    c2_status_t ret = alloc->status();
+    if (ret == C2_OK) {
+        *allocation = alloc;
+        native_handle_delete(
+                const_cast<native_handle_t*>(reinterpret_cast<const native_handle_t*>(handle)));
+    }
+    return ret;
+}
+
+// static
+bool C2DmaBufAllocator::CheckHandle(const C2Handle* const o) {
+    return C2HandleBuf::IsValid(o);
+}
+
+}  // namespace android
diff --git a/media/codec2/vndk/C2Store.cpp b/media/codec2/vndk/C2Store.cpp
index d16527e..1e907c1 100644
--- a/media/codec2/vndk/C2Store.cpp
+++ b/media/codec2/vndk/C2Store.cpp
@@ -21,6 +21,7 @@
 #include <C2AllocatorBlob.h>
 #include <C2AllocatorGralloc.h>
 #include <C2AllocatorIon.h>
+#include <C2DmaBufAllocator.h>
 #include <C2BufferPriv.h>
 #include <C2BqBufferPriv.h>
 #include <C2Component.h>
@@ -82,6 +83,7 @@
 
     /// returns a shared-singleton ion allocator
     std::shared_ptr<C2Allocator> fetchIonAllocator();
+    std::shared_ptr<C2Allocator> fetchDmaBufAllocator();
 
     /// returns a shared-singleton gralloc allocator
     std::shared_ptr<C2Allocator> fetchGrallocAllocator();
@@ -99,6 +101,20 @@
 C2PlatformAllocatorStoreImpl::C2PlatformAllocatorStoreImpl() {
 }
 
+static bool using_ion(void) {
+    static int cached_result = -1;
+
+    if (cached_result == -1) {
+        struct stat buffer;
+        cached_result = (stat("/dev/ion", &buffer) == 0);
+        if (cached_result)
+            ALOGD("Using ION\n");
+        else
+            ALOGD("Using DMABUF Heaps\n");
+    }
+    return (cached_result == 1);
+}
+
 c2_status_t C2PlatformAllocatorStoreImpl::fetchAllocator(
         id_t id, std::shared_ptr<C2Allocator> *const allocator) {
     allocator->reset();
@@ -107,8 +123,11 @@
     }
     switch (id) {
     // TODO: should we implement a generic registry for all, and use that?
-    case C2PlatformAllocatorStore::ION:
-        *allocator = fetchIonAllocator();
+    case C2PlatformAllocatorStore::ION: /* also ::DMABUFHEAP */
+        if (using_ion())
+            *allocator = fetchIonAllocator();
+        else
+            *allocator = fetchDmaBufAllocator();
         break;
 
     case C2PlatformAllocatorStore::GRALLOC:
@@ -142,7 +161,9 @@
 namespace {
 
 std::mutex gIonAllocatorMutex;
+std::mutex gDmaBufAllocatorMutex;
 std::weak_ptr<C2AllocatorIon> gIonAllocator;
+std::weak_ptr<C2DmaBufAllocator> gDmaBufAllocator;
 
 void UseComponentStoreForIonAllocator(
         const std::shared_ptr<C2AllocatorIon> allocator,
@@ -197,6 +218,65 @@
     allocator->setUsageMapper(mapper, minUsage, maxUsage, blockSize);
 }
 
+void UseComponentStoreForDmaBufAllocator(const std::shared_ptr<C2DmaBufAllocator> allocator,
+                                         std::shared_ptr<C2ComponentStore> store) {
+    C2DmaBufAllocator::UsageMapperFn mapper;
+    const size_t maxHeapNameLen = 128;
+    uint64_t minUsage = 0;
+    uint64_t maxUsage = C2MemoryUsage(C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE).expected;
+    size_t blockSize = getpagesize();
+
+    // query min and max usage as well as block size via supported values
+    std::unique_ptr<C2StoreDmaBufUsageInfo> usageInfo;
+    usageInfo = C2StoreDmaBufUsageInfo::AllocUnique(maxHeapNameLen);
+
+    std::vector<C2FieldSupportedValuesQuery> query = {
+            C2FieldSupportedValuesQuery::Possible(C2ParamField::Make(*usageInfo, usageInfo->m.usage)),
+            C2FieldSupportedValuesQuery::Possible(
+                    C2ParamField::Make(*usageInfo, usageInfo->m.capacity)),
+    };
+    c2_status_t res = store->querySupportedValues_sm(query);
+    if (res == C2_OK) {
+        if (query[0].status == C2_OK) {
+            const C2FieldSupportedValues& fsv = query[0].values;
+            if (fsv.type == C2FieldSupportedValues::FLAGS && !fsv.values.empty()) {
+                minUsage = fsv.values[0].u64;
+                maxUsage = 0;
+                for (C2Value::Primitive v : fsv.values) {
+                    maxUsage |= v.u64;
+                }
+            }
+        }
+        if (query[1].status == C2_OK) {
+            const C2FieldSupportedValues& fsv = query[1].values;
+            if (fsv.type == C2FieldSupportedValues::RANGE && fsv.range.step.u32 > 0) {
+                blockSize = fsv.range.step.u32;
+            }
+        }
+
+        mapper = [store](C2MemoryUsage usage, size_t capacity, C2String* heapName,
+                         unsigned* flags) -> c2_status_t {
+            if (capacity > UINT32_MAX) {
+                return C2_BAD_VALUE;
+            }
+
+            std::unique_ptr<C2StoreDmaBufUsageInfo> usageInfo;
+            usageInfo = C2StoreDmaBufUsageInfo::AllocUnique(maxHeapNameLen, usage.expected, capacity);
+            std::vector<std::unique_ptr<C2SettingResult>> failures;  // TODO: remove
+
+            c2_status_t res = store->config_sm({&*usageInfo}, &failures);
+            if (res == C2_OK) {
+                *heapName = C2String(usageInfo->m.heapName);
+                *flags = usageInfo->m.allocFlags;
+            }
+
+            return res;
+        };
+    }
+
+    allocator->setUsageMapper(mapper, minUsage, maxUsage, blockSize);
+}
+
 }
 
 void C2PlatformAllocatorStoreImpl::setComponentStore(std::shared_ptr<C2ComponentStore> store) {
@@ -233,6 +313,22 @@
     return allocator;
 }
 
+std::shared_ptr<C2Allocator> C2PlatformAllocatorStoreImpl::fetchDmaBufAllocator() {
+    std::lock_guard<std::mutex> lock(gDmaBufAllocatorMutex);
+    std::shared_ptr<C2DmaBufAllocator> allocator = gDmaBufAllocator.lock();
+    if (allocator == nullptr) {
+        std::shared_ptr<C2ComponentStore> componentStore;
+        {
+            std::lock_guard<std::mutex> lock(_mComponentStoreReadLock);
+            componentStore = _mComponentStore;
+        }
+        allocator = std::make_shared<C2DmaBufAllocator>(C2PlatformAllocatorStore::DMABUFHEAP);
+        UseComponentStoreForDmaBufAllocator(allocator, componentStore);
+        gDmaBufAllocator = allocator;
+    }
+    return allocator;
+}
+
 std::shared_ptr<C2Allocator> C2PlatformAllocatorStoreImpl::fetchBlobAllocator() {
     static std::mutex mutex;
     static std::weak_ptr<C2Allocator> blobAllocator;
@@ -347,7 +443,7 @@
             allocatorId = GetPreferredLinearAllocatorId(GetCodec2PoolMask());
         }
         switch(allocatorId) {
-            case C2PlatformAllocatorStore::ION:
+            case C2PlatformAllocatorStore::ION: /* also ::DMABUFHEAP */
                 res = allocatorStore->fetchAllocator(
                         C2PlatformAllocatorStore::ION, &allocator);
                 if (res == C2_OK) {
@@ -645,6 +741,7 @@
 
     struct Interface : public C2InterfaceHelper {
         std::shared_ptr<C2StoreIonUsageInfo> mIonUsageInfo;
+        std::shared_ptr<C2StoreDmaBufUsageInfo> mDmaBufUsageInfo;
 
         Interface(std::shared_ptr<C2ReflectorHelper> reflector)
             : C2InterfaceHelper(reflector) {
@@ -680,7 +777,13 @@
                     me.set().minAlignment = 0;
 #endif
                     return C2R::Ok();
-                }
+                };
+
+                static C2R setDmaBufUsage(bool /* mayBlock */, C2P<C2StoreDmaBufUsageInfo> &me) {
+                    strncpy(me.set().m.heapName, "system", me.v.flexCount());
+                    me.set().m.allocFlags = 0;
+                    return C2R::Ok();
+                };
             };
 
             addParameter(
@@ -695,6 +798,18 @@
                 })
                 .withSetter(Setter::setIonUsage)
                 .build());
+
+            addParameter(
+                DefineParam(mDmaBufUsageInfo, "dmabuf-usage")
+                .withDefault(C2StoreDmaBufUsageInfo::AllocShared(0))
+                .withFields({
+                    C2F(mDmaBufUsageInfo, m.usage).flags({C2MemoryUsage::CPU_READ | C2MemoryUsage::CPU_WRITE}),
+                    C2F(mDmaBufUsageInfo, m.capacity).inRange(0, UINT32_MAX, 1024),
+                    C2F(mDmaBufUsageInfo, m.allocFlags).flags({}),
+                    C2F(mDmaBufUsageInfo, m.heapName).any(),
+                })
+                .withSetter(Setter::setDmaBufUsage)
+                .build());
         }
     };
 
diff --git a/media/codec2/vndk/include/C2DmaBufAllocator.h b/media/codec2/vndk/include/C2DmaBufAllocator.h
new file mode 100644
index 0000000..abb8307
--- /dev/null
+++ b/media/codec2/vndk/include/C2DmaBufAllocator.h
@@ -0,0 +1,112 @@
+/*
+ * 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 STAGEFRIGHT_CODEC2_ALLOCATOR_BUF_H_
+#define STAGEFRIGHT_CODEC2_ALLOCATOR_BUF_H_
+
+#include <BufferAllocator/BufferAllocator.h>
+#include <C2Buffer.h>
+#include <sys/stat.h>  // stat
+
+#include <functional>
+#include <list>
+#include <mutex>
+#include <tuple>
+#include <unordered_map>
+
+namespace android {
+
+class C2DmaBufAllocator : public C2Allocator {
+   public:
+    virtual c2_status_t newLinearAllocation(
+            uint32_t capacity, C2MemoryUsage usage,
+            std::shared_ptr<C2LinearAllocation>* allocation) override;
+
+    virtual c2_status_t priorLinearAllocation(
+            const C2Handle* handle, std::shared_ptr<C2LinearAllocation>* allocation) override;
+
+    C2DmaBufAllocator(id_t id);
+
+    virtual c2_status_t status() const { return mInit; }
+
+    virtual bool checkHandle(const C2Handle* const o) const override { return CheckHandle(o); }
+
+    static bool CheckHandle(const C2Handle* const o);
+
+    virtual id_t getId() const override;
+
+    virtual C2String getName() const override;
+
+    virtual std::shared_ptr<const Traits> getTraits() const override;
+
+    // Usage mapper function used by the allocator
+    //   (usage, capacity) => (heapName, flags)
+    //
+    // capacity is aligned to the default block-size (defaults to page size) to
+    // reduce caching overhead
+    typedef std::function<c2_status_t(C2MemoryUsage, size_t,
+                                      /* => */ C2String*, unsigned*)>
+            UsageMapperFn;
+
+    /**
+     * Updates the usage mapper for subsequent new allocations, as well as the
+     * supported minimum and maximum usage masks and default block-size to use
+     * for the mapper.
+     *
+     * \param mapper          This method is called to map Codec 2.0 buffer usage
+     *                        to dmabuf heap name and flags required by the dma
+     *                        buf heap device
+     *
+     * \param minUsage        Minimum buffer usage required for supported
+     *                        allocations (defaults to 0)
+     *
+     * \param maxUsage        Maximum buffer usage supported by the ion allocator
+     *                        (defaults to SW_READ | SW_WRITE)
+     *
+     * \param blockSize       Alignment used prior to calling |mapper| for the
+     *                        buffer capacity. This also helps reduce the size of
+     *                        cache required for caching mapper results.
+     *                        (defaults to the page size)
+     */
+    void setUsageMapper(const UsageMapperFn& mapper, uint64_t minUsage, uint64_t maxUsage,
+                        uint64_t blockSize);
+
+   private:
+    c2_status_t mInit;
+    BufferAllocator mBufferAllocator;
+
+    c2_status_t mapUsage(C2MemoryUsage usage, size_t size,
+                         /* => */ C2String* heap_name, unsigned* flags);
+
+    // this locks mTraits, mBlockSize, mUsageMapper, mUsageMapperLru and
+    // mUsageMapperCache
+    mutable std::mutex mUsageMapperLock;
+    std::shared_ptr<const Traits> mTraits;
+    size_t mBlockSize;
+    UsageMapperFn mUsageMapper;
+    typedef std::pair<uint64_t, size_t> MapperKey;
+    struct MapperKeyHash {
+        std::size_t operator()(const MapperKey&) const;
+    };
+    typedef std::tuple<C2String, unsigned, c2_status_t> MapperValue;
+    typedef std::pair<MapperKey, MapperValue> MapperKeyValue;
+    typedef std::list<MapperKeyValue>::iterator MapperKeyValuePointer;
+    std::list<MapperKeyValue> mUsageMapperLru;
+    std::unordered_map<MapperKey, MapperKeyValuePointer, MapperKeyHash> mUsageMapperCache;
+};
+}  // namespace android
+
+#endif  // STAGEFRIGHT_CODEC2_ALLOCATOR_BUF_H_
diff --git a/media/codec2/vndk/include/C2PlatformSupport.h b/media/codec2/vndk/include/C2PlatformSupport.h
index a14e0d3..4814494 100644
--- a/media/codec2/vndk/include/C2PlatformSupport.h
+++ b/media/codec2/vndk/include/C2PlatformSupport.h
@@ -47,6 +47,17 @@
          */
         ION = PLATFORM_START,
 
+        /*
+         * ID of the DMA-Buf Heap (ion replacement) backed platform allocator.
+         *
+         * C2Handle consists of:
+         *   fd  shared dmabuf buffer handle
+         *   int size (lo 32 bits)
+         *   int size (hi 32 bits)
+         *   int magic '\xc2io\x00'
+         */
+        DMABUFHEAP = ION,
+
         /**
          * ID of the gralloc backed platform allocator.
          *
diff --git a/media/extractors/mp4/ItemTable.cpp b/media/extractors/mp4/ItemTable.cpp
index 2599c2c..0b1cfa0 100644
--- a/media/extractors/mp4/ItemTable.cpp
+++ b/media/extractors/mp4/ItemTable.cpp
@@ -76,6 +76,7 @@
     size_t size;
     sp<ABuffer> hvcc;
     sp<ABuffer> icc;
+    sp<ABuffer> av1c;
 
     Vector<uint32_t> thumbnails;
     Vector<uint32_t> dimgRefs;
@@ -764,6 +765,39 @@
     return OK;
 }
 
+struct Av1cBox : public Box, public ItemProperty {
+    Av1cBox(DataSourceHelper *source) :
+        Box(source, FOURCC("av1C")) {}
+
+    status_t parse(off64_t offset, size_t size) override;
+
+    void attachTo(ImageItem &image) const override {
+        image.av1c = mAv1c;
+    }
+
+private:
+    sp<ABuffer> mAv1c;
+};
+
+status_t Av1cBox::parse(off64_t offset, size_t size) {
+    ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
+
+    mAv1c = new ABuffer(size);
+
+    if (mAv1c->data() == NULL) {
+        ALOGE("b/28471206");
+        return NO_MEMORY;
+    }
+
+    if (source()->readAt(offset, mAv1c->data(), size) < (ssize_t)size) {
+        return ERROR_IO;
+    }
+
+    ALOGV("property av1C");
+
+    return OK;
+}
+
 struct IrotBox : public Box, public ItemProperty {
     IrotBox(DataSourceHelper *source) :
         Box(source, FOURCC("irot")), mAngle(0) {}
@@ -957,6 +991,11 @@
             itemProperty = new ColrBox(source());
             break;
         }
+        case FOURCC("av1C"):
+        {
+            itemProperty = new Av1cBox(source());
+            break;
+        }
         default:
         {
             // push dummy to maintain correct item property index
@@ -1203,8 +1242,9 @@
 
 //////////////////////////////////////////////////////////////////
 
-ItemTable::ItemTable(DataSourceHelper *source)
+ItemTable::ItemTable(DataSourceHelper *source, bool isHeif)
     : mDataSource(source),
+      mIsHeif(isHeif),
       mPrimaryItemId(0),
       mIdatOffset(0),
       mIdatSize(0),
@@ -1363,7 +1403,8 @@
         //   'Exif': EXIF metadata
         if (info.itemType != FOURCC("grid") &&
             info.itemType != FOURCC("hvc1") &&
-            info.itemType != FOURCC("Exif")) {
+            info.itemType != FOURCC("Exif") &&
+            info.itemType != FOURCC("av01")) {
             continue;
         }
 
@@ -1509,7 +1550,9 @@
     }
 
     AMediaFormat *meta = AMediaFormat_new();
-    AMediaFormat_setString(meta, AMEDIAFORMAT_KEY_MIME, MEDIA_MIMETYPE_IMAGE_ANDROID_HEIC);
+    AMediaFormat_setString(
+        meta, AMEDIAFORMAT_KEY_MIME,
+        mIsHeif ? MEDIA_MIMETYPE_IMAGE_ANDROID_HEIC : MEDIA_MIMETYPE_IMAGE_AVIF);
 
     if (image->itemId == mPrimaryItemId) {
         AMediaFormat_setInt32(meta, AMEDIAFORMAT_KEY_IS_DEFAULT, 1);
@@ -1539,7 +1582,7 @@
         ssize_t thumbItemIndex = mItemIdToItemMap.indexOfKey(image->thumbnails[0]);
         if (thumbItemIndex >= 0) {
             const ImageItem &thumbnail = mItemIdToItemMap[thumbItemIndex];
-
+            // TODO(vigneshv): Handle thumbnail for AVIF.
             if (thumbnail.hvcc != NULL) {
                 AMediaFormat_setInt32(meta,
                         AMEDIAFORMAT_KEY_THUMBNAIL_WIDTH, thumbnail.width);
@@ -1574,12 +1617,21 @@
                 AMEDIAFORMAT_KEY_MAX_INPUT_SIZE, image->width * image->height * 3 / 2);
     }
 
-    if (image->hvcc == NULL) {
-        ALOGE("%s: hvcc is missing for image[%u]!", __FUNCTION__, imageIndex);
-        return NULL;
+    if (mIsHeif) {
+        if (image->hvcc == NULL) {
+            ALOGE("%s: hvcc is missing for image[%u]!", __FUNCTION__, imageIndex);
+            return NULL;
+        }
+        AMediaFormat_setBuffer(meta,
+                AMEDIAFORMAT_KEY_CSD_HEVC, image->hvcc->data(), image->hvcc->size());
+    } else {
+        if (image->av1c == NULL) {
+            ALOGE("%s: av1c is missing for image[%u]!", __FUNCTION__, imageIndex);
+            return NULL;
+        }
+        AMediaFormat_setBuffer(meta,
+                AMEDIAFORMAT_KEY_CSD_0, image->av1c->data(), image->av1c->size());
     }
-    AMediaFormat_setBuffer(meta,
-            AMEDIAFORMAT_KEY_CSD_HEVC, image->hvcc->data(), image->hvcc->size());
 
     if (image->icc != NULL) {
         AMediaFormat_setBuffer(meta,
diff --git a/media/extractors/mp4/ItemTable.h b/media/extractors/mp4/ItemTable.h
index be81b59..b19dc18 100644
--- a/media/extractors/mp4/ItemTable.h
+++ b/media/extractors/mp4/ItemTable.h
@@ -42,12 +42,12 @@
 
 /*
  * ItemTable keeps track of all image items (including coded images, grids and
- * tiles) inside a HEIF still image (ISO/IEC FDIS 23008-12.2:2017(E)).
+ * tiles) inside a HEIF/AVIF still image (ISO/IEC FDIS 23008-12.2:2017(E)).
  */
 
 class ItemTable : public RefBase {
 public:
-    explicit ItemTable(DataSourceHelper *source);
+    ItemTable(DataSourceHelper *source, bool isHeif);
 
     status_t parse(uint32_t type, off64_t offset, size_t size);
 
@@ -65,6 +65,8 @@
 
 private:
     DataSourceHelper *mDataSource;
+    // If this is true, then this item table is for a HEIF image. Otherwise it is for an AVIF image.
+    bool mIsHeif;
 
     KeyedVector<uint32_t, ItemLoc> mItemLocs;
     Vector<ItemInfo> mItemInfos;
diff --git a/media/extractors/mp4/MPEG4Extractor.cpp b/media/extractors/mp4/MPEG4Extractor.cpp
index bd36403..42bfc39 100644
--- a/media/extractors/mp4/MPEG4Extractor.cpp
+++ b/media/extractors/mp4/MPEG4Extractor.cpp
@@ -149,6 +149,7 @@
     uint8_t *mSrcBuffer;
 
     bool mIsHeif;
+    bool mIsAvif;
     bool mIsAudio;
     bool mIsUsac = false;
     sp<ItemTable> mItemTable;
@@ -414,6 +415,7 @@
       mIsHeif(false),
       mHasMoovBox(false),
       mPreferHeif(mime != NULL && !strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_HEIF)),
+      mIsAvif(false),
       mFirstTrack(NULL),
       mLastTrack(NULL) {
     ALOGV("mime=%s, mPreferHeif=%d", mime, mPreferHeif);
@@ -670,7 +672,7 @@
         }
     }
 
-    if (mIsHeif && (mItemTable != NULL) && (mItemTable->countImages() > 0)) {
+    if ((mIsAvif || mIsHeif) && (mItemTable != NULL) && (mItemTable->countImages() > 0)) {
         off64_t exifOffset;
         size_t exifSize;
         if (mItemTable->getExifOffsetAndSize(&exifOffset, &exifSize) == OK) {
@@ -696,7 +698,7 @@
             }
             mInitCheck = OK;
 
-            ALOGV("adding HEIF image track %u", imageIndex);
+            ALOGV("adding %s image track %u", mIsHeif ? "HEIF" : "AVIF", imageIndex);
             Track *track = new Track;
             if (mLastTrack != NULL) {
                 mLastTrack->next = track;
@@ -722,6 +724,10 @@
                 MEDIA_MIMETYPE_IMAGE_ANDROID_HEIC) != NULL) {
             AMediaFormat_setString(mFileMetaData,
                     AMEDIAFORMAT_KEY_MIME, MEDIA_MIMETYPE_CONTAINER_HEIF);
+        } else if (findTrackByMimePrefix(
+                MEDIA_MIMETYPE_IMAGE_AVIF) != NULL) {
+            AMediaFormat_setString(mFileMetaData,
+                    AMEDIAFORMAT_KEY_MIME, MEDIA_MIMETYPE_IMAGE_AVIF);
         } else {
             AMediaFormat_setString(mFileMetaData,
                     AMEDIAFORMAT_KEY_MIME, "application/octet-stream");
@@ -2576,9 +2582,9 @@
         case FOURCC("iref"):
         case FOURCC("ipro"):
         {
-            if (mIsHeif) {
+            if (mIsHeif || mIsAvif) {
                 if (mItemTable == NULL) {
-                    mItemTable = new ItemTable(mDataSource);
+                    mItemTable = new ItemTable(mDataSource, mIsHeif);
                 }
                 status_t err = mItemTable->parse(
                         chunk_type, data_offset, chunk_data_size);
@@ -3019,14 +3025,20 @@
                     mIsHeif = true;
                     brandSet.erase(FOURCC("mif1"));
                     brandSet.erase(FOURCC("heic"));
+                } else if (brandSet.count(FOURCC("avif")) > 0 ||
+                       brandSet.count(FOURCC("avis")) > 0) {
+                    ALOGV("identified AVIF image");
+                    mIsAvif = true;
+                    brandSet.erase(FOURCC("avif"));
+                    brandSet.erase(FOURCC("avis"));
                 }
 
                 if (!brandSet.empty()) {
                     // This means that the file should have moov box.
                     // It could be any iso files (mp4, heifs, etc.)
                     mHasMoovBox = true;
-                    if (mIsHeif) {
-                        ALOGV("identified HEIF image with other tracks");
+                    if (mIsHeif || mIsAvif) {
+                        ALOGV("identified %s image with other tracks", mIsHeif ? "HEIF" : "AVIF");
                     }
                 }
             }
@@ -4364,7 +4376,8 @@
         if (size != 24 || ((ptr[0] != 1 || ptr[1] != 0) && (ptr[0] != 2 || ptr[1] != 1))) {
             return NULL;
         }
-   } else if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AV1)) {
+   } else if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AV1)
+           || !strcasecmp(mime, MEDIA_MIMETYPE_IMAGE_AVIF)) {
         void *data;
         size_t size;
         if (!AMediaFormat_getBuffer(track->meta, AMEDIAFORMAT_KEY_CSD_0, &data, &size)) {
@@ -4376,6 +4389,9 @@
         if (size < 5 || ptr[0] != 0x81) {  // configurationVersion == 1
             return NULL;
         }
+        if (!strcasecmp(mime, MEDIA_MIMETYPE_IMAGE_AVIF)) {
+            itemTable = mItemTable;
+        }
     } else if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_VP9)) {
         void *data;
         size_t size;
@@ -4937,7 +4953,6 @@
       mStarted(false),
       mBuffer(NULL),
       mSrcBuffer(NULL),
-      mIsHeif(itemTable != NULL),
       mItemTable(itemTable),
       mElstShiftStartTicks(elstShiftStartTicks),
       mElstInitialEmptyEditTicks(elstInitialEmptyEditTicks) {
@@ -4972,6 +4987,8 @@
               !strcasecmp(mime, MEDIA_MIMETYPE_IMAGE_ANDROID_HEIC);
     mIsAC4 = !strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AC4);
     mIsDolbyVision = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_DOLBY_VISION);
+    mIsHeif = !strcasecmp(mime, MEDIA_MIMETYPE_IMAGE_ANDROID_HEIC) && mItemTable != NULL;
+    mIsAvif = !strcasecmp(mime, MEDIA_MIMETYPE_IMAGE_AVIF) && mItemTable != NULL;
 
     if (mIsAVC) {
         void *data;
@@ -5966,7 +5983,7 @@
 
     if (options && options->getSeekTo(&seekTimeUs, &mode)) {
         ALOGV("seekTimeUs:%" PRId64, seekTimeUs);
-        if (mIsHeif) {
+        if (mIsHeif || mIsAvif) {
             CHECK(mSampleTable == NULL);
             CHECK(mItemTable != NULL);
             int32_t imageIndex;
@@ -6111,7 +6128,7 @@
         newBuffer = true;
 
         status_t err;
-        if (!mIsHeif) {
+        if (!mIsHeif && !mIsAvif) {
             err = mSampleTable->getMetaDataForSample(mCurrentSampleIndex, &offset, &size,
                                                     (uint64_t*)&cts, &isSyncSample, &stts);
             if(err == OK) {
@@ -6750,7 +6767,8 @@
         || !memcmp(header, "ftypM4A ", 8) || !memcmp(header, "ftypf4v ", 8)
         || !memcmp(header, "ftypkddi", 8) || !memcmp(header, "ftypM4VP", 8)
         || !memcmp(header, "ftypmif1", 8) || !memcmp(header, "ftypheic", 8)
-        || !memcmp(header, "ftypmsf1", 8) || !memcmp(header, "ftyphevc", 8)) {
+        || !memcmp(header, "ftypmsf1", 8) || !memcmp(header, "ftyphevc", 8)
+        || !memcmp(header, "ftypavif", 8) || !memcmp(header, "ftypavis", 8)) {
         *confidence = 0.4;
 
         return true;
@@ -6786,6 +6804,8 @@
         FOURCC("heic"),  // HEIF image
         FOURCC("msf1"),  // HEIF image sequence
         FOURCC("hevc"),  // HEIF image sequence
+        FOURCC("avif"),  // AVIF image
+        FOURCC("avis"),  // AVIF image sequence
     };
 
     for (size_t i = 0;
diff --git a/media/extractors/mp4/MPEG4Extractor.h b/media/extractors/mp4/MPEG4Extractor.h
index bafc7f5..542a3e6 100644
--- a/media/extractors/mp4/MPEG4Extractor.h
+++ b/media/extractors/mp4/MPEG4Extractor.h
@@ -144,6 +144,7 @@
     bool mIsHeif;
     bool mHasMoovBox;
     bool mPreferHeif;
+    bool mIsAvif;
 
     Track *mFirstTrack, *mLastTrack;
 
diff --git a/media/extractors/wav/WAVExtractor.cpp b/media/extractors/wav/WAVExtractor.cpp
index d19447a..901b29d 100644
--- a/media/extractors/wav/WAVExtractor.cpp
+++ b/media/extractors/wav/WAVExtractor.cpp
@@ -95,9 +95,9 @@
     AMediaFormat *mMeta;
     uint16_t mWaveFormat;
     const bool mOutputFloat;
-    int32_t mSampleRate;
-    int32_t mNumChannels;
-    int32_t mBitsPerSample;
+    uint32_t mSampleRate;
+    uint32_t mNumChannels;
+    uint32_t mBitsPerSample;
     off64_t mOffset;
     size_t mSize;
     bool mStarted;
@@ -379,9 +379,9 @@
       mOffset(offset),
       mSize(size),
       mStarted(false) {
-    CHECK(AMediaFormat_getInt32(mMeta, AMEDIAFORMAT_KEY_SAMPLE_RATE, &mSampleRate));
-    CHECK(AMediaFormat_getInt32(mMeta, AMEDIAFORMAT_KEY_CHANNEL_COUNT, &mNumChannels));
-    CHECK(AMediaFormat_getInt32(mMeta, AMEDIAFORMAT_KEY_BITS_PER_SAMPLE, &mBitsPerSample));
+    CHECK(AMediaFormat_getInt32(mMeta, AMEDIAFORMAT_KEY_SAMPLE_RATE, (int32_t*) &mSampleRate));
+    CHECK(AMediaFormat_getInt32(mMeta, AMEDIAFORMAT_KEY_CHANNEL_COUNT, (int32_t*) &mNumChannels));
+    CHECK(AMediaFormat_getInt32(mMeta, AMEDIAFORMAT_KEY_BITS_PER_SAMPLE, (int32_t*) &mBitsPerSample));
 }
 
 WAVSource::~WAVSource() {
@@ -472,7 +472,7 @@
     }
 
     const size_t maxBytesAvailable =
-        (mCurrentPos - mOffset >= (off64_t)mSize)
+        (mCurrentPos < mOffset || mCurrentPos - mOffset >= (off64_t)mSize)
             ? 0 : mSize - (mCurrentPos - mOffset);
 
     if (maxBytesToRead > maxBytesAvailable) {
diff --git a/media/libaaudio/src/Android.bp b/media/libaaudio/src/Android.bp
index 328ceda..aafcccc 100644
--- a/media/libaaudio/src/Android.bp
+++ b/media/libaaudio/src/Android.bp
@@ -21,6 +21,7 @@
     ],
 
     cflags: [
+        "-Wthread-safety",
         "-Wno-unused-parameter",
         "-Wall",
         "-Werror",
diff --git a/media/libaaudio/src/client/AudioStreamInternal.cpp b/media/libaaudio/src/client/AudioStreamInternal.cpp
index 94f10e5..809c76e 100644
--- a/media/libaaudio/src/client/AudioStreamInternal.cpp
+++ b/media/libaaudio/src/client/AudioStreamInternal.cpp
@@ -75,6 +75,7 @@
 }
 
 AudioStreamInternal::~AudioStreamInternal() {
+    ALOGD("%s() %p called", __func__, this);
 }
 
 aaudio_result_t AudioStreamInternal::open(const AudioStreamBuilder &builder) {
@@ -270,21 +271,21 @@
     return result;
 
 error:
-    releaseCloseFinal();
+    safeReleaseClose();
     return result;
 }
 
 // This must be called under mStreamLock.
 aaudio_result_t AudioStreamInternal::release_l() {
     aaudio_result_t result = AAUDIO_OK;
-    ALOGV("%s(): mServiceStreamHandle = 0x%08X", __func__, mServiceStreamHandle);
+    ALOGD("%s(): mServiceStreamHandle = 0x%08X", __func__, mServiceStreamHandle);
     if (mServiceStreamHandle != AAUDIO_HANDLE_INVALID) {
         aaudio_stream_state_t currentState = getState();
         // Don't release a stream while it is running. Stop it first.
         // If DISCONNECTED then we should still try to stop in case the
         // error callback is still running.
         if (isActive() || currentState == AAUDIO_STREAM_STATE_DISCONNECTED) {
-            requestStop();
+            requestStop_l();
         }
 
         logReleaseBufferState();
@@ -330,7 +331,7 @@
  * The processing code will then save the current offset
  * between client and server and apply that to any position given to the app.
  */
-aaudio_result_t AudioStreamInternal::requestStart()
+aaudio_result_t AudioStreamInternal::requestStart_l()
 {
     int64_t startTime;
     if (mServiceStreamHandle == AAUDIO_HANDLE_INVALID) {
@@ -373,7 +374,7 @@
                               * AAUDIO_NANOS_PER_SECOND
                               / getSampleRate();
         mCallbackEnabled.store(true);
-        result = createThread(periodNanos, aaudio_callback_thread_proc, this);
+        result = createThread_l(periodNanos, aaudio_callback_thread_proc, this);
     }
     if (result != AAUDIO_OK) {
         setState(originalState);
@@ -399,33 +400,36 @@
 }
 
 // This must be called under mStreamLock.
-aaudio_result_t AudioStreamInternal::stopCallback()
+aaudio_result_t AudioStreamInternal::stopCallback_l()
 {
     if (isDataCallbackSet()
             && (isActive() || getState() == AAUDIO_STREAM_STATE_DISCONNECTED)) {
         mCallbackEnabled.store(false);
-        aaudio_result_t result = joinThread(NULL); // may temporarily unlock mStreamLock
+        aaudio_result_t result = joinThread_l(NULL); // may temporarily unlock mStreamLock
         if (result == AAUDIO_ERROR_INVALID_HANDLE) {
             ALOGD("%s() INVALID_HANDLE, stream was probably stolen", __func__);
             result = AAUDIO_OK;
         }
         return result;
     } else {
+        ALOGD("%s() skipped, isDataCallbackSet() = %d, isActive() = %d, getState()  = %d", __func__,
+            isDataCallbackSet(), isActive(), getState());
         return AAUDIO_OK;
     }
 }
 
-// This must be called under mStreamLock.
-aaudio_result_t AudioStreamInternal::requestStop() {
-    aaudio_result_t result = stopCallback();
+aaudio_result_t AudioStreamInternal::requestStop_l() {
+    aaudio_result_t result = stopCallback_l();
     if (result != AAUDIO_OK) {
+        ALOGW("%s() stop callback returned %d, returning early", __func__, result);
         return result;
     }
     // The stream may have been unlocked temporarily to let a callback finish
     // and the callback may have stopped the stream.
     // Check to make sure the stream still needs to be stopped.
-    // See also AudioStream::safeStop().
+    // See also AudioStream::safeStop_l().
     if (!(isActive() || getState() == AAUDIO_STREAM_STATE_DISCONNECTED)) {
+        ALOGD("%s() returning early, not active or disconnected", __func__);
         return AAUDIO_OK;
     }
 
@@ -805,11 +809,6 @@
     return mBufferCapacityInFrames;
 }
 
-// This must be called under mStreamLock.
-aaudio_result_t AudioStreamInternal::joinThread(void** returnArg) {
-    return AudioStream::joinThread(returnArg, calculateReasonableTimeout(getFramesPerBurst()));
-}
-
 bool AudioStreamInternal::isClockModelInControl() const {
     return isActive() && mAudioEndpoint->isFreeRunning() && mClockModel.isRunning();
 }
diff --git a/media/libaaudio/src/client/AudioStreamInternal.h b/media/libaaudio/src/client/AudioStreamInternal.h
index d7024cf..fbe4c13 100644
--- a/media/libaaudio/src/client/AudioStreamInternal.h
+++ b/media/libaaudio/src/client/AudioStreamInternal.h
@@ -44,10 +44,6 @@
     AudioStreamInternal(AAudioServiceInterface  &serviceInterface, bool inService);
     virtual ~AudioStreamInternal();
 
-    aaudio_result_t requestStart() override;
-
-    aaudio_result_t requestStop() override;
-
     aaudio_result_t getTimestamp(clockid_t clockId,
                                        int64_t *framePosition,
                                        int64_t *timeNanoseconds) override;
@@ -56,8 +52,6 @@
 
     aaudio_result_t open(const AudioStreamBuilder &builder) override;
 
-    aaudio_result_t release_l() override;
-
     aaudio_result_t setBufferSize(int32_t requestedFrames) override;
 
     int32_t getBufferSize() const override;
@@ -72,12 +66,9 @@
 
     aaudio_result_t unregisterThread() override;
 
-    aaudio_result_t joinThread(void** returnArg);
-
     // Called internally from 'C'
     virtual void *callbackLoop() = 0;
 
-
     bool isMMap() override {
         return true;
     }
@@ -96,6 +87,10 @@
     }
 
 protected:
+    aaudio_result_t requestStart_l() REQUIRES(mStreamLock) override;
+    aaudio_result_t requestStop_l() REQUIRES(mStreamLock) override;
+
+    aaudio_result_t release_l() REQUIRES(mStreamLock) override;
 
     aaudio_result_t processData(void *buffer,
                          int32_t numFrames,
@@ -117,7 +112,7 @@
 
     aaudio_result_t processCommands();
 
-    aaudio_result_t stopCallback();
+    aaudio_result_t stopCallback_l();
 
     virtual void prepareBuffersForStart() {}
 
diff --git a/media/libaaudio/src/client/AudioStreamInternalPlay.cpp b/media/libaaudio/src/client/AudioStreamInternalPlay.cpp
index 980592c..b81e5e4 100644
--- a/media/libaaudio/src/client/AudioStreamInternalPlay.cpp
+++ b/media/libaaudio/src/client/AudioStreamInternalPlay.cpp
@@ -56,7 +56,7 @@
                              getDeviceChannelCount());
 
         if (result != AAUDIO_OK) {
-            releaseCloseFinal();
+            safeReleaseClose();
         }
         // Sample rate is constrained to common values by now and should not overflow.
         int32_t numFrames = kRampMSec * getSampleRate() / AAUDIO_MILLIS_PER_SECOND;
@@ -66,9 +66,9 @@
 }
 
 // This must be called under mStreamLock.
-aaudio_result_t AudioStreamInternalPlay::requestPause()
+aaudio_result_t AudioStreamInternalPlay::requestPause_l()
 {
-    aaudio_result_t result = stopCallback();
+    aaudio_result_t result = stopCallback_l();
     if (result != AAUDIO_OK) {
         return result;
     }
@@ -83,7 +83,7 @@
     return mServiceInterface.pauseStream(mServiceStreamHandle);
 }
 
-aaudio_result_t AudioStreamInternalPlay::requestFlush() {
+aaudio_result_t AudioStreamInternalPlay::requestFlush_l() {
     if (mServiceStreamHandle == AAUDIO_HANDLE_INVALID) {
         ALOGW("%s() mServiceStreamHandle invalid", __func__);
         return AAUDIO_ERROR_INVALID_STATE;
diff --git a/media/libaaudio/src/client/AudioStreamInternalPlay.h b/media/libaaudio/src/client/AudioStreamInternalPlay.h
index 7b1cddc..03c957d 100644
--- a/media/libaaudio/src/client/AudioStreamInternalPlay.h
+++ b/media/libaaudio/src/client/AudioStreamInternalPlay.h
@@ -35,9 +35,9 @@
 
     aaudio_result_t open(const AudioStreamBuilder &builder) override;
 
-    aaudio_result_t requestPause() override;
+    aaudio_result_t requestPause_l() override;
 
-    aaudio_result_t requestFlush() override;
+    aaudio_result_t requestFlush_l() override;
 
     bool isFlushSupported() const override {
         // Only implement FLUSH for OUTPUT streams.
diff --git a/media/libaaudio/src/core/AudioStream.cpp b/media/libaaudio/src/core/AudioStream.cpp
index ac2da57..57c4c16 100644
--- a/media/libaaudio/src/core/AudioStream.cpp
+++ b/media/libaaudio/src/core/AudioStream.cpp
@@ -42,16 +42,20 @@
         : mPlayerBase(new MyPlayerBase())
         , mStreamId(AAudio_getNextStreamId())
         {
-    // mThread is a pthread_t of unknown size so we need memset.
-    memset(&mThread, 0, sizeof(mThread));
     setPeriodNanoseconds(0);
 }
 
 AudioStream::~AudioStream() {
-    // Please preserve this log because there have been several bugs related to
+    // Please preserve these logs because there have been several bugs related to
     // AudioStream deletion and late callbacks.
     ALOGD("%s(s#%u) mPlayerBase strongCount = %d",
             __func__, getId(), mPlayerBase->getStrongCount());
+
+    ALOGE_IF(pthread_equal(pthread_self(), mThread),
+            "%s() destructor running in callback", __func__);
+
+    ALOGE_IF(mHasThread, "%s() callback thread never join()ed", __func__);
+
     // If the stream is deleted when OPEN or in use then audio resources will leak.
     // This would indicate an internal error. So we want to find this ASAP.
     LOG_ALWAYS_FATAL_IF(!(getState() == AAUDIO_STREAM_STATE_CLOSED
@@ -164,7 +168,7 @@
             return AAUDIO_ERROR_INVALID_STATE;
     }
 
-    aaudio_result_t result = requestStart();
+    aaudio_result_t result = requestStart_l();
     if (result == AAUDIO_OK) {
         // We only call this for logging in "dumpsys audio". So ignore return code.
         (void) mPlayerBase->start();
@@ -214,7 +218,7 @@
             return AAUDIO_ERROR_INVALID_STATE;
     }
 
-    aaudio_result_t result = requestPause();
+    aaudio_result_t result = requestPause_l();
     if (result == AAUDIO_OK) {
         // We only call this for logging in "dumpsys audio". So ignore return code.
         (void) mPlayerBase->pause();
@@ -239,12 +243,12 @@
         return result;
     }
 
-    return requestFlush();
+    return requestFlush_l();
 }
 
 aaudio_result_t AudioStream::systemStopFromCallback() {
     std::lock_guard<std::mutex> lock(mStreamLock);
-    aaudio_result_t result = safeStop();
+    aaudio_result_t result = safeStop_l();
     if (result == AAUDIO_OK) {
         // We only call this for logging in "dumpsys audio". So ignore return code.
         (void) mPlayerBase->stop();
@@ -258,7 +262,7 @@
         ALOGE("stream cannot be stopped by calling from a callback!");
         return AAUDIO_ERROR_INVALID_STATE;
     }
-    aaudio_result_t result = safeStop();
+    aaudio_result_t result = safeStop_l();
     if (result == AAUDIO_OK) {
         // We only call this for logging in "dumpsys audio". So ignore return code.
         (void) mPlayerBase->stop();
@@ -266,8 +270,7 @@
     return result;
 }
 
-// This must be called under mStreamLock.
-aaudio_result_t AudioStream::safeStop() {
+aaudio_result_t AudioStream::safeStop_l() {
 
     switch (getState()) {
         // Proceed with stopping.
@@ -299,11 +302,11 @@
             return AAUDIO_ERROR_INVALID_STATE;
     }
 
-    return requestStop();
+    return requestStop_l();
 }
 
 aaudio_result_t AudioStream::safeRelease() {
-    // This get temporarily unlocked in the MMAP release() when joining callback threads.
+    // This may get temporarily unlocked in the MMAP release() when joining callback threads.
     std::lock_guard<std::mutex> lock(mStreamLock);
     if (collidesWithCallback()) {
         ALOGE("%s cannot be called from a callback!", __func__);
@@ -322,7 +325,14 @@
         ALOGE("%s cannot be called from a callback!", __func__);
         return AAUDIO_ERROR_INVALID_STATE;
     }
-    releaseCloseFinal();
+    releaseCloseFinal_l();
+    return AAUDIO_OK;
+}
+
+aaudio_result_t AudioStream::safeReleaseCloseFromCallback() {
+    // This get temporarily unlocked in the MMAP release() when joining callback threads.
+    std::lock_guard<std::mutex> lock(mStreamLock);
+    releaseCloseFinal_l();
     return AAUDIO_OK;
 }
 
@@ -403,21 +413,28 @@
     return procResult;
 }
 
-// This is the entry point for the new thread created by createThread().
+
+// This is the entry point for the new thread created by createThread_l().
 // It converts the 'C' function call to a C++ method call.
 static void* AudioStream_internalThreadProc(void* threadArg) {
     AudioStream *audioStream = (AudioStream *) threadArg;
-    return audioStream->wrapUserThread();
+    // Prevent the stream from being deleted while being used.
+    // This is just for extra safety. It is probably not needed because
+    // this callback should be joined before the stream is closed.
+    android::sp<AudioStream> protectedStream(audioStream);
+    // Balance the incStrong() in createThread_l().
+    protectedStream->decStrong(nullptr);
+    return protectedStream->wrapUserThread();
 }
 
 // This is not exposed in the API.
 // But it is still used internally to implement callbacks for MMAP mode.
-aaudio_result_t AudioStream::createThread(int64_t periodNanoseconds,
-                                     aaudio_audio_thread_proc_t threadProc,
-                                     void* threadArg)
+aaudio_result_t AudioStream::createThread_l(int64_t periodNanoseconds,
+                                            aaudio_audio_thread_proc_t threadProc,
+                                            void* threadArg)
 {
     if (mHasThread) {
-        ALOGE("createThread() - mHasThread already true");
+        ALOGE("%s() - mHasThread already true", __func__);
         return AAUDIO_ERROR_INVALID_STATE;
     }
     if (threadProc == nullptr) {
@@ -427,10 +444,14 @@
     mThreadProc = threadProc;
     mThreadArg = threadArg;
     setPeriodNanoseconds(periodNanoseconds);
+    // Prevent this object from getting deleted before the thread has a chance to create
+    // its strong pointer. Assume the thread will call decStrong().
+    this->incStrong(nullptr);
     int err = pthread_create(&mThread, nullptr, AudioStream_internalThreadProc, this);
     if (err != 0) {
         android::status_t status = -errno;
-        ALOGE("createThread() - pthread_create() failed, %d", status);
+        ALOGE("%s() - pthread_create() failed, %d", __func__, status);
+        this->decStrong(nullptr); // Because the thread won't do it.
         return AAudioConvert_androidToAAudioResult(status);
     } else {
         // TODO Use AAudioThread or maybe AndroidThread
@@ -450,36 +471,39 @@
     }
 }
 
+aaudio_result_t AudioStream::joinThread(void** returnArg) {
+    // This may get temporarily unlocked in the MMAP release() when joining callback threads.
+    std::lock_guard<std::mutex> lock(mStreamLock);
+    return joinThread_l(returnArg);
+}
+
 // This must be called under mStreamLock.
-aaudio_result_t AudioStream::joinThread(void** returnArg, int64_t timeoutNanoseconds __unused)
-{
+aaudio_result_t AudioStream::joinThread_l(void** returnArg) {
     if (!mHasThread) {
-        ALOGE("joinThread() - but has no thread");
+        ALOGD("joinThread() - but has no thread");
         return AAUDIO_ERROR_INVALID_STATE;
     }
     aaudio_result_t result = AAUDIO_OK;
     // If the callback is stopping the stream because the app passed back STOP
     // then we don't need to join(). The thread is already about to exit.
-    if (pthread_self() != mThread) {
+    if (!pthread_equal(pthread_self(), mThread)) {
         // Called from an app thread. Not the callback.
         // Unlock because the callback may be trying to stop the stream but is blocked.
         mStreamLock.unlock();
-#if 0
-        // TODO implement equivalent of pthread_timedjoin_np()
-        struct timespec abstime;
-        int err = pthread_timedjoin_np(mThread, returnArg, &abstime);
-#else
         int err = pthread_join(mThread, returnArg);
-#endif
         mStreamLock.lock();
         if (err) {
             ALOGE("%s() pthread_join() returns err = %d", __func__, err);
             result = AAudioConvert_androidToAAudioResult(-err);
+        } else {
+            ALOGD("%s() pthread_join succeeded", __func__);
+            // This must be set false so that the callback thread can be created
+            // when the stream is restarted.
+            mHasThread = false;
         }
+    } else {
+        ALOGD("%s() pthread_join() called on itself!", __func__);
     }
-    // This must be set false so that the callback thread can be created
-    // when the stream is restarted.
-    mHasThread = false;
     return (result != AAUDIO_OK) ? result : mThreadRegistrationResult;
 }
 
@@ -582,7 +606,7 @@
     }
     if (audioStream) {
         // No pan and only left volume is taken into account from IPLayer interface
-        audioStream->setDuckAndMuteVolume(mVolumeMultiplierL  /* * mPanMultiplierL */);
+        audioStream->setDuckAndMuteVolume(mVolumeMultiplierL  /* mPanMultiplierL */);
     }
     return android::NO_ERROR;
 }
diff --git a/media/libaaudio/src/core/AudioStream.h b/media/libaaudio/src/core/AudioStream.h
index e438477..510ead8 100644
--- a/media/libaaudio/src/core/AudioStream.h
+++ b/media/libaaudio/src/core/AudioStream.h
@@ -20,11 +20,13 @@
 #include <atomic>
 #include <mutex>
 #include <stdint.h>
-#include <aaudio/AAudio.h>
+
+#include <android-base/thread_annotations.h>
 #include <binder/IServiceManager.h>
 #include <binder/Status.h>
 #include <utils/StrongPointer.h>
 
+#include <aaudio/AAudio.h>
 #include <media/AudioSystem.h>
 #include <media/PlayerBase.h>
 #include <media/VolumeShaper.h>
@@ -57,11 +59,6 @@
 
 protected:
 
-    /* Asynchronous requests.
-     * Use waitForStateChange() to wait for completion.
-     */
-    virtual aaudio_result_t requestStart() = 0;
-
     /**
      * Check the state to see if Pause is currently legal.
      *
@@ -80,18 +77,22 @@
         return false;
     }
 
-    virtual aaudio_result_t requestPause()
-    {
+    /* Asynchronous requests.
+     * Use waitForStateChange() to wait for completion.
+     */
+    virtual aaudio_result_t requestStart_l() REQUIRES(mStreamLock) = 0;
+
+    virtual aaudio_result_t requestPause_l() REQUIRES(mStreamLock) {
         // Only implement this for OUTPUT streams.
         return AAUDIO_ERROR_UNIMPLEMENTED;
     }
 
-    virtual aaudio_result_t requestFlush() {
+    virtual aaudio_result_t requestFlush_l() REQUIRES(mStreamLock) {
         // Only implement this for OUTPUT streams.
         return AAUDIO_ERROR_UNIMPLEMENTED;
     }
 
-    virtual aaudio_result_t requestStop() = 0;
+    virtual aaudio_result_t requestStop_l() REQUIRES(mStreamLock) = 0;
 
 public:
     virtual aaudio_result_t getTimestamp(clockid_t clockId,
@@ -131,11 +132,12 @@
      * The AAudioStream_close() method releases if needed and then closes.
      */
 
+protected:
     /**
      * Free any hardware or system resources from the open() call.
      * It is safe to call release_l() multiple times.
      */
-    virtual aaudio_result_t release_l() {
+    virtual aaudio_result_t release_l() REQUIRES(mStreamLock) {
         setState(AAUDIO_STREAM_STATE_CLOSING);
         return AAUDIO_OK;
     }
@@ -144,7 +146,7 @@
      * Free any resources not already freed by release_l().
      * Assume release_l() already called.
      */
-    virtual void close_l() {
+    virtual void close_l() REQUIRES(mStreamLock) {
         // Releasing the stream will set the state to CLOSING.
         assert(getState() == AAUDIO_STREAM_STATE_CLOSING);
         // setState() prevents a transition from CLOSING to any state other than CLOSED.
@@ -152,17 +154,7 @@
         setState(AAUDIO_STREAM_STATE_CLOSED);
     }
 
-    /**
-     * Release then close the stream.
-     */
-    void releaseCloseFinal() {
-        if (getState() != AAUDIO_STREAM_STATE_CLOSING) { // not already released?
-            // Ignore result and keep closing.
-            (void) release_l();
-        }
-        close_l();
-    }
-
+public:
     // This is only used to identify a stream in the logs without
     // revealing any pointers.
     aaudio_stream_id_t getId() {
@@ -171,11 +163,11 @@
 
     virtual aaudio_result_t setBufferSize(int32_t requestedFrames) = 0;
 
-    virtual aaudio_result_t createThread(int64_t periodNanoseconds,
-                                       aaudio_audio_thread_proc_t threadProc,
-                                       void *threadArg);
+    virtual aaudio_result_t createThread_l(int64_t periodNanoseconds,
+                                           aaudio_audio_thread_proc_t threadProc,
+                                           void *threadArg);
 
-    aaudio_result_t joinThread(void **returnArg, int64_t timeoutNanoseconds);
+    aaudio_result_t joinThread(void **returnArg);
 
     virtual aaudio_result_t registerThread() {
         return AAUDIO_OK;
@@ -432,6 +424,8 @@
      */
     aaudio_result_t safeReleaseClose();
 
+    aaudio_result_t safeReleaseCloseFromCallback();
+
 protected:
 
     // PlayerBase allows the system to control the stream volume.
@@ -483,9 +477,8 @@
 
     private:
         // Use a weak pointer so the AudioStream can be deleted.
-
         std::mutex               mParentLock;
-        android::wp<AudioStream> mParent;
+        android::wp<AudioStream> mParent GUARDED_BY(mParentLock);
         aaudio_result_t          mResult = AAUDIO_OK;
         bool                     mRegistered = false;
     };
@@ -543,6 +536,8 @@
         mSessionId = sessionId;
     }
 
+    aaudio_result_t joinThread_l(void **returnArg) REQUIRES(mStreamLock);
+
     std::atomic<bool>    mCallbackEnabled{false};
 
     float                mDuckAndMuteVolume = 1.0f;
@@ -609,11 +604,22 @@
 
     std::string mMetricsId; // set once during open()
 
+    std::mutex                 mStreamLock;
+
 private:
 
-    aaudio_result_t safeStop();
+    aaudio_result_t safeStop_l() REQUIRES(mStreamLock);
 
-    std::mutex                 mStreamLock;
+    /**
+     * Release then close the stream.
+     */
+    void releaseCloseFinal_l() REQUIRES(mStreamLock) {
+        if (getState() != AAUDIO_STREAM_STATE_CLOSING) { // not already released?
+            // Ignore result and keep closing.
+            (void) release_l();
+        }
+        close_l();
+    }
 
     const android::sp<MyPlayerBase>   mPlayerBase;
 
@@ -653,8 +659,8 @@
     std::atomic<pid_t>          mErrorCallbackThread{CALLBACK_THREAD_NONE};
 
     // background thread ----------------------------------
-    bool                        mHasThread = false;
-    pthread_t                   mThread; // initialized in constructor
+    bool                        mHasThread GUARDED_BY(mStreamLock) = false;
+    pthread_t                   mThread  GUARDED_BY(mStreamLock) = {};
 
     // These are set by the application thread and then read by the audio pthread.
     std::atomic<int64_t>        mPeriodNanoseconds; // for tuning SCHED_FIFO threads
diff --git a/media/libaaudio/src/legacy/AudioStreamLegacy.cpp b/media/libaaudio/src/legacy/AudioStreamLegacy.cpp
index 33c1bf5..fdaa2ab 100644
--- a/media/libaaudio/src/legacy/AudioStreamLegacy.cpp
+++ b/media/libaaudio/src/legacy/AudioStreamLegacy.cpp
@@ -226,7 +226,7 @@
             ALOGD("%s() request DISCONNECT in data callback, device %d => %d",
                   __func__, (int) getDeviceId(), (int) deviceId);
             // If the stream is stopped before the data callback has a chance to handle the
-            // request then the requestStop() and requestPause() methods will handle it after
+            // request then the requestStop_l() and requestPause() methods will handle it after
             // the callback has stopped.
             mRequestDisconnect.request();
         } else {
diff --git a/media/libaaudio/src/legacy/AudioStreamRecord.cpp b/media/libaaudio/src/legacy/AudioStreamRecord.cpp
index d46ef56..45b2258 100644
--- a/media/libaaudio/src/legacy/AudioStreamRecord.cpp
+++ b/media/libaaudio/src/legacy/AudioStreamRecord.cpp
@@ -185,7 +185,7 @@
         // Did we get a valid track?
         status_t status = mAudioRecord->initCheck();
         if (status != OK) {
-            releaseCloseFinal();
+            safeReleaseClose();
             ALOGE("open(), initCheck() returned %d", status);
             return AAudioConvert_androidToAAudioResult(status);
         }
@@ -341,7 +341,7 @@
     return;
 }
 
-aaudio_result_t AudioStreamRecord::requestStart()
+aaudio_result_t AudioStreamRecord::requestStart_l()
 {
     if (mAudioRecord.get() == nullptr) {
         return AAUDIO_ERROR_INVALID_STATE;
@@ -365,7 +365,7 @@
     return AAUDIO_OK;
 }
 
-aaudio_result_t AudioStreamRecord::requestStop() {
+aaudio_result_t AudioStreamRecord::requestStop_l() {
     if (mAudioRecord.get() == nullptr) {
         return AAUDIO_ERROR_INVALID_STATE;
     }
diff --git a/media/libaaudio/src/legacy/AudioStreamRecord.h b/media/libaaudio/src/legacy/AudioStreamRecord.h
index ad8dfe4..b2f8ba5 100644
--- a/media/libaaudio/src/legacy/AudioStreamRecord.h
+++ b/media/libaaudio/src/legacy/AudioStreamRecord.h
@@ -41,9 +41,6 @@
     aaudio_result_t release_l() override;
     void close_l() override;
 
-    aaudio_result_t requestStart() override;
-    aaudio_result_t requestStop() override;
-
     virtual aaudio_result_t getTimestamp(clockid_t clockId,
                                          int64_t *framePosition,
                                          int64_t *timeNanoseconds) override;
@@ -77,6 +74,9 @@
 
 protected:
 
+    aaudio_result_t requestStart_l() REQUIRES(mStreamLock) override;
+    aaudio_result_t requestStop_l() REQUIRES(mStreamLock) override;
+
     int32_t getFramesPerBurstFromDevice() const override;
     int32_t getBufferCapacityFromDevice() const override;
 
diff --git a/media/libaaudio/src/legacy/AudioStreamTrack.cpp b/media/libaaudio/src/legacy/AudioStreamTrack.cpp
index 307904e..1d036d0 100644
--- a/media/libaaudio/src/legacy/AudioStreamTrack.cpp
+++ b/media/libaaudio/src/legacy/AudioStreamTrack.cpp
@@ -178,7 +178,7 @@
     // Did we get a valid track?
     status_t status = mAudioTrack->initCheck();
     if (status != NO_ERROR) {
-        releaseCloseFinal();
+        safeReleaseClose();
         ALOGE("open(), initCheck() returned %d", status);
         return AAudioConvert_androidToAAudioResult(status);
     }
@@ -293,7 +293,7 @@
     return;
 }
 
-aaudio_result_t AudioStreamTrack::requestStart() {
+aaudio_result_t AudioStreamTrack::requestStart_l() {
     if (mAudioTrack.get() == nullptr) {
         ALOGE("requestStart() no AudioTrack");
         return AAUDIO_ERROR_INVALID_STATE;
@@ -320,7 +320,7 @@
     return AAUDIO_OK;
 }
 
-aaudio_result_t AudioStreamTrack::requestPause() {
+aaudio_result_t AudioStreamTrack::requestPause_l() {
     if (mAudioTrack.get() == nullptr) {
         ALOGE("%s() no AudioTrack", __func__);
         return AAUDIO_ERROR_INVALID_STATE;
@@ -336,7 +336,7 @@
     return checkForDisconnectRequest(false);
 }
 
-aaudio_result_t AudioStreamTrack::requestFlush() {
+aaudio_result_t AudioStreamTrack::requestFlush_l() {
     if (mAudioTrack.get() == nullptr) {
         ALOGE("%s() no AudioTrack", __func__);
         return AAUDIO_ERROR_INVALID_STATE;
@@ -350,7 +350,7 @@
     return AAUDIO_OK;
 }
 
-aaudio_result_t AudioStreamTrack::requestStop() {
+aaudio_result_t AudioStreamTrack::requestStop_l() {
     if (mAudioTrack.get() == nullptr) {
         ALOGE("%s() no AudioTrack", __func__);
         return AAUDIO_ERROR_INVALID_STATE;
diff --git a/media/libaaudio/src/legacy/AudioStreamTrack.h b/media/libaaudio/src/legacy/AudioStreamTrack.h
index 5a8fb39..f604871 100644
--- a/media/libaaudio/src/legacy/AudioStreamTrack.h
+++ b/media/libaaudio/src/legacy/AudioStreamTrack.h
@@ -44,11 +44,13 @@
     aaudio_result_t release_l() override;
     void close_l() override;
 
-    aaudio_result_t requestStart() override;
-    aaudio_result_t requestPause() override;
-    aaudio_result_t requestFlush() override;
-    aaudio_result_t requestStop() override;
+protected:
+    aaudio_result_t requestStart_l() REQUIRES(mStreamLock)  override;
+    aaudio_result_t requestPause_l() REQUIRES(mStreamLock) override;
+    aaudio_result_t requestFlush_l() REQUIRES(mStreamLock) override;
+    aaudio_result_t requestStop_l() REQUIRES(mStreamLock) override;
 
+public:
     bool isFlushSupported() const override {
         // Only implement FLUSH for OUTPUT streams.
         return true;
diff --git a/media/libaaudio/src/utility/AAudioUtilities.cpp b/media/libaaudio/src/utility/AAudioUtilities.cpp
index dbb3d2b..3dfb801 100644
--- a/media/libaaudio/src/utility/AAudioUtilities.cpp
+++ b/media/libaaudio/src/utility/AAudioUtilities.cpp
@@ -27,7 +27,7 @@
 #include "core/AudioGlobal.h"
 #include <aaudio/AAudioTesting.h>
 #include <math.h>
-#include <system/audio-base.h>
+#include <system/audio.h>
 #include <assert.h>
 
 #include "utility/AAudioUtilities.h"
diff --git a/media/libaaudio/tests/test_stop_hang.cpp b/media/libaaudio/tests/test_stop_hang.cpp
index 2397b6c..982ff4a 100644
--- a/media/libaaudio/tests/test_stop_hang.cpp
+++ b/media/libaaudio/tests/test_stop_hang.cpp
@@ -45,7 +45,7 @@
                 {
                     // Will block if the thread is running.
                     // This mutex is used to close() immediately after the callback returns
-                    // and before the requestStop() is called.
+                    // and before the requestStop_l() is called.
                     std::lock_guard<std::mutex> lock(doneLock);
                     if (done) break;
                 }
diff --git a/media/libaudioclient/AidlConversion.cpp b/media/libaudioclient/AidlConversion.cpp
new file mode 100644
index 0000000..95a6a4a
--- /dev/null
+++ b/media/libaudioclient/AidlConversion.cpp
@@ -0,0 +1,1223 @@
+/*
+ * 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.
+ */
+
+#include <limits>
+
+#define LOG_TAG "AidlConversion"
+//#define LOG_NDEBUG 0
+#include <system/audio.h>
+#include <utils/Log.h>
+
+#include "media/AidlConversion.h"
+
+#define VALUE_OR_RETURN(result)                          \
+    ({                                                   \
+        auto _tmp = (result);                            \
+        if (!_tmp.ok()) return unexpected(_tmp.error()); \
+        _tmp.value();                                    \
+    })
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Utilities
+
+namespace android {
+
+using base::unexpected;
+
+namespace {
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// The code below establishes:
+// IntegralTypeOf<T>, which works for either integral types (in which case it evaluates to T), or
+// enum types (in which case it evaluates to std::underlying_type_T<T>).
+
+template<typename T, typename = std::enable_if_t<std::is_integral_v<T> || std::is_enum_v<T>>>
+struct IntegralTypeOfStruct {
+    using Type = T;
+};
+
+template<typename T>
+struct IntegralTypeOfStruct<T, std::enable_if_t<std::is_enum_v<T>>> {
+    using Type = std::underlying_type_t<T>;
+};
+
+template<typename T>
+using IntegralTypeOf = typename IntegralTypeOfStruct<T>::Type;
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Utilities for handling bitmasks.
+
+template<typename Enum>
+Enum index2enum_index(int index) {
+    static_assert(std::is_enum_v<Enum> || std::is_integral_v<Enum>);
+    return static_cast<Enum>(index);
+}
+
+template<typename Enum>
+Enum index2enum_bitmask(int index) {
+    static_assert(std::is_enum_v<Enum> || std::is_integral_v<Enum>);
+    return static_cast<Enum>(1 << index);
+}
+
+template<typename Mask, typename Enum>
+Mask enumToMask_bitmask(Enum e) {
+    static_assert(std::is_enum_v<Enum> || std::is_integral_v<Enum>);
+    static_assert(std::is_enum_v<Mask> || std::is_integral_v<Mask>);
+    return static_cast<Mask>(e);
+}
+
+template<typename Mask, typename Enum>
+Mask enumToMask_index(Enum e) {
+    static_assert(std::is_enum_v<Enum> || std::is_integral_v<Enum>);
+    static_assert(std::is_enum_v<Mask> || std::is_integral_v<Mask>);
+    return static_cast<Mask>(static_cast<std::make_unsigned_t<IntegralTypeOf<Mask>>>(1)
+            << static_cast<int>(e));
+}
+
+template<typename DestMask, typename SrcMask, typename DestEnum, typename SrcEnum>
+ConversionResult<DestMask> convertBitmask(
+        SrcMask src, const std::function<ConversionResult<DestEnum>(SrcEnum)>& enumConversion,
+        const std::function<SrcEnum(int)>& srcIndexToEnum,
+        const std::function<DestMask(DestEnum)>& destEnumToMask) {
+    using UnsignedDestMask = std::make_unsigned_t<IntegralTypeOf<DestMask>>;
+    using UnsignedSrcMask = std::make_unsigned_t<IntegralTypeOf<SrcMask>>;
+
+    UnsignedDestMask dest = static_cast<UnsignedDestMask>(0);
+    UnsignedSrcMask usrc = static_cast<UnsignedSrcMask>(src);
+
+    int srcBitIndex = 0;
+    while (usrc != 0) {
+        if (usrc & 1) {
+            SrcEnum srcEnum = srcIndexToEnum(srcBitIndex);
+            DestEnum destEnum = VALUE_OR_RETURN(enumConversion(srcEnum));
+            DestMask destMask = destEnumToMask(destEnum);
+            dest |= destMask;
+        }
+        ++srcBitIndex;
+        usrc >>= 1;
+    }
+    return static_cast<DestMask>(dest);
+}
+
+template<typename Mask, typename Enum>
+bool bitmaskIsSet(Mask mask, Enum index) {
+    return (mask & enumToMask_index<Mask, Enum>(index)) != 0;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+template<typename To, typename From>
+ConversionResult<To> convertIntegral(From from) {
+    // Special handling is required for signed / vs. unsigned comparisons, since otherwise we may
+    // have the signed converted to unsigned and produce wrong results.
+    if (std::is_signed_v<From> && !std::is_signed_v<To>) {
+        if (from < 0 || from > std::numeric_limits<To>::max()) {
+            return unexpected(BAD_VALUE);
+        }
+    } else if (std::is_signed_v<To> && !std::is_signed_v<From>) {
+        if (from > std::numeric_limits<To>::max()) {
+            return unexpected(BAD_VALUE);
+        }
+    } else {
+        if (from < std::numeric_limits<To>::min() || from > std::numeric_limits<To>::max()) {
+            return unexpected(BAD_VALUE);
+        }
+    }
+    return static_cast<To>(from);
+}
+
+template<typename To, typename From>
+ConversionResult<To> convertReinterpret(From from) {
+    static_assert(sizeof(From) == sizeof(To));
+    return static_cast<To>(from);
+}
+
+enum class Direction {
+    INPUT, OUTPUT
+};
+
+ConversionResult<Direction> direction(media::AudioPortRole role, media::AudioPortType type) {
+    switch (type) {
+        case media::AudioPortType::DEVICE:
+            switch (role) {
+                case media::AudioPortRole::SOURCE:
+                    return Direction::INPUT;
+                case media::AudioPortRole::SINK:
+                    return Direction::OUTPUT;
+                default:
+                    break;
+            }
+            break;
+        case media::AudioPortType::MIX:
+            switch (role) {
+                case media::AudioPortRole::SOURCE:
+                    return Direction::OUTPUT;
+                case media::AudioPortRole::SINK:
+                    return Direction::INPUT;
+                default:
+                    break;
+            }
+            break;
+        default:
+            break;
+    }
+    return unexpected(BAD_VALUE);
+}
+
+ConversionResult<Direction> direction(audio_port_role_t role, audio_port_type_t type) {
+    switch (type) {
+        case AUDIO_PORT_TYPE_DEVICE:
+            switch (role) {
+                case AUDIO_PORT_ROLE_SOURCE:
+                    return Direction::INPUT;
+                case AUDIO_PORT_ROLE_SINK:
+                    return Direction::OUTPUT;
+                default:
+                    break;
+            }
+            break;
+        case AUDIO_PORT_TYPE_MIX:
+            switch (role) {
+                case AUDIO_PORT_ROLE_SOURCE:
+                    return Direction::OUTPUT;
+                case AUDIO_PORT_ROLE_SINK:
+                    return Direction::INPUT;
+                default:
+                    break;
+            }
+            break;
+        default:
+            break;
+    }
+    return unexpected(BAD_VALUE);
+}
+
+}  // namespace
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Converters
+
+// The legacy enum is unnamed. Thus, we use int.
+ConversionResult<int> aidl2legacy_AudioPortConfigType(media::AudioPortConfigType aidl) {
+    switch (aidl) {
+        case media::AudioPortConfigType::SAMPLE_RATE:
+            return AUDIO_PORT_CONFIG_SAMPLE_RATE;
+        case media::AudioPortConfigType::CHANNEL_MASK:
+            return AUDIO_PORT_CONFIG_CHANNEL_MASK;
+        case media::AudioPortConfigType::FORMAT:
+            return AUDIO_PORT_CONFIG_FORMAT;
+        case media::AudioPortConfigType::FLAGS:
+            return AUDIO_PORT_CONFIG_FLAGS;
+        default:
+            return unexpected(BAD_VALUE);
+    }
+}
+
+// The legacy enum is unnamed. Thus, we use int.
+ConversionResult<media::AudioPortConfigType> legacy2aidl_AudioPortConfigType(int legacy) {
+    switch (legacy) {
+        case AUDIO_PORT_CONFIG_SAMPLE_RATE:
+            return media::AudioPortConfigType::SAMPLE_RATE;
+        case AUDIO_PORT_CONFIG_CHANNEL_MASK:
+            return media::AudioPortConfigType::CHANNEL_MASK;
+        case AUDIO_PORT_CONFIG_FORMAT:
+            return media::AudioPortConfigType::FORMAT;
+        case AUDIO_PORT_CONFIG_FLAGS:
+            return media::AudioPortConfigType::FLAGS;
+        default:
+            return unexpected(BAD_VALUE);
+    }
+}
+
+ConversionResult<unsigned int> aidl2legacy_int32_t_config_mask(int32_t aidl) {
+    return convertBitmask<unsigned int, int32_t, int, media::AudioPortConfigType>(
+            aidl, aidl2legacy_AudioPortConfigType,
+            // AudioPortConfigType enum is index-based.
+            index2enum_index<media::AudioPortConfigType>,
+            // AUDIO_PORT_CONFIG_* flags are mask-based.
+            enumToMask_bitmask<unsigned int, int>);
+}
+
+ConversionResult<int32_t> legacy2aidl_config_mask_int32_t(unsigned int legacy) {
+    return convertBitmask<int32_t, unsigned int, media::AudioPortConfigType, int>(
+            legacy, legacy2aidl_AudioPortConfigType,
+            // AUDIO_PORT_CONFIG_* flags are mask-based.
+            index2enum_bitmask<unsigned>,
+            // AudioPortConfigType enum is index-based.
+            enumToMask_index<int32_t, media::AudioPortConfigType>);
+}
+
+ConversionResult<audio_channel_mask_t> aidl2legacy_int32_t_audio_channel_mask_t(int32_t aidl) {
+    // TODO(ytai): should we convert bit-by-bit?
+    // One problem here is that the representation is both opaque and is different based on the
+    // context (input vs. output). Can determine based on type and role, as per useInChannelMask().
+    return convertReinterpret<audio_channel_mask_t>(aidl);
+}
+
+ConversionResult<int32_t> legacy2aidl_audio_channel_mask_t_int32_t(audio_channel_mask_t legacy) {
+    // TODO(ytai): should we convert bit-by-bit?
+    // One problem here is that the representation is both opaque and is different based on the
+    // context (input vs. output). Can determine based on type and role, as per useInChannelMask().
+    return convertReinterpret<int32_t>(legacy);
+}
+
+ConversionResult<audio_io_config_event> aidl2legacy_AudioIoConfigEvent_audio_io_config_event(
+        media::AudioIoConfigEvent aidl) {
+    switch (aidl) {
+        case media::AudioIoConfigEvent::OUTPUT_REGISTERED:
+            return AUDIO_OUTPUT_REGISTERED;
+        case media::AudioIoConfigEvent::OUTPUT_OPENED:
+            return AUDIO_OUTPUT_OPENED;
+        case media::AudioIoConfigEvent::OUTPUT_CLOSED:
+            return AUDIO_OUTPUT_CLOSED;
+        case media::AudioIoConfigEvent::OUTPUT_CONFIG_CHANGED:
+            return AUDIO_OUTPUT_CONFIG_CHANGED;
+        case media::AudioIoConfigEvent::INPUT_REGISTERED:
+            return AUDIO_INPUT_REGISTERED;
+        case media::AudioIoConfigEvent::INPUT_OPENED:
+            return AUDIO_INPUT_OPENED;
+        case media::AudioIoConfigEvent::INPUT_CLOSED:
+            return AUDIO_INPUT_CLOSED;
+        case media::AudioIoConfigEvent::INPUT_CONFIG_CHANGED:
+            return AUDIO_INPUT_CONFIG_CHANGED;
+        case media::AudioIoConfigEvent::CLIENT_STARTED:
+            return AUDIO_CLIENT_STARTED;
+        default:
+            return unexpected(BAD_VALUE);
+    }
+}
+
+ConversionResult<media::AudioIoConfigEvent> legacy2aidl_audio_io_config_event_AudioIoConfigEvent(
+        audio_io_config_event legacy) {
+    switch (legacy) {
+        case AUDIO_OUTPUT_REGISTERED:
+            return media::AudioIoConfigEvent::OUTPUT_REGISTERED;
+        case AUDIO_OUTPUT_OPENED:
+            return media::AudioIoConfigEvent::OUTPUT_OPENED;
+        case AUDIO_OUTPUT_CLOSED:
+            return media::AudioIoConfigEvent::OUTPUT_CLOSED;
+        case AUDIO_OUTPUT_CONFIG_CHANGED:
+            return media::AudioIoConfigEvent::OUTPUT_CONFIG_CHANGED;
+        case AUDIO_INPUT_REGISTERED:
+            return media::AudioIoConfigEvent::INPUT_REGISTERED;
+        case AUDIO_INPUT_OPENED:
+            return media::AudioIoConfigEvent::INPUT_OPENED;
+        case AUDIO_INPUT_CLOSED:
+            return media::AudioIoConfigEvent::INPUT_CLOSED;
+        case AUDIO_INPUT_CONFIG_CHANGED:
+            return media::AudioIoConfigEvent::INPUT_CONFIG_CHANGED;
+        case AUDIO_CLIENT_STARTED:
+            return media::AudioIoConfigEvent::CLIENT_STARTED;
+        default:
+            return unexpected(BAD_VALUE);
+    }
+}
+
+ConversionResult<audio_port_role_t> aidl2legacy_AudioPortRole_audio_port_role_t(
+        media::AudioPortRole aidl) {
+    switch (aidl) {
+        case media::AudioPortRole::NONE:
+            return AUDIO_PORT_ROLE_NONE;
+        case media::AudioPortRole::SOURCE:
+            return AUDIO_PORT_ROLE_SOURCE;
+        case media::AudioPortRole::SINK:
+            return AUDIO_PORT_ROLE_SINK;
+        default:
+            return unexpected(BAD_VALUE);
+    }
+}
+
+ConversionResult<media::AudioPortRole> legacy2aidl_audio_port_role_t_AudioPortRole(
+        audio_port_role_t legacy) {
+    switch (legacy) {
+        case AUDIO_PORT_ROLE_NONE:
+            return media::AudioPortRole::NONE;
+        case AUDIO_PORT_ROLE_SOURCE:
+            return media::AudioPortRole::SOURCE;
+        case AUDIO_PORT_ROLE_SINK:
+            return media::AudioPortRole::SINK;
+        default:
+            return unexpected(BAD_VALUE);
+    }
+}
+
+ConversionResult<audio_port_type_t> aidl2legacy_AudioPortType_audio_port_type_t(
+        media::AudioPortType aidl) {
+    switch (aidl) {
+        case media::AudioPortType::NONE:
+            return AUDIO_PORT_TYPE_NONE;
+        case media::AudioPortType::DEVICE:
+            return AUDIO_PORT_TYPE_DEVICE;
+        case media::AudioPortType::MIX:
+            return AUDIO_PORT_TYPE_MIX;
+        case media::AudioPortType::SESSION:
+            return AUDIO_PORT_TYPE_SESSION;
+        default:
+            return unexpected(BAD_VALUE);
+    }
+}
+
+ConversionResult<media::AudioPortType> legacy2aidl_audio_port_type_t_AudioPortType(
+        audio_port_type_t legacy) {
+    switch (legacy) {
+        case AUDIO_PORT_TYPE_NONE:
+            return media::AudioPortType::NONE;
+        case AUDIO_PORT_TYPE_DEVICE:
+            return media::AudioPortType::DEVICE;
+        case AUDIO_PORT_TYPE_MIX:
+            return media::AudioPortType::MIX;
+        case AUDIO_PORT_TYPE_SESSION:
+            return media::AudioPortType::SESSION;
+        default:
+            return unexpected(BAD_VALUE);
+    }
+}
+
+ConversionResult<audio_format_t> aidl2legacy_AudioFormat_audio_format_t(
+        media::audio::common::AudioFormat aidl) {
+    // This relies on AudioFormat being kept in sync with audio_format_t.
+    static_assert(sizeof(media::audio::common::AudioFormat) == sizeof(audio_format_t));
+    return static_cast<audio_format_t>(aidl);
+}
+
+ConversionResult<media::audio::common::AudioFormat> legacy2aidl_audio_format_t_AudioFormat(
+        audio_format_t legacy) {
+    // This relies on AudioFormat being kept in sync with audio_format_t.
+    static_assert(sizeof(media::audio::common::AudioFormat) == sizeof(audio_format_t));
+    return static_cast<media::audio::common::AudioFormat>(legacy);
+}
+
+ConversionResult<int> aidl2legacy_AudioGainMode_int(media::AudioGainMode aidl) {
+    switch (aidl) {
+        case media::AudioGainMode::JOINT:
+            return AUDIO_GAIN_MODE_JOINT;
+        case media::AudioGainMode::CHANNELS:
+            return AUDIO_GAIN_MODE_CHANNELS;
+        case media::AudioGainMode::RAMP:
+            return AUDIO_GAIN_MODE_RAMP;
+        default:
+            return unexpected(BAD_VALUE);
+    }
+}
+
+ConversionResult<media::AudioGainMode> legacy2aidl_int_AudioGainMode(int legacy) {
+    switch (legacy) {
+        case AUDIO_GAIN_MODE_JOINT:
+            return media::AudioGainMode::JOINT;
+        case AUDIO_GAIN_MODE_CHANNELS:
+            return media::AudioGainMode::CHANNELS;
+        case AUDIO_GAIN_MODE_RAMP:
+            return media::AudioGainMode::RAMP;
+        default:
+            return unexpected(BAD_VALUE);
+    }
+}
+
+ConversionResult<audio_gain_mode_t> aidl2legacy_int32_t_audio_gain_mode_t(int32_t aidl) {
+    return convertBitmask<audio_gain_mode_t, int32_t, int, media::AudioGainMode>(
+            aidl, aidl2legacy_AudioGainMode_int,
+            // AudioGainMode is index-based.
+            index2enum_index<media::AudioGainMode>,
+            // AUDIO_GAIN_MODE_* constants are mask-based.
+            enumToMask_bitmask<audio_gain_mode_t, int>);
+}
+
+ConversionResult<int32_t> legacy2aidl_audio_gain_mode_t_int32_t(audio_gain_mode_t legacy) {
+    return convertBitmask<int32_t, audio_gain_mode_t, media::AudioGainMode, int>(
+            legacy, legacy2aidl_int_AudioGainMode,
+            // AUDIO_GAIN_MODE_* constants are mask-based.
+            index2enum_bitmask<int>,
+            // AudioGainMode is index-based.
+            enumToMask_index<int32_t, media::AudioGainMode>);
+}
+
+ConversionResult<audio_devices_t> aidl2legacy_int32_t_audio_devices_t(int32_t aidl) {
+    // TODO(ytai): bitfield?
+    return convertReinterpret<audio_devices_t>(aidl);
+}
+
+ConversionResult<int32_t> legacy2aidl_audio_devices_t_int32_t(audio_devices_t legacy) {
+    // TODO(ytai): bitfield?
+    return convertReinterpret<int32_t>(legacy);
+}
+
+ConversionResult<audio_gain_config> aidl2legacy_AudioGainConfig_audio_gain_config(
+        const media::AudioGainConfig& aidl, media::AudioPortRole role, media::AudioPortType type) {
+    audio_gain_config legacy;
+    legacy.index = VALUE_OR_RETURN(convertIntegral<int>(aidl.index));
+    legacy.mode = VALUE_OR_RETURN(aidl2legacy_int32_t_audio_gain_mode_t(aidl.mode));
+    legacy.channel_mask =
+            VALUE_OR_RETURN(aidl2legacy_int32_t_audio_channel_mask_t(aidl.channelMask));
+    const bool isInput = VALUE_OR_RETURN(direction(role, type)) == Direction::INPUT;
+    const bool isJoint = bitmaskIsSet(aidl.mode, media::AudioGainMode::JOINT);
+    size_t numValues = isJoint ? 1
+                               : isInput ? audio_channel_count_from_in_mask(legacy.channel_mask)
+                                         : audio_channel_count_from_out_mask(legacy.channel_mask);
+    if (aidl.values.size() != numValues || aidl.values.size() > std::size(legacy.values)) {
+        return unexpected(BAD_VALUE);
+    }
+    for (size_t i = 0; i < numValues; ++i) {
+        legacy.values[i] = VALUE_OR_RETURN(convertIntegral<int>(aidl.values[i]));
+    }
+    legacy.ramp_duration_ms = VALUE_OR_RETURN(convertIntegral<unsigned int>(aidl.rampDurationMs));
+    return legacy;
+}
+
+ConversionResult<media::AudioGainConfig> legacy2aidl_audio_gain_config_AudioGainConfig(
+        const audio_gain_config& legacy, audio_port_role_t role, audio_port_type_t type) {
+    media::AudioGainConfig aidl;
+    aidl.index = VALUE_OR_RETURN(convertIntegral<int32_t>(legacy.index));
+    aidl.mode = VALUE_OR_RETURN(legacy2aidl_audio_gain_mode_t_int32_t(legacy.mode));
+    aidl.channelMask =
+            VALUE_OR_RETURN(legacy2aidl_audio_channel_mask_t_int32_t(legacy.channel_mask));
+    const bool isInput = VALUE_OR_RETURN(direction(role, type)) == Direction::INPUT;
+    const bool isJoint = (legacy.mode & AUDIO_GAIN_MODE_JOINT) != 0;
+    size_t numValues = isJoint ? 1
+                               : isInput ? audio_channel_count_from_in_mask(legacy.channel_mask)
+                                         : audio_channel_count_from_out_mask(legacy.channel_mask);
+    aidl.values.resize(numValues);
+    for (size_t i = 0; i < numValues; ++i) {
+        aidl.values[i] = VALUE_OR_RETURN(convertIntegral<int32_t>(legacy.values[i]));
+    }
+    aidl.rampDurationMs = VALUE_OR_RETURN(convertIntegral<int32_t>(legacy.ramp_duration_ms));
+    return aidl;
+}
+
+ConversionResult<audio_input_flags_t> aidl2legacy_AudioInputFlags_audio_input_flags_t(
+        media::AudioInputFlags aidl) {
+    switch (aidl) {
+        case media::AudioInputFlags::FAST:
+            return AUDIO_INPUT_FLAG_FAST;
+        case media::AudioInputFlags::HW_HOTWORD:
+            return AUDIO_INPUT_FLAG_HW_HOTWORD;
+        case media::AudioInputFlags::RAW:
+            return AUDIO_INPUT_FLAG_RAW;
+        case media::AudioInputFlags::SYNC:
+            return AUDIO_INPUT_FLAG_SYNC;
+        case media::AudioInputFlags::MMAP_NOIRQ:
+            return AUDIO_INPUT_FLAG_MMAP_NOIRQ;
+        case media::AudioInputFlags::VOIP_TX:
+            return AUDIO_INPUT_FLAG_VOIP_TX;
+        case media::AudioInputFlags::HW_AV_SYNC:
+            return AUDIO_INPUT_FLAG_HW_AV_SYNC;
+        case media::AudioInputFlags::DIRECT:
+            return AUDIO_INPUT_FLAG_DIRECT;
+        default:
+            return unexpected(BAD_VALUE);
+    }
+}
+
+ConversionResult<media::AudioInputFlags> legacy2aidl_audio_input_flags_t_AudioInputFlags(
+        audio_input_flags_t legacy) {
+    switch (legacy) {
+        case AUDIO_INPUT_FLAG_FAST:
+            return media::AudioInputFlags::FAST;
+        case AUDIO_INPUT_FLAG_HW_HOTWORD:
+            return media::AudioInputFlags::HW_HOTWORD;
+        case AUDIO_INPUT_FLAG_RAW:
+            return media::AudioInputFlags::RAW;
+        case AUDIO_INPUT_FLAG_SYNC:
+            return media::AudioInputFlags::SYNC;
+        case AUDIO_INPUT_FLAG_MMAP_NOIRQ:
+            return media::AudioInputFlags::MMAP_NOIRQ;
+        case AUDIO_INPUT_FLAG_VOIP_TX:
+            return media::AudioInputFlags::VOIP_TX;
+        case AUDIO_INPUT_FLAG_HW_AV_SYNC:
+            return media::AudioInputFlags::HW_AV_SYNC;
+        case AUDIO_INPUT_FLAG_DIRECT:
+            return media::AudioInputFlags::DIRECT;
+        default:
+            return unexpected(BAD_VALUE);
+    }
+}
+
+ConversionResult<audio_output_flags_t> aidl2legacy_AudioOutputFlags_audio_output_flags_t(
+        media::AudioOutputFlags aidl) {
+    switch (aidl) {
+        case media::AudioOutputFlags::DIRECT:
+            return AUDIO_OUTPUT_FLAG_DIRECT;
+        case media::AudioOutputFlags::PRIMARY:
+            return AUDIO_OUTPUT_FLAG_PRIMARY;
+        case media::AudioOutputFlags::FAST:
+            return AUDIO_OUTPUT_FLAG_FAST;
+        case media::AudioOutputFlags::DEEP_BUFFER:
+            return AUDIO_OUTPUT_FLAG_DEEP_BUFFER;
+        case media::AudioOutputFlags::COMPRESS_OFFLOAD:
+            return AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD;
+        case media::AudioOutputFlags::NON_BLOCKING:
+            return AUDIO_OUTPUT_FLAG_NON_BLOCKING;
+        case media::AudioOutputFlags::HW_AV_SYNC:
+            return AUDIO_OUTPUT_FLAG_HW_AV_SYNC;
+        case media::AudioOutputFlags::TTS:
+            return AUDIO_OUTPUT_FLAG_TTS;
+        case media::AudioOutputFlags::RAW:
+            return AUDIO_OUTPUT_FLAG_RAW;
+        case media::AudioOutputFlags::SYNC:
+            return AUDIO_OUTPUT_FLAG_SYNC;
+        case media::AudioOutputFlags::IEC958_NONAUDIO:
+            return AUDIO_OUTPUT_FLAG_IEC958_NONAUDIO;
+        case media::AudioOutputFlags::DIRECT_PCM:
+            return AUDIO_OUTPUT_FLAG_DIRECT_PCM;
+        case media::AudioOutputFlags::MMAP_NOIRQ:
+            return AUDIO_OUTPUT_FLAG_MMAP_NOIRQ;
+        case media::AudioOutputFlags::VOIP_RX:
+            return AUDIO_OUTPUT_FLAG_VOIP_RX;
+        case media::AudioOutputFlags::INCALL_MUSIC:
+            return AUDIO_OUTPUT_FLAG_INCALL_MUSIC;
+        default:
+            return unexpected(BAD_VALUE);
+    }
+}
+
+ConversionResult<media::AudioOutputFlags> legacy2aidl_audio_output_flags_t_AudioOutputFlags(
+        audio_output_flags_t legacy) {
+    switch (legacy) {
+        case AUDIO_OUTPUT_FLAG_DIRECT:
+            return media::AudioOutputFlags::DIRECT;
+        case AUDIO_OUTPUT_FLAG_PRIMARY:
+            return media::AudioOutputFlags::PRIMARY;
+        case AUDIO_OUTPUT_FLAG_FAST:
+            return media::AudioOutputFlags::FAST;
+        case AUDIO_OUTPUT_FLAG_DEEP_BUFFER:
+            return media::AudioOutputFlags::DEEP_BUFFER;
+        case AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD:
+            return media::AudioOutputFlags::COMPRESS_OFFLOAD;
+        case AUDIO_OUTPUT_FLAG_NON_BLOCKING:
+            return media::AudioOutputFlags::NON_BLOCKING;
+        case AUDIO_OUTPUT_FLAG_HW_AV_SYNC:
+            return media::AudioOutputFlags::HW_AV_SYNC;
+        case AUDIO_OUTPUT_FLAG_TTS:
+            return media::AudioOutputFlags::TTS;
+        case AUDIO_OUTPUT_FLAG_RAW:
+            return media::AudioOutputFlags::RAW;
+        case AUDIO_OUTPUT_FLAG_SYNC:
+            return media::AudioOutputFlags::SYNC;
+        case AUDIO_OUTPUT_FLAG_IEC958_NONAUDIO:
+            return media::AudioOutputFlags::IEC958_NONAUDIO;
+        case AUDIO_OUTPUT_FLAG_DIRECT_PCM:
+            return media::AudioOutputFlags::DIRECT_PCM;
+        case AUDIO_OUTPUT_FLAG_MMAP_NOIRQ:
+            return media::AudioOutputFlags::MMAP_NOIRQ;
+        case AUDIO_OUTPUT_FLAG_VOIP_RX:
+            return media::AudioOutputFlags::VOIP_RX;
+        case AUDIO_OUTPUT_FLAG_INCALL_MUSIC:
+            return media::AudioOutputFlags::INCALL_MUSIC;
+        default:
+            return unexpected(BAD_VALUE);
+    }
+}
+
+ConversionResult<audio_input_flags_t> aidl2legacy_audio_input_flags_mask(int32_t aidl) {
+    using LegacyMask = std::underlying_type_t<audio_input_flags_t>;
+
+    LegacyMask converted = VALUE_OR_RETURN(
+            (convertBitmask<LegacyMask, int32_t, audio_input_flags_t, media::AudioInputFlags>(
+                    aidl, aidl2legacy_AudioInputFlags_audio_input_flags_t,
+                    index2enum_index<media::AudioInputFlags>,
+                    enumToMask_bitmask<LegacyMask, audio_input_flags_t>)));
+    return static_cast<audio_input_flags_t>(converted);
+}
+
+ConversionResult<int32_t> legacy2aidl_audio_input_flags_mask(audio_input_flags_t legacy) {
+    using LegacyMask = std::underlying_type_t<audio_input_flags_t>;
+
+    LegacyMask legacyMask = static_cast<LegacyMask>(legacy);
+    return convertBitmask<int32_t, LegacyMask, media::AudioInputFlags, audio_input_flags_t>(
+            legacyMask, legacy2aidl_audio_input_flags_t_AudioInputFlags,
+            index2enum_bitmask<audio_input_flags_t>,
+            enumToMask_index<int32_t, media::AudioInputFlags>);
+}
+
+ConversionResult<audio_output_flags_t> aidl2legacy_audio_output_flags_mask(int32_t aidl) {
+    using LegacyMask = std::underlying_type_t<audio_output_flags_t>;
+
+    LegacyMask converted = VALUE_OR_RETURN(
+            (convertBitmask<LegacyMask, int32_t, audio_output_flags_t, media::AudioOutputFlags>(
+                    aidl, aidl2legacy_AudioOutputFlags_audio_output_flags_t,
+                    index2enum_index<media::AudioOutputFlags>,
+                    enumToMask_bitmask<LegacyMask, audio_output_flags_t>)));
+    return convertReinterpret<audio_output_flags_t>(converted);
+}
+
+ConversionResult<int32_t> legacy2aidl_audio_output_flags_mask(audio_output_flags_t legacy) {
+    using LegacyMask = std::underlying_type_t<audio_output_flags_t>;
+
+    LegacyMask legacyMask = static_cast<LegacyMask>(legacy);
+    return convertBitmask<int32_t, LegacyMask, media::AudioOutputFlags, audio_output_flags_t>(
+            legacyMask, legacy2aidl_audio_output_flags_t_AudioOutputFlags,
+            index2enum_bitmask<audio_output_flags_t>,
+            enumToMask_index<int32_t, media::AudioOutputFlags>);
+}
+
+ConversionResult<audio_io_flags> aidl2legacy_AudioIoFlags_audio_io_flags(
+        const media::AudioIoFlags& aidl, media::AudioPortRole role, media::AudioPortType type) {
+    audio_io_flags legacy;
+    // Our way of representing a union in AIDL is to have multiple vectors and require that at most
+    // one of the them has size 1 and the rest are empty.
+    size_t totalSize = aidl.input.size() + aidl.output.size();
+    if (totalSize > 1) {
+        return unexpected(BAD_VALUE);
+    }
+
+    Direction dir = VALUE_OR_RETURN(direction(role, type));
+    switch (dir) {
+        case Direction::INPUT:
+            if (aidl.input.empty()) {
+                return unexpected(BAD_VALUE);
+            }
+            legacy.input = VALUE_OR_RETURN(aidl2legacy_audio_input_flags_mask(aidl.input[0]));
+            break;
+
+        case Direction::OUTPUT:
+            if (aidl.output.empty()) {
+                return unexpected(BAD_VALUE);
+            }
+            legacy.output = VALUE_OR_RETURN(aidl2legacy_audio_output_flags_mask(aidl.output[0]));
+            break;
+    }
+
+    return legacy;
+}
+
+ConversionResult<media::AudioIoFlags> legacy2aidl_audio_io_flags_AudioIoFlags(
+        const audio_io_flags& legacy, audio_port_role_t role, audio_port_type_t type) {
+    media::AudioIoFlags aidl;
+
+    Direction dir = VALUE_OR_RETURN(direction(role, type));
+    switch (dir) {
+        case Direction::INPUT:
+            aidl.input.push_back(VALUE_OR_RETURN(legacy2aidl_audio_input_flags_mask(legacy.input)));
+            break;
+        case Direction::OUTPUT:
+            aidl.output.push_back(
+                    VALUE_OR_RETURN(legacy2aidl_audio_output_flags_mask(legacy.output)));
+            break;
+    }
+    return aidl;
+}
+
+ConversionResult<audio_port_config_device_ext> aidl2legacy_AudioPortConfigDeviceExt(
+        const media::AudioPortConfigDeviceExt& aidl) {
+    audio_port_config_device_ext legacy;
+    legacy.hw_module = VALUE_OR_RETURN(convertReinterpret<audio_module_handle_t>(aidl.hwModule));
+    legacy.type = VALUE_OR_RETURN(aidl2legacy_int32_t_audio_devices_t(aidl.type));
+    if (aidl.address.size() > AUDIO_DEVICE_MAX_ADDRESS_LEN - 1) {
+        return unexpected(BAD_VALUE);
+    }
+    std::strcpy(legacy.address, aidl.address.c_str());
+    return legacy;
+}
+
+ConversionResult<media::AudioPortConfigDeviceExt> legacy2aidl_AudioPortConfigDeviceExt(
+        const audio_port_config_device_ext& legacy) {
+    media::AudioPortConfigDeviceExt aidl;
+    aidl.hwModule = VALUE_OR_RETURN(convertReinterpret<int32_t>(legacy.hw_module));
+    aidl.type = VALUE_OR_RETURN(legacy2aidl_audio_devices_t_int32_t(legacy.type));
+
+    if (strnlen(legacy.address, AUDIO_DEVICE_MAX_ADDRESS_LEN) == AUDIO_DEVICE_MAX_ADDRESS_LEN) {
+        // No null-terminator.
+        return unexpected(BAD_VALUE);
+    }
+    aidl.address = legacy.address;
+    return aidl;
+}
+
+ConversionResult<audio_stream_type_t> aidl2legacy_AudioStreamType_audio_stream_type_t(
+        media::AudioStreamType aidl) {
+    switch (aidl) {
+        case media::AudioStreamType::DEFAULT:
+            return AUDIO_STREAM_DEFAULT;
+        case media::AudioStreamType::VOICE_CALL:
+            return AUDIO_STREAM_VOICE_CALL;
+        case media::AudioStreamType::SYSTEM:
+            return AUDIO_STREAM_SYSTEM;
+        case media::AudioStreamType::RING:
+            return AUDIO_STREAM_RING;
+        case media::AudioStreamType::MUSIC:
+            return AUDIO_STREAM_MUSIC;
+        case media::AudioStreamType::ALARM:
+            return AUDIO_STREAM_ALARM;
+        case media::AudioStreamType::NOTIFICATION:
+            return AUDIO_STREAM_NOTIFICATION;
+        case media::AudioStreamType::BLUETOOTH_SCO:
+            return AUDIO_STREAM_BLUETOOTH_SCO;
+        case media::AudioStreamType::ENFORCED_AUDIBLE:
+            return AUDIO_STREAM_ENFORCED_AUDIBLE;
+        case media::AudioStreamType::DTMF:
+            return AUDIO_STREAM_DTMF;
+        case media::AudioStreamType::TTS:
+            return AUDIO_STREAM_TTS;
+        case media::AudioStreamType::ACCESSIBILITY:
+            return AUDIO_STREAM_ACCESSIBILITY;
+        case media::AudioStreamType::ASSISTANT:
+            return AUDIO_STREAM_ASSISTANT;
+        case media::AudioStreamType::REROUTING:
+            return AUDIO_STREAM_REROUTING;
+        case media::AudioStreamType::PATCH:
+            return AUDIO_STREAM_PATCH;
+        case media::AudioStreamType::CALL_ASSISTANT:
+            return AUDIO_STREAM_CALL_ASSISTANT;
+        default:
+            return unexpected(BAD_VALUE);
+    }
+}
+
+ConversionResult<media::AudioStreamType> legacy2aidl_audio_stream_type_t_AudioStreamType(
+        audio_stream_type_t legacy) {
+    switch (legacy) {
+        case AUDIO_STREAM_DEFAULT:
+            return media::AudioStreamType::DEFAULT;
+        case AUDIO_STREAM_VOICE_CALL:
+            return media::AudioStreamType::VOICE_CALL;
+        case AUDIO_STREAM_SYSTEM:
+            return media::AudioStreamType::SYSTEM;
+        case AUDIO_STREAM_RING:
+            return media::AudioStreamType::RING;
+        case AUDIO_STREAM_MUSIC:
+            return media::AudioStreamType::MUSIC;
+        case AUDIO_STREAM_ALARM:
+            return media::AudioStreamType::ALARM;
+        case AUDIO_STREAM_NOTIFICATION:
+            return media::AudioStreamType::NOTIFICATION;
+        case AUDIO_STREAM_BLUETOOTH_SCO:
+            return media::AudioStreamType::BLUETOOTH_SCO;
+        case AUDIO_STREAM_ENFORCED_AUDIBLE:
+            return media::AudioStreamType::ENFORCED_AUDIBLE;
+        case AUDIO_STREAM_DTMF:
+            return media::AudioStreamType::DTMF;
+        case AUDIO_STREAM_TTS:
+            return media::AudioStreamType::TTS;
+        case AUDIO_STREAM_ACCESSIBILITY:
+            return media::AudioStreamType::ACCESSIBILITY;
+        case AUDIO_STREAM_ASSISTANT:
+            return media::AudioStreamType::ASSISTANT;
+        case AUDIO_STREAM_REROUTING:
+            return media::AudioStreamType::REROUTING;
+        case AUDIO_STREAM_PATCH:
+            return media::AudioStreamType::PATCH;
+        case AUDIO_STREAM_CALL_ASSISTANT:
+            return media::AudioStreamType::CALL_ASSISTANT;
+        default:
+            return unexpected(BAD_VALUE);
+    }
+}
+
+ConversionResult<audio_source_t> aidl2legacy_AudioSourceType_audio_source_t(
+        media::AudioSourceType aidl) {
+    switch (aidl) {
+        case media::AudioSourceType::DEFAULT:
+            return AUDIO_SOURCE_DEFAULT;
+        case media::AudioSourceType::MIC:
+            return AUDIO_SOURCE_MIC;
+        case media::AudioSourceType::VOICE_UPLINK:
+            return AUDIO_SOURCE_VOICE_UPLINK;
+        case media::AudioSourceType::VOICE_DOWNLINK:
+            return AUDIO_SOURCE_VOICE_DOWNLINK;
+        case media::AudioSourceType::VOICE_CALL:
+            return AUDIO_SOURCE_VOICE_CALL;
+        case media::AudioSourceType::CAMCORDER:
+            return AUDIO_SOURCE_CAMCORDER;
+        case media::AudioSourceType::VOICE_RECOGNITION:
+            return AUDIO_SOURCE_VOICE_RECOGNITION;
+        case media::AudioSourceType::VOICE_COMMUNICATION:
+            return AUDIO_SOURCE_VOICE_COMMUNICATION;
+        case media::AudioSourceType::REMOTE_SUBMIX:
+            return AUDIO_SOURCE_REMOTE_SUBMIX;
+        case media::AudioSourceType::UNPROCESSED:
+            return AUDIO_SOURCE_UNPROCESSED;
+        case media::AudioSourceType::VOICE_PERFORMANCE:
+            return AUDIO_SOURCE_VOICE_PERFORMANCE;
+        case media::AudioSourceType::ECHO_REFERENCE:
+            return AUDIO_SOURCE_ECHO_REFERENCE;
+        case media::AudioSourceType::FM_TUNER:
+            return AUDIO_SOURCE_FM_TUNER;
+        case media::AudioSourceType::HOTWORD:
+            return AUDIO_SOURCE_HOTWORD;
+        default:
+            return unexpected(BAD_VALUE);
+    }
+}
+
+ConversionResult<media::AudioSourceType> legacy2aidl_audio_source_t_AudioSourceType(
+        audio_source_t legacy) {
+    switch (legacy) {
+        case AUDIO_SOURCE_DEFAULT:
+            return media::AudioSourceType::DEFAULT;
+        case AUDIO_SOURCE_MIC:
+            return media::AudioSourceType::MIC;
+        case AUDIO_SOURCE_VOICE_UPLINK:
+            return media::AudioSourceType::VOICE_UPLINK;
+        case AUDIO_SOURCE_VOICE_DOWNLINK:
+            return media::AudioSourceType::VOICE_DOWNLINK;
+        case AUDIO_SOURCE_VOICE_CALL:
+            return media::AudioSourceType::VOICE_CALL;
+        case AUDIO_SOURCE_CAMCORDER:
+            return media::AudioSourceType::CAMCORDER;
+        case AUDIO_SOURCE_VOICE_RECOGNITION:
+            return media::AudioSourceType::VOICE_RECOGNITION;
+        case AUDIO_SOURCE_VOICE_COMMUNICATION:
+            return media::AudioSourceType::VOICE_COMMUNICATION;
+        case AUDIO_SOURCE_REMOTE_SUBMIX:
+            return media::AudioSourceType::REMOTE_SUBMIX;
+        case AUDIO_SOURCE_UNPROCESSED:
+            return media::AudioSourceType::UNPROCESSED;
+        case AUDIO_SOURCE_VOICE_PERFORMANCE:
+            return media::AudioSourceType::VOICE_PERFORMANCE;
+        case AUDIO_SOURCE_ECHO_REFERENCE:
+            return media::AudioSourceType::ECHO_REFERENCE;
+        case AUDIO_SOURCE_FM_TUNER:
+            return media::AudioSourceType::FM_TUNER;
+        case AUDIO_SOURCE_HOTWORD:
+            return media::AudioSourceType::HOTWORD;
+        default:
+            return unexpected(BAD_VALUE);
+    }
+}
+
+ConversionResult<audio_session_t> aidl2legacy_AudioSessionType_audio_session_t(
+        media::AudioSessionType aidl) {
+    switch (aidl) {
+        case media::AudioSessionType::DEVICE:
+            return AUDIO_SESSION_DEVICE;
+        case media::AudioSessionType::OUTPUT_STAGE:
+            return AUDIO_SESSION_OUTPUT_STAGE;
+        case media::AudioSessionType::OUTPUT_MIX:
+            return AUDIO_SESSION_OUTPUT_MIX;
+        default:
+            return unexpected(BAD_VALUE);
+    }
+}
+
+ConversionResult<media::AudioSessionType> legacy2aidl_audio_session_t_AudioSessionType(
+        audio_session_t legacy) {
+    switch (legacy) {
+        case AUDIO_SESSION_DEVICE:
+            return media::AudioSessionType::DEVICE;
+        case AUDIO_SESSION_OUTPUT_STAGE:
+            return media::AudioSessionType::OUTPUT_STAGE;
+        case AUDIO_SESSION_OUTPUT_MIX:
+            return media::AudioSessionType::OUTPUT_MIX;
+        default:
+            return unexpected(BAD_VALUE);
+    }
+}
+
+// This type is unnamed in the original definition, thus we name it here.
+using audio_port_config_mix_ext_usecase = decltype(audio_port_config_mix_ext::usecase);
+
+ConversionResult<audio_port_config_mix_ext_usecase> aidl2legacy_AudioPortConfigMixExtUseCase(
+        const media::AudioPortConfigMixExtUseCase& aidl, media::AudioPortRole role) {
+    audio_port_config_mix_ext_usecase legacy;
+
+    // Our way of representing a union in AIDL is to have multiple vectors and require that exactly
+    // one of the them has size 1 and the rest are empty.
+    size_t totalSize = aidl.stream.size() + aidl.source.size();
+    if (totalSize > 1) {
+        return unexpected(BAD_VALUE);
+    }
+
+    switch (role) {
+        case media::AudioPortRole::NONE:
+            if (totalSize != 0) {
+                return unexpected(BAD_VALUE);
+            }
+            break;
+
+        case media::AudioPortRole::SOURCE:
+            // This is not a bug. A SOURCE role corresponds to the stream field.
+            if (aidl.stream.empty()) {
+                return unexpected(BAD_VALUE);
+            }
+            legacy.stream = VALUE_OR_RETURN(
+                    aidl2legacy_AudioStreamType_audio_stream_type_t(aidl.stream[0]));
+            break;
+
+        case media::AudioPortRole::SINK:
+            // This is not a bug. A SINK role corresponds to the source field.
+            if (aidl.source.empty()) {
+                return unexpected(BAD_VALUE);
+            }
+            legacy.source =
+                    VALUE_OR_RETURN(aidl2legacy_AudioSourceType_audio_source_t(aidl.source[0]));
+            break;
+
+        default:
+            LOG_ALWAYS_FATAL("Shouldn't get here");
+    }
+    return legacy;
+}
+
+ConversionResult<media::AudioPortConfigMixExtUseCase> legacy2aidl_AudioPortConfigMixExtUseCase(
+        const audio_port_config_mix_ext_usecase& legacy, audio_port_role_t role) {
+    media::AudioPortConfigMixExtUseCase aidl;
+
+    switch (role) {
+        case AUDIO_PORT_ROLE_NONE:
+            break;
+        case AUDIO_PORT_ROLE_SOURCE:
+            // This is not a bug. A SOURCE role corresponds to the stream field.
+            aidl.stream.push_back(VALUE_OR_RETURN(
+                                          legacy2aidl_audio_stream_type_t_AudioStreamType(
+                                                  legacy.stream)));
+            break;
+        case AUDIO_PORT_ROLE_SINK:
+            // This is not a bug. A SINK role corresponds to the source field.
+            aidl.source.push_back(
+                    VALUE_OR_RETURN(legacy2aidl_audio_source_t_AudioSourceType(legacy.source)));
+            break;
+        default:
+            LOG_ALWAYS_FATAL("Shouldn't get here");
+    }
+    return aidl;
+}
+
+ConversionResult<audio_port_config_mix_ext> aidl2legacy_AudioPortConfigMixExt(
+        const media::AudioPortConfigMixExt& aidl, media::AudioPortRole role) {
+    audio_port_config_mix_ext legacy;
+    legacy.hw_module = VALUE_OR_RETURN(convertReinterpret<audio_module_handle_t>(aidl.hwModule));
+    legacy.handle = VALUE_OR_RETURN(convertReinterpret<audio_io_handle_t>(aidl.handle));
+    legacy.usecase = VALUE_OR_RETURN(aidl2legacy_AudioPortConfigMixExtUseCase(aidl.usecase, role));
+    return legacy;
+}
+
+ConversionResult<media::AudioPortConfigMixExt> legacy2aidl_AudioPortConfigMixExt(
+        const audio_port_config_mix_ext& legacy, audio_port_role_t role) {
+    media::AudioPortConfigMixExt aidl;
+    aidl.hwModule = VALUE_OR_RETURN(convertReinterpret<int32_t>(legacy.hw_module));
+    aidl.handle = VALUE_OR_RETURN(convertReinterpret<int32_t>(legacy.handle));
+    aidl.usecase = VALUE_OR_RETURN(legacy2aidl_AudioPortConfigMixExtUseCase(legacy.usecase, role));
+    return aidl;
+}
+
+ConversionResult<audio_port_config_session_ext> aidl2legacy_AudioPortConfigSessionExt(
+        const media::AudioPortConfigSessionExt& aidl) {
+    audio_port_config_session_ext legacy;
+    legacy.session = VALUE_OR_RETURN(aidl2legacy_AudioSessionType_audio_session_t(aidl.session));
+    return legacy;
+}
+
+ConversionResult<media::AudioPortConfigSessionExt> legacy2aidl_AudioPortConfigSessionExt(
+        const audio_port_config_session_ext& legacy) {
+    media::AudioPortConfigSessionExt aidl;
+    aidl.session = VALUE_OR_RETURN(legacy2aidl_audio_session_t_AudioSessionType(legacy.session));
+    return aidl;
+}
+
+// This type is unnamed in the original definition, thus we name it here.
+using audio_port_config_ext = decltype(audio_port_config::ext);
+
+ConversionResult<audio_port_config_ext> aidl2legacy_AudioPortConfigExt(
+        const media::AudioPortConfigExt& aidl, media::AudioPortType type,
+        media::AudioPortRole role) {
+    audio_port_config_ext legacy;
+    // Our way of representing a union in AIDL is to have multiple vectors and require that at most
+    // one of the them has size 1 and the rest are empty.
+    size_t totalSize = aidl.device.size() + aidl.mix.size() + aidl.session.size();
+    if (totalSize > 1) {
+        return unexpected(BAD_VALUE);
+    }
+    switch (type) {
+        case media::AudioPortType::NONE:
+            if (totalSize != 0) {
+                return unexpected(BAD_VALUE);
+            }
+            break;
+        case media::AudioPortType::DEVICE:
+            if (aidl.device.empty()) {
+                return unexpected(BAD_VALUE);
+            }
+            legacy.device = VALUE_OR_RETURN(aidl2legacy_AudioPortConfigDeviceExt(aidl.device[0]));
+            break;
+        case media::AudioPortType::MIX:
+            if (aidl.mix.empty()) {
+                return unexpected(BAD_VALUE);
+            }
+            legacy.mix = VALUE_OR_RETURN(aidl2legacy_AudioPortConfigMixExt(aidl.mix[0], role));
+            break;
+        case media::AudioPortType::SESSION:
+            if (aidl.session.empty()) {
+                return unexpected(BAD_VALUE);
+            }
+            legacy.session =
+                    VALUE_OR_RETURN(aidl2legacy_AudioPortConfigSessionExt(aidl.session[0]));
+            break;
+        default:
+            LOG_ALWAYS_FATAL("Shouldn't get here");
+    }
+    return legacy;
+}
+
+ConversionResult<media::AudioPortConfigExt> legacy2aidl_AudioPortConfigExt(
+        const audio_port_config_ext& legacy, audio_port_type_t type, audio_port_role_t role) {
+    media::AudioPortConfigExt aidl;
+
+    switch (type) {
+        case AUDIO_PORT_TYPE_NONE:
+            break;
+        case AUDIO_PORT_TYPE_DEVICE:
+            aidl.device.push_back(
+                    VALUE_OR_RETURN(legacy2aidl_AudioPortConfigDeviceExt(legacy.device)));
+            break;
+        case AUDIO_PORT_TYPE_MIX:
+            aidl.mix.push_back(
+                    VALUE_OR_RETURN(legacy2aidl_AudioPortConfigMixExt(legacy.mix, role)));
+            break;
+        case AUDIO_PORT_TYPE_SESSION:
+            aidl.session.push_back(
+                    VALUE_OR_RETURN(legacy2aidl_AudioPortConfigSessionExt(legacy.session)));
+            break;
+        default:
+            LOG_ALWAYS_FATAL("Shouldn't get here");
+    }
+    return aidl;
+}
+
+ConversionResult<audio_port_config> aidl2legacy_AudioPortConfig_audio_port_config(
+        const media::AudioPortConfig& aidl) {
+    audio_port_config legacy;
+    legacy.id = VALUE_OR_RETURN(convertReinterpret<audio_port_handle_t>(aidl.id));
+    legacy.role = VALUE_OR_RETURN(aidl2legacy_AudioPortRole_audio_port_role_t(aidl.role));
+    legacy.type = VALUE_OR_RETURN(aidl2legacy_AudioPortType_audio_port_type_t(aidl.type));
+    legacy.config_mask = VALUE_OR_RETURN(aidl2legacy_int32_t_config_mask(aidl.configMask));
+    if (bitmaskIsSet(aidl.configMask, media::AudioPortConfigType::SAMPLE_RATE)) {
+        legacy.sample_rate = VALUE_OR_RETURN(convertIntegral<unsigned int>(aidl.sampleRate));
+    }
+    if (bitmaskIsSet(aidl.configMask, media::AudioPortConfigType::CHANNEL_MASK)) {
+        legacy.channel_mask =
+                VALUE_OR_RETURN(aidl2legacy_int32_t_audio_channel_mask_t(aidl.channelMask));
+    }
+    if (bitmaskIsSet(aidl.configMask, media::AudioPortConfigType::FORMAT)) {
+        legacy.format = VALUE_OR_RETURN(aidl2legacy_AudioFormat_audio_format_t(aidl.format));
+    }
+    if (bitmaskIsSet(aidl.configMask, media::AudioPortConfigType::GAIN)) {
+        legacy.gain = VALUE_OR_RETURN(
+                aidl2legacy_AudioGainConfig_audio_gain_config(aidl.gain, aidl.role, aidl.type));
+    }
+    if (bitmaskIsSet(aidl.configMask, media::AudioPortConfigType::FLAGS)) {
+        legacy.flags = VALUE_OR_RETURN(
+                aidl2legacy_AudioIoFlags_audio_io_flags(aidl.flags, aidl.role, aidl.type));
+    }
+    legacy.ext = VALUE_OR_RETURN(aidl2legacy_AudioPortConfigExt(aidl.ext, aidl.type, aidl.role));
+    return legacy;
+}
+
+ConversionResult<media::AudioPortConfig> legacy2aidl_audio_port_config_AudioPortConfig(
+        const audio_port_config& legacy) {
+    media::AudioPortConfig aidl;
+    aidl.id = VALUE_OR_RETURN(convertReinterpret<audio_port_handle_t>(legacy.id));
+    aidl.role = VALUE_OR_RETURN(legacy2aidl_audio_port_role_t_AudioPortRole(legacy.role));
+    aidl.type = VALUE_OR_RETURN(legacy2aidl_audio_port_type_t_AudioPortType(legacy.type));
+    aidl.configMask = VALUE_OR_RETURN(legacy2aidl_config_mask_int32_t(legacy.config_mask));
+    if (legacy.config_mask & AUDIO_PORT_CONFIG_SAMPLE_RATE) {
+        aidl.sampleRate = VALUE_OR_RETURN(convertIntegral<int32_t>(legacy.sample_rate));
+    }
+    if (legacy.config_mask & AUDIO_PORT_CONFIG_CHANNEL_MASK) {
+        aidl.channelMask =
+                VALUE_OR_RETURN(legacy2aidl_audio_channel_mask_t_int32_t(legacy.channel_mask));
+    }
+    if (legacy.config_mask & AUDIO_PORT_CONFIG_FORMAT) {
+        aidl.format = VALUE_OR_RETURN(legacy2aidl_audio_format_t_AudioFormat(legacy.format));
+    }
+    if (legacy.config_mask & AUDIO_PORT_CONFIG_GAIN) {
+        aidl.gain = VALUE_OR_RETURN(legacy2aidl_audio_gain_config_AudioGainConfig(
+                legacy.gain, legacy.role, legacy.type));
+    }
+    if (legacy.config_mask & AUDIO_PORT_CONFIG_FLAGS) {
+        aidl.flags = VALUE_OR_RETURN(
+                legacy2aidl_audio_io_flags_AudioIoFlags(legacy.flags, legacy.role, legacy.type));
+    }
+    aidl.ext =
+            VALUE_OR_RETURN(legacy2aidl_AudioPortConfigExt(legacy.ext, legacy.type, legacy.role));
+    return aidl;
+}
+
+ConversionResult<struct audio_patch> aidl2legacy_AudioPatch_audio_patch(
+        const media::AudioPatch& aidl) {
+    struct audio_patch legacy;
+    legacy.id = VALUE_OR_RETURN(convertReinterpret<audio_patch_handle_t>(aidl.id));
+    legacy.num_sinks = VALUE_OR_RETURN(convertIntegral<unsigned int>(aidl.sinks.size()));
+    if (legacy.num_sinks > AUDIO_PATCH_PORTS_MAX) {
+        return unexpected(BAD_VALUE);
+    }
+    for (size_t i = 0; i < legacy.num_sinks; ++i) {
+        legacy.sinks[i] =
+                VALUE_OR_RETURN(aidl2legacy_AudioPortConfig_audio_port_config(aidl.sinks[i]));
+    }
+    legacy.num_sources = VALUE_OR_RETURN(convertIntegral<unsigned int>(aidl.sources.size()));
+    if (legacy.num_sources > AUDIO_PATCH_PORTS_MAX) {
+        return unexpected(BAD_VALUE);
+    }
+    for (size_t i = 0; i < legacy.num_sources; ++i) {
+        legacy.sources[i] =
+                VALUE_OR_RETURN(aidl2legacy_AudioPortConfig_audio_port_config(aidl.sources[i]));
+    }
+    return legacy;
+}
+
+ConversionResult<media::AudioPatch> legacy2aidl_audio_patch_AudioPatch(
+        const struct audio_patch& legacy) {
+    media::AudioPatch aidl;
+    aidl.id = VALUE_OR_RETURN(convertReinterpret<int32_t>(legacy.id));
+
+    if (legacy.num_sinks > AUDIO_PATCH_PORTS_MAX) {
+        return unexpected(BAD_VALUE);
+    }
+    for (unsigned int i = 0; i < legacy.num_sinks; ++i) {
+        aidl.sinks.push_back(
+                VALUE_OR_RETURN(legacy2aidl_audio_port_config_AudioPortConfig(legacy.sinks[i])));
+    }
+    if (legacy.num_sources > AUDIO_PATCH_PORTS_MAX) {
+        return unexpected(BAD_VALUE);
+    }
+    for (unsigned int i = 0; i < legacy.num_sources; ++i) {
+        aidl.sources.push_back(
+                VALUE_OR_RETURN(legacy2aidl_audio_port_config_AudioPortConfig(legacy.sources[i])));
+    }
+    return aidl;
+}
+
+ConversionResult<sp<AudioIoDescriptor>> aidl2legacy_AudioIoDescriptor_AudioIoDescriptor(
+        const media::AudioIoDescriptor& aidl) {
+    sp<AudioIoDescriptor> legacy(new AudioIoDescriptor());
+    legacy->mIoHandle = VALUE_OR_RETURN(convertReinterpret<audio_io_handle_t>(aidl.ioHandle));
+    legacy->mPatch = VALUE_OR_RETURN(aidl2legacy_AudioPatch_audio_patch(aidl.patch));
+    legacy->mSamplingRate = VALUE_OR_RETURN(convertIntegral<uint32_t>(aidl.samplingRate));
+    legacy->mFormat = VALUE_OR_RETURN(aidl2legacy_AudioFormat_audio_format_t(aidl.format));
+    legacy->mChannelMask =
+            VALUE_OR_RETURN(aidl2legacy_int32_t_audio_channel_mask_t(aidl.channelMask));
+    legacy->mFrameCount = VALUE_OR_RETURN(convertIntegral<size_t>(aidl.frameCount));
+    legacy->mFrameCountHAL = VALUE_OR_RETURN(convertIntegral<size_t>(aidl.frameCountHAL));
+    legacy->mLatency = VALUE_OR_RETURN(convertIntegral<uint32_t>(aidl.latency));
+    legacy->mPortId = VALUE_OR_RETURN(convertReinterpret<audio_port_handle_t>(aidl.portId));
+    return legacy;
+}
+
+ConversionResult<media::AudioIoDescriptor> legacy2aidl_AudioIoDescriptor_AudioIoDescriptor(
+        const sp<AudioIoDescriptor>& legacy) {
+    media::AudioIoDescriptor aidl;
+    aidl.ioHandle = VALUE_OR_RETURN(convertReinterpret<int32_t>(legacy->mIoHandle));
+    aidl.patch = VALUE_OR_RETURN(legacy2aidl_audio_patch_AudioPatch(legacy->mPatch));
+    aidl.samplingRate = VALUE_OR_RETURN(convertIntegral<int32_t>(legacy->mSamplingRate));
+    aidl.format = VALUE_OR_RETURN(legacy2aidl_audio_format_t_AudioFormat(legacy->mFormat));
+    aidl.channelMask = VALUE_OR_RETURN(convertReinterpret<int32_t>(legacy->mChannelMask));
+    aidl.frameCount = VALUE_OR_RETURN(convertIntegral<int64_t>(legacy->mFrameCount));
+    aidl.frameCountHAL = VALUE_OR_RETURN(convertIntegral<int64_t>(legacy->mFrameCountHAL));
+    aidl.latency = VALUE_OR_RETURN(convertIntegral<int32_t>(legacy->mLatency));
+    aidl.portId = VALUE_OR_RETURN(convertReinterpret<int32_t>(legacy->mPortId));
+    return aidl;
+}
+
+}  // namespace android
diff --git a/media/libaudioclient/Android.bp b/media/libaudioclient/Android.bp
index d7e9461..fef0ca9 100644
--- a/media/libaudioclient/Android.bp
+++ b/media/libaudioclient/Android.bp
@@ -2,6 +2,7 @@
     name: "libaudioclient_headers",
     vendor_available: true,
     min_sdk_version: "29",
+    host_supported: true,
 
     header_libs: [
         "libaudiofoundation_headers",
@@ -12,7 +13,12 @@
     export_header_lib_headers: [
         "libaudiofoundation_headers",
     ],
-    host_supported: true,
+    static_libs: [
+        "audioflinger-aidl-unstable-cpp",
+    ],
+    export_static_lib_headers: [
+        "audioflinger-aidl-unstable-cpp",
+    ],
     target: {
         darwin: {
             enabled: false,
@@ -29,6 +35,7 @@
         "AudioVolumeGroup.cpp",
     ],
     shared_libs: [
+        "audioflinger-aidl-unstable-cpp",
         "capture_state_listener-aidl-cpp",
         "libaudiofoundation",
         "libaudioutils",
@@ -44,6 +51,7 @@
     include_dirs: ["system/media/audio_utils/include"],
     export_include_dirs: ["include"],
     export_shared_lib_headers: [
+        "audioflinger-aidl-unstable-cpp",
         "capture_state_listener-aidl-cpp",
     ],
 }
@@ -73,7 +81,6 @@
         "AudioTrack.cpp",
         "AudioTrackShared.cpp",
         "IAudioFlinger.cpp",
-        "IAudioFlingerClient.cpp",
         "IAudioPolicyService.cpp",
         "IAudioPolicyServiceClient.cpp",
         "IAudioTrack.cpp",
@@ -83,7 +90,9 @@
         "TrackPlayerBase.cpp",
     ],
     shared_libs: [
+        "audioflinger-aidl-unstable-cpp",
         "capture_state_listener-aidl-cpp",
+        "libaudioclient_aidl_conversion",
         "libaudiofoundation",
         "libaudioutils",
         "libaudiopolicy",
@@ -101,7 +110,10 @@
         "libutils",
         "libvibrator",
     ],
-    export_shared_lib_headers: ["libbinder"],
+    export_shared_lib_headers: [
+        "audioflinger-aidl-unstable-cpp",
+        "libbinder",
+    ],
 
     include_dirs: [
         "frameworks/av/media/libnbaio/include_mono/",
@@ -140,6 +152,32 @@
     },
 }
 
+cc_library_shared {
+    name: "libaudioclient_aidl_conversion",
+    srcs: ["AidlConversion.cpp"],
+    local_include_dirs: ["include"],
+    shared_libs: [
+        "audioclient-types-aidl-unstable-cpp",
+        "libbase",
+        "liblog",
+        "libutils",
+    ],
+    export_shared_lib_headers: [
+        "audioclient-types-aidl-unstable-cpp",
+    ],
+    cflags: [
+        "-Wall",
+        "-Werror",
+        "-Wno-error=deprecated-declarations",
+    ],
+    sanitize: {
+        misc_undefined: [
+            "unsigned-integer-overflow",
+            "signed-integer-overflow",
+        ],
+    },
+}
+
 // AIDL interface between libaudioclient and framework.jar
 filegroup {
     name: "libaudioclient_aidl",
@@ -189,3 +227,61 @@
         "shared-file-region-aidl",
     ],
 }
+
+aidl_interface {
+    name: "audioclient-types-aidl",
+    unstable: true,
+    host_supported: true,
+    vendor_available: true,
+    double_loadable: true,
+    local_include_dir: "aidl",
+    srcs: [
+        "aidl/android/media/AudioGainConfig.aidl",
+        "aidl/android/media/AudioGainMode.aidl",
+        "aidl/android/media/AudioInputFlags.aidl",
+        "aidl/android/media/AudioIoConfigEvent.aidl",
+        "aidl/android/media/AudioIoDescriptor.aidl",
+        "aidl/android/media/AudioIoFlags.aidl",
+        "aidl/android/media/AudioOutputFlags.aidl",
+        "aidl/android/media/AudioPatch.aidl",
+        "aidl/android/media/AudioPortConfig.aidl",
+        "aidl/android/media/AudioPortConfigType.aidl",
+        "aidl/android/media/AudioPortConfigDeviceExt.aidl",
+        "aidl/android/media/AudioPortConfigExt.aidl",
+        "aidl/android/media/AudioPortConfigMixExt.aidl",
+        "aidl/android/media/AudioPortConfigMixExtUseCase.aidl",
+        "aidl/android/media/AudioPortConfigSessionExt.aidl",
+        "aidl/android/media/AudioPortRole.aidl",
+        "aidl/android/media/AudioPortType.aidl",
+        "aidl/android/media/AudioSessionType.aidl",
+        "aidl/android/media/AudioSourceType.aidl",
+        "aidl/android/media/AudioStreamType.aidl",
+    ],
+    imports: [
+        "audio_common-aidl",
+    ],
+}
+
+aidl_interface {
+    name: "audioflinger-aidl",
+    unstable: true,
+    local_include_dir: "aidl",
+    host_supported: true,
+    vendor_available: true,
+    srcs: [
+        "aidl/android/media/IAudioFlingerClient.aidl",
+    ],
+    imports: [
+        "audioclient-types-aidl",
+    ],
+    double_loadable: true,
+    backend: {
+        cpp: {
+            min_sdk_version: "29",
+            apex_available: [
+                "//apex_available:platform",
+                "com.android.media",
+            ],
+        },
+    },
+}
diff --git a/media/libaudioclient/AudioSystem.cpp b/media/libaudioclient/AudioSystem.cpp
index edb0889..0507879 100644
--- a/media/libaudioclient/AudioSystem.cpp
+++ b/media/libaudioclient/AudioSystem.cpp
@@ -23,6 +23,7 @@
 #include <binder/IServiceManager.h>
 #include <binder/ProcessState.h>
 #include <binder/IPCThreadState.h>
+#include <media/AidlConversion.h>
 #include <media/AudioResamplerPublic.h>
 #include <media/AudioSystem.h>
 #include <media/IAudioFlinger.h>
@@ -32,10 +33,17 @@
 
 #include <system/audio.h>
 
+#define VALUE_OR_RETURN(x) \
+    ({ auto _tmp = (x); \
+       if (!_tmp.ok()) return Status::fromStatusT(_tmp.error()); \
+       _tmp.value(); })
+
 // ----------------------------------------------------------------------------
 
 namespace android {
 
+using binder::Status;
+
 // client singleton for AudioFlinger binder interface
 Mutex AudioSystem::gLock;
 Mutex AudioSystem::gLockErrorCallbacks;
@@ -521,11 +529,17 @@
     ALOGW("AudioFlinger server died!");
 }
 
-void AudioSystem::AudioFlingerClient::ioConfigChanged(audio_io_config_event event,
-                                                      const sp<AudioIoDescriptor>& ioDesc) {
+Status AudioSystem::AudioFlingerClient::ioConfigChanged(
+        media::AudioIoConfigEvent _event,
+        const media::AudioIoDescriptor& _ioDesc) {
+    audio_io_config_event event = VALUE_OR_RETURN(
+            aidl2legacy_AudioIoConfigEvent_audio_io_config_event(_event));
+    sp<AudioIoDescriptor> ioDesc(
+            VALUE_OR_RETURN(aidl2legacy_AudioIoDescriptor_AudioIoDescriptor(_ioDesc)));
+
     ALOGV("ioConfigChanged() event %d", event);
 
-    if (ioDesc == 0 || ioDesc->mIoHandle == AUDIO_IO_HANDLE_NONE) return;
+    if (ioDesc->mIoHandle == AUDIO_IO_HANDLE_NONE) return Status::ok();
 
     audio_port_handle_t deviceId = AUDIO_PORT_HANDLE_NONE;
     std::vector<sp<AudioDeviceCallback>> callbacksToCall;
@@ -640,6 +654,8 @@
         // If callbacksToCall is not empty, it implies ioDesc->mIoHandle and deviceId are valid
         cb->onAudioDeviceUpdate(ioDesc->mIoHandle, deviceId);
     }
+
+    return Status::ok();
 }
 
 status_t AudioSystem::AudioFlingerClient::getInputBufferSize(
diff --git a/media/libaudioclient/IAudioFlinger.cpp b/media/libaudioclient/IAudioFlinger.cpp
index 7c304a1..d86182e 100644
--- a/media/libaudioclient/IAudioFlinger.cpp
+++ b/media/libaudioclient/IAudioFlinger.cpp
@@ -373,7 +373,7 @@
         return reply.readString8();
     }
 
-    virtual void registerClient(const sp<IAudioFlingerClient>& client)
+    virtual void registerClient(const sp<media::IAudioFlingerClient>& client)
     {
         Parcel data, reply;
         data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
@@ -1213,7 +1213,7 @@
 
         case REGISTER_CLIENT: {
             CHECK_INTERFACE(IAudioFlinger, data, reply);
-            sp<IAudioFlingerClient> client = interface_cast<IAudioFlingerClient>(
+            sp<media::IAudioFlingerClient> client = interface_cast<media::IAudioFlingerClient>(
                     data.readStrongBinder());
             registerClient(client);
             return NO_ERROR;
diff --git a/media/libaudioclient/IAudioFlingerClient.cpp b/media/libaudioclient/IAudioFlingerClient.cpp
deleted file mode 100644
index 47eb7dc..0000000
--- a/media/libaudioclient/IAudioFlingerClient.cpp
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "IAudioFlingerClient"
-#include <utils/Log.h>
-
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <binder/Parcel.h>
-
-#include <media/IAudioFlingerClient.h>
-#include <media/AudioSystem.h>
-
-namespace android {
-
-enum {
-    IO_CONFIG_CHANGED = IBinder::FIRST_CALL_TRANSACTION
-};
-
-class BpAudioFlingerClient : public BpInterface<IAudioFlingerClient>
-{
-public:
-    explicit BpAudioFlingerClient(const sp<IBinder>& impl)
-        : BpInterface<IAudioFlingerClient>(impl)
-    {
-    }
-
-    void ioConfigChanged(audio_io_config_event event, const sp<AudioIoDescriptor>& ioDesc)
-    {
-        Parcel data, reply;
-        data.writeInterfaceToken(IAudioFlingerClient::getInterfaceDescriptor());
-        data.writeInt32(event);
-        data.writeInt32((int32_t)ioDesc->mIoHandle);
-        data.write(&ioDesc->mPatch, sizeof(struct audio_patch));
-        data.writeInt32(ioDesc->mSamplingRate);
-        data.writeInt32(ioDesc->mFormat);
-        data.writeInt32(ioDesc->mChannelMask);
-        data.writeInt64(ioDesc->mFrameCount);
-        data.writeInt64(ioDesc->mFrameCountHAL);
-        data.writeInt32(ioDesc->mLatency);
-        data.writeInt32(ioDesc->mPortId);
-        remote()->transact(IO_CONFIG_CHANGED, data, &reply, IBinder::FLAG_ONEWAY);
-    }
-};
-
-IMPLEMENT_META_INTERFACE(AudioFlingerClient, "android.media.IAudioFlingerClient");
-
-// ----------------------------------------------------------------------
-
-status_t BnAudioFlingerClient::onTransact(
-    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
-{
-    switch (code) {
-    case IO_CONFIG_CHANGED: {
-            CHECK_INTERFACE(IAudioFlingerClient, data, reply);
-            audio_io_config_event event = (audio_io_config_event)data.readInt32();
-            sp<AudioIoDescriptor> ioDesc = new AudioIoDescriptor();
-            ioDesc->mIoHandle = (audio_io_handle_t) data.readInt32();
-            data.read(&ioDesc->mPatch, sizeof(struct audio_patch));
-            ioDesc->mSamplingRate = data.readInt32();
-            ioDesc->mFormat = (audio_format_t) data.readInt32();
-            ioDesc->mChannelMask = (audio_channel_mask_t) data.readInt32();
-            ioDesc->mFrameCount = data.readInt64();
-            ioDesc->mFrameCountHAL = data.readInt64();
-            ioDesc->mLatency = data.readInt32();
-            ioDesc->mPortId = data.readInt32();
-            ioConfigChanged(event, ioDesc);
-            return NO_ERROR;
-        } break;
-        default:
-            return BBinder::onTransact(code, data, reply, flags);
-    }
-}
-
-// ----------------------------------------------------------------------------
-
-} // namespace android
diff --git a/media/libaudioclient/aidl/android/media/AudioGainConfig.aidl b/media/libaudioclient/aidl/android/media/AudioGainConfig.aidl
new file mode 100644
index 0000000..b93c2dc
--- /dev/null
+++ b/media/libaudioclient/aidl/android/media/AudioGainConfig.aidl
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+/**
+ * {@hide}
+ */
+parcelable AudioGainConfig {
+    /** Index of the corresponding audio_gain in the audio_port gains[] table. */
+    int index;
+
+    /** Mode requested for this command. Bitfield indexed by AudioGainMode. */
+    int mode;
+
+    /**
+     * Channels which gain value follows. N/A in joint mode.
+     * Interpreted as audio_channel_mask_t.
+     */
+    int channelMask;
+
+    /**
+     * Gain values in millibels.
+     * For each channel ordered from LSb to MSb in channel mask. The number of values is 1 in joint
+     * mode, otherwise equals the number of bits implied by channelMask.
+     */
+    int[]  values;
+
+    /** Ramp duration in ms. */
+    int rampDurationMs;
+}
diff --git a/media/libmediatranscoding/aidl/android/media/TranscodingJobPriority.aidl b/media/libaudioclient/aidl/android/media/AudioGainMode.aidl
similarity index 65%
copy from media/libmediatranscoding/aidl/android/media/TranscodingJobPriority.aidl
copy to media/libaudioclient/aidl/android/media/AudioGainMode.aidl
index 1a5d81a..39395e5 100644
--- a/media/libmediatranscoding/aidl/android/media/TranscodingJobPriority.aidl
+++ b/media/libaudioclient/aidl/android/media/AudioGainMode.aidl
@@ -13,25 +13,11 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
 package android.media;
 
-/**
- * Priority of a transcoding job.
- *
- * {@hide}
- */
 @Backing(type="int")
-enum TranscodingJobPriority {
-    // TODO(hkuang): define what each priority level actually mean.
-    kUnspecified = 0,
-    kLow = 1,
-    /**
-     * 2 ~ 20 is reserved for future use.
-     */
-    kNormal = 21,
-    /**
-     * 22 ~ 30 is reserved for future use.
-     */
-    kHigh = 31,
-}
\ No newline at end of file
+enum AudioGainMode {
+    JOINT    = 0,
+    CHANNELS = 1,
+    RAMP     = 2,
+}
diff --git a/media/libmediatranscoding/aidl/android/media/TranscodingJobPriority.aidl b/media/libaudioclient/aidl/android/media/AudioInputFlags.aidl
similarity index 65%
copy from media/libmediatranscoding/aidl/android/media/TranscodingJobPriority.aidl
copy to media/libaudioclient/aidl/android/media/AudioInputFlags.aidl
index 1a5d81a..8f517e7 100644
--- a/media/libmediatranscoding/aidl/android/media/TranscodingJobPriority.aidl
+++ b/media/libaudioclient/aidl/android/media/AudioInputFlags.aidl
@@ -13,25 +13,16 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
 package android.media;
 
-/**
- * Priority of a transcoding job.
- *
- * {@hide}
- */
 @Backing(type="int")
-enum TranscodingJobPriority {
-    // TODO(hkuang): define what each priority level actually mean.
-    kUnspecified = 0,
-    kLow = 1,
-    /**
-     * 2 ~ 20 is reserved for future use.
-     */
-    kNormal = 21,
-    /**
-     * 22 ~ 30 is reserved for future use.
-     */
-    kHigh = 31,
-}
\ No newline at end of file
+enum AudioInputFlags {
+    FAST       = 0,
+    HW_HOTWORD = 1,
+    RAW        = 2,
+    SYNC       = 3,
+    MMAP_NOIRQ = 4,
+    VOIP_TX    = 5,
+    HW_AV_SYNC = 6,
+    DIRECT     = 7,
+}
diff --git a/media/libmediatranscoding/aidl/android/media/TranscodingJobPriority.aidl b/media/libaudioclient/aidl/android/media/AudioIoConfigEvent.aidl
similarity index 67%
copy from media/libmediatranscoding/aidl/android/media/TranscodingJobPriority.aidl
copy to media/libaudioclient/aidl/android/media/AudioIoConfigEvent.aidl
index 1a5d81a..d5f23a1 100644
--- a/media/libmediatranscoding/aidl/android/media/TranscodingJobPriority.aidl
+++ b/media/libaudioclient/aidl/android/media/AudioIoConfigEvent.aidl
@@ -17,21 +17,17 @@
 package android.media;
 
 /**
- * Priority of a transcoding job.
- *
  * {@hide}
  */
 @Backing(type="int")
-enum TranscodingJobPriority {
-    // TODO(hkuang): define what each priority level actually mean.
-    kUnspecified = 0,
-    kLow = 1,
-    /**
-     * 2 ~ 20 is reserved for future use.
-     */
-    kNormal = 21,
-    /**
-     * 22 ~ 30 is reserved for future use.
-     */
-    kHigh = 31,
-}
\ No newline at end of file
+enum AudioIoConfigEvent {
+    OUTPUT_REGISTERED = 0,
+    OUTPUT_OPENED = 1,
+    OUTPUT_CLOSED = 2,
+    OUTPUT_CONFIG_CHANGED = 3,
+    INPUT_REGISTERED = 4,
+    INPUT_OPENED = 5,
+    INPUT_CLOSED = 6,
+    INPUT_CONFIG_CHANGED = 7,
+    CLIENT_STARTED = 8,
+}
diff --git a/media/libaudioclient/aidl/android/media/AudioIoDescriptor.aidl b/media/libaudioclient/aidl/android/media/AudioIoDescriptor.aidl
new file mode 100644
index 0000000..876ef9b
--- /dev/null
+++ b/media/libaudioclient/aidl/android/media/AudioIoDescriptor.aidl
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.media.AudioPatch;
+import android.media.audio.common.AudioFormat;
+
+/**
+ * {@hide}
+ */
+parcelable AudioIoDescriptor {
+    /** Interpreted as audio_io_handle_t. */
+    int ioHandle;
+    AudioPatch patch;
+    int samplingRate;
+    AudioFormat format;
+    /** Interpreted as audio_channel_mask_t. */
+    int channelMask;
+    long frameCount;
+    long frameCountHAL;
+    /** Only valid for output. */
+    int latency;
+    /**
+     * Interpreted as audio_port_handle_t.
+     * valid for event AUDIO_CLIENT_STARTED.
+     */
+    int portId;
+}
diff --git a/media/libmediatranscoding/aidl/android/media/TranscodingJobPriority.aidl b/media/libaudioclient/aidl/android/media/AudioIoFlags.aidl
similarity index 65%
copy from media/libmediatranscoding/aidl/android/media/TranscodingJobPriority.aidl
copy to media/libaudioclient/aidl/android/media/AudioIoFlags.aidl
index 1a5d81a..1fe2acc 100644
--- a/media/libmediatranscoding/aidl/android/media/TranscodingJobPriority.aidl
+++ b/media/libaudioclient/aidl/android/media/AudioIoFlags.aidl
@@ -17,21 +17,14 @@
 package android.media;
 
 /**
- * Priority of a transcoding job.
- *
  * {@hide}
  */
-@Backing(type="int")
-enum TranscodingJobPriority {
-    // TODO(hkuang): define what each priority level actually mean.
-    kUnspecified = 0,
-    kLow = 1,
-    /**
-     * 2 ~ 20 is reserved for future use.
-     */
-    kNormal = 21,
-    /**
-     * 22 ~ 30 is reserved for future use.
-     */
-    kHigh = 31,
-}
\ No newline at end of file
+// TODO(b/150948558): This should be a union. In the meantime, we require
+// that exactly one of the below arrays has a single element and the rest
+// are empty.
+parcelable AudioIoFlags {
+    /** Bitmask indexed by AudioInputFlags. */
+    int[] input;
+    /** Bitmask indexed by AudioOutputFlags. */
+    int[] output;
+}
diff --git a/media/libmediatranscoding/aidl/android/media/TranscodingJobPriority.aidl b/media/libaudioclient/aidl/android/media/AudioOutputFlags.aidl
similarity index 60%
copy from media/libmediatranscoding/aidl/android/media/TranscodingJobPriority.aidl
copy to media/libaudioclient/aidl/android/media/AudioOutputFlags.aidl
index 1a5d81a..aebf871 100644
--- a/media/libmediatranscoding/aidl/android/media/TranscodingJobPriority.aidl
+++ b/media/libaudioclient/aidl/android/media/AudioOutputFlags.aidl
@@ -13,25 +13,23 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
 package android.media;
 
-/**
- * Priority of a transcoding job.
- *
- * {@hide}
- */
 @Backing(type="int")
-enum TranscodingJobPriority {
-    // TODO(hkuang): define what each priority level actually mean.
-    kUnspecified = 0,
-    kLow = 1,
-    /**
-     * 2 ~ 20 is reserved for future use.
-     */
-    kNormal = 21,
-    /**
-     * 22 ~ 30 is reserved for future use.
-     */
-    kHigh = 31,
-}
\ No newline at end of file
+enum AudioOutputFlags {
+    DIRECT           = 0,
+    PRIMARY          = 1,
+    FAST             = 2,
+    DEEP_BUFFER      = 3,
+    COMPRESS_OFFLOAD = 4,
+    NON_BLOCKING     = 5,
+    HW_AV_SYNC       = 6,
+    TTS              = 7,
+    RAW              = 8,
+    SYNC             = 9,
+    IEC958_NONAUDIO  = 10,
+    DIRECT_PCM       = 11,
+    MMAP_NOIRQ       = 12,
+    VOIP_RX          = 13,
+    INCALL_MUSIC     = 14,
+}
diff --git a/media/libmediatranscoding/aidl/android/media/TranscodingJobPriority.aidl b/media/libaudioclient/aidl/android/media/AudioPatch.aidl
similarity index 67%
copy from media/libmediatranscoding/aidl/android/media/TranscodingJobPriority.aidl
copy to media/libaudioclient/aidl/android/media/AudioPatch.aidl
index 1a5d81a..8519faf 100644
--- a/media/libmediatranscoding/aidl/android/media/TranscodingJobPriority.aidl
+++ b/media/libaudioclient/aidl/android/media/AudioPatch.aidl
@@ -16,22 +16,17 @@
 
 package android.media;
 
+import android.media.AudioPortConfig;
+
 /**
- * Priority of a transcoding job.
- *
  * {@hide}
  */
-@Backing(type="int")
-enum TranscodingJobPriority {
-    // TODO(hkuang): define what each priority level actually mean.
-    kUnspecified = 0,
-    kLow = 1,
+parcelable AudioPatch {
     /**
-     * 2 ~ 20 is reserved for future use.
+     * Patch unique ID.
+     * Interpreted as audio_patch_handle_t.
      */
-    kNormal = 21,
-    /**
-     * 22 ~ 30 is reserved for future use.
-     */
-    kHigh = 31,
-}
\ No newline at end of file
+    int id;
+    AudioPortConfig[] sources;
+    AudioPortConfig[] sinks;
+}
diff --git a/media/libaudioclient/aidl/android/media/AudioPortConfig.aidl b/media/libaudioclient/aidl/android/media/AudioPortConfig.aidl
new file mode 100644
index 0000000..2dd30a4
--- /dev/null
+++ b/media/libaudioclient/aidl/android/media/AudioPortConfig.aidl
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.media.AudioGainConfig;
+import android.media.AudioIoFlags;
+import android.media.AudioPortConfigExt;
+import android.media.AudioPortConfigType;
+import android.media.AudioPortRole;
+import android.media.AudioPortType;
+import android.media.audio.common.AudioFormat;
+
+/**
+ * {@hide}
+ */
+parcelable AudioPortConfig {
+    /**
+     * Port unique ID.
+     * Interpreted as audio_port_handle_t.
+     */
+    int id;
+    /** Sink or source. */
+    AudioPortRole role;
+    /** Device, mix ... */
+    AudioPortType type;
+    /** Bitmask, indexed by AudioPortConfigType. */
+    int configMask;
+    /** Sampling rate in Hz. */
+    int sampleRate;
+    /**
+     * Channel mask, if applicable.
+     * Interpreted as audio_channel_mask_t.
+     * TODO: bitmask?
+     */
+    int channelMask;
+    /**
+     * Format, if applicable.
+     */
+    AudioFormat format;
+    /** Gain to apply, if applicable. */
+    AudioGainConfig gain;
+    /** Framework only: HW_AV_SYNC, DIRECT, ... */
+    AudioIoFlags flags;
+    AudioPortConfigExt ext;
+}
diff --git a/media/libmediatranscoding/aidl/android/media/TranscodingJobPriority.aidl b/media/libaudioclient/aidl/android/media/AudioPortConfigDeviceExt.aidl
similarity index 64%
copy from media/libmediatranscoding/aidl/android/media/TranscodingJobPriority.aidl
copy to media/libaudioclient/aidl/android/media/AudioPortConfigDeviceExt.aidl
index 1a5d81a..a99aa9b 100644
--- a/media/libmediatranscoding/aidl/android/media/TranscodingJobPriority.aidl
+++ b/media/libaudioclient/aidl/android/media/AudioPortConfigDeviceExt.aidl
@@ -17,21 +17,20 @@
 package android.media;
 
 /**
- * Priority of a transcoding job.
- *
  * {@hide}
  */
-@Backing(type="int")
-enum TranscodingJobPriority {
-    // TODO(hkuang): define what each priority level actually mean.
-    kUnspecified = 0,
-    kLow = 1,
+parcelable AudioPortConfigDeviceExt {
     /**
-     * 2 ~ 20 is reserved for future use.
+     * Module the device is attached to.
+     * Interpreted as audio_module_handle_t.
      */
-    kNormal = 21,
+    int hwModule;
     /**
-     * 22 ~ 30 is reserved for future use.
+     * Device type (e.g AUDIO_DEVICE_OUT_SPEAKER).
+     * Interpreted as audio_devices_t.
+     * TODO: Convert to a standalone AIDL representation.
      */
-    kHigh = 31,
-}
\ No newline at end of file
+    int type;
+    /** Device address. "" if N/A. */
+    @utf8InCpp String address;
+}
diff --git a/media/libaudioclient/aidl/android/media/AudioPortConfigExt.aidl b/media/libaudioclient/aidl/android/media/AudioPortConfigExt.aidl
new file mode 100644
index 0000000..83e985e
--- /dev/null
+++ b/media/libaudioclient/aidl/android/media/AudioPortConfigExt.aidl
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.media.AudioPortConfigDeviceExt;
+import android.media.AudioPortConfigMixExt;
+import android.media.AudioPortConfigSessionExt;
+
+/**
+ * {@hide}
+ */
+parcelable AudioPortConfigExt {
+    // TODO(b/150948558): This should be a union. In the meantime, we require
+    // that exactly one of the below arrays has a single element and the rest
+    // are empty.
+
+    /** Device specific info. */
+    AudioPortConfigDeviceExt[] device;
+    /** Mix specific info. */
+    AudioPortConfigMixExt[] mix;
+    /** Session specific info. */
+    AudioPortConfigSessionExt[] session;
+}
diff --git a/media/libmediatranscoding/aidl/android/media/TranscodingJobPriority.aidl b/media/libaudioclient/aidl/android/media/AudioPortConfigMixExt.aidl
similarity index 66%
copy from media/libmediatranscoding/aidl/android/media/TranscodingJobPriority.aidl
copy to media/libaudioclient/aidl/android/media/AudioPortConfigMixExt.aidl
index 1a5d81a..d3226f2 100644
--- a/media/libmediatranscoding/aidl/android/media/TranscodingJobPriority.aidl
+++ b/media/libaudioclient/aidl/android/media/AudioPortConfigMixExt.aidl
@@ -16,22 +16,21 @@
 
 package android.media;
 
+import android.media.AudioPortConfigMixExtUseCase;
+
 /**
- * Priority of a transcoding job.
- *
  * {@hide}
  */
-@Backing(type="int")
-enum TranscodingJobPriority {
-    // TODO(hkuang): define what each priority level actually mean.
-    kUnspecified = 0,
-    kLow = 1,
+parcelable AudioPortConfigMixExt {
     /**
-     * 2 ~ 20 is reserved for future use.
+     * Module the stream is attached to.
+     * Interpreted as audio_module_handle_t.
      */
-    kNormal = 21,
+    int hwModule;
     /**
-     * 22 ~ 30 is reserved for future use.
+     * I/O handle of the input/output stream.
+     * Interpreted as audio_io_handle_t.
      */
-    kHigh = 31,
-}
\ No newline at end of file
+    int handle;
+    AudioPortConfigMixExtUseCase usecase;
+}
diff --git a/media/libaudioclient/aidl/android/media/AudioPortConfigMixExtUseCase.aidl b/media/libaudioclient/aidl/android/media/AudioPortConfigMixExtUseCase.aidl
new file mode 100644
index 0000000..675daf8
--- /dev/null
+++ b/media/libaudioclient/aidl/android/media/AudioPortConfigMixExtUseCase.aidl
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.media.AudioSourceType;
+import android.media.AudioStreamType;
+
+/**
+ * {@hide}
+ */
+parcelable AudioPortConfigMixExtUseCase {
+    // TODO(b/150948558): This should be a union. In the meantime, we require
+    // that exactly one of the below arrays has a single element and the rest
+    // are empty.
+
+    /** This to be set if the containing config has the AudioPortRole::SOURCE role. */
+    AudioStreamType[] stream;
+    /** This to be set if the containing config has the AudioPortRole::SINK role. */
+    AudioSourceType[] source;
+}
diff --git a/media/libmediatranscoding/aidl/android/media/TranscodingJobPriority.aidl b/media/libaudioclient/aidl/android/media/AudioPortConfigSessionExt.aidl
similarity index 65%
copy from media/libmediatranscoding/aidl/android/media/TranscodingJobPriority.aidl
copy to media/libaudioclient/aidl/android/media/AudioPortConfigSessionExt.aidl
index 1a5d81a..d3261d9 100644
--- a/media/libmediatranscoding/aidl/android/media/TranscodingJobPriority.aidl
+++ b/media/libaudioclient/aidl/android/media/AudioPortConfigSessionExt.aidl
@@ -16,22 +16,11 @@
 
 package android.media;
 
+import android.media.AudioSessionType;
+
 /**
- * Priority of a transcoding job.
- *
  * {@hide}
  */
-@Backing(type="int")
-enum TranscodingJobPriority {
-    // TODO(hkuang): define what each priority level actually mean.
-    kUnspecified = 0,
-    kLow = 1,
-    /**
-     * 2 ~ 20 is reserved for future use.
-     */
-    kNormal = 21,
-    /**
-     * 22 ~ 30 is reserved for future use.
-     */
-    kHigh = 31,
-}
\ No newline at end of file
+parcelable AudioPortConfigSessionExt {
+    AudioSessionType session;
+}
diff --git a/media/libmediatranscoding/aidl/android/media/TranscodingJobPriority.aidl b/media/libaudioclient/aidl/android/media/AudioPortConfigType.aidl
similarity index 65%
copy from media/libmediatranscoding/aidl/android/media/TranscodingJobPriority.aidl
copy to media/libaudioclient/aidl/android/media/AudioPortConfigType.aidl
index 1a5d81a..c7bb4d8 100644
--- a/media/libmediatranscoding/aidl/android/media/TranscodingJobPriority.aidl
+++ b/media/libaudioclient/aidl/android/media/AudioPortConfigType.aidl
@@ -13,25 +13,13 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
 package android.media;
 
-/**
- * Priority of a transcoding job.
- *
- * {@hide}
- */
 @Backing(type="int")
-enum TranscodingJobPriority {
-    // TODO(hkuang): define what each priority level actually mean.
-    kUnspecified = 0,
-    kLow = 1,
-    /**
-     * 2 ~ 20 is reserved for future use.
-     */
-    kNormal = 21,
-    /**
-     * 22 ~ 30 is reserved for future use.
-     */
-    kHigh = 31,
-}
\ No newline at end of file
+enum AudioPortConfigType {
+    SAMPLE_RATE  = 0,
+    CHANNEL_MASK = 1,
+    FORMAT       = 2,
+    GAIN         = 3,
+    FLAGS        = 4,
+}
diff --git a/media/libmediatranscoding/aidl/android/media/TranscodingJobPriority.aidl b/media/libaudioclient/aidl/android/media/AudioPortRole.aidl
similarity index 65%
copy from media/libmediatranscoding/aidl/android/media/TranscodingJobPriority.aidl
copy to media/libaudioclient/aidl/android/media/AudioPortRole.aidl
index 1a5d81a..3212325 100644
--- a/media/libmediatranscoding/aidl/android/media/TranscodingJobPriority.aidl
+++ b/media/libaudioclient/aidl/android/media/AudioPortRole.aidl
@@ -13,25 +13,11 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
 package android.media;
 
-/**
- * Priority of a transcoding job.
- *
- * {@hide}
- */
 @Backing(type="int")
-enum TranscodingJobPriority {
-    // TODO(hkuang): define what each priority level actually mean.
-    kUnspecified = 0,
-    kLow = 1,
-    /**
-     * 2 ~ 20 is reserved for future use.
-     */
-    kNormal = 21,
-    /**
-     * 22 ~ 30 is reserved for future use.
-     */
-    kHigh = 31,
-}
\ No newline at end of file
+enum AudioPortRole {
+    NONE = 0,
+    SOURCE = 1,
+    SINK = 2,
+}
diff --git a/media/libmediatranscoding/aidl/android/media/TranscodingJobPriority.aidl b/media/libaudioclient/aidl/android/media/AudioPortType.aidl
similarity index 65%
copy from media/libmediatranscoding/aidl/android/media/TranscodingJobPriority.aidl
copy to media/libaudioclient/aidl/android/media/AudioPortType.aidl
index 1a5d81a..90eea9a 100644
--- a/media/libmediatranscoding/aidl/android/media/TranscodingJobPriority.aidl
+++ b/media/libaudioclient/aidl/android/media/AudioPortType.aidl
@@ -13,25 +13,12 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
 package android.media;
 
-/**
- * Priority of a transcoding job.
- *
- * {@hide}
- */
 @Backing(type="int")
-enum TranscodingJobPriority {
-    // TODO(hkuang): define what each priority level actually mean.
-    kUnspecified = 0,
-    kLow = 1,
-    /**
-     * 2 ~ 20 is reserved for future use.
-     */
-    kNormal = 21,
-    /**
-     * 22 ~ 30 is reserved for future use.
-     */
-    kHigh = 31,
-}
\ No newline at end of file
+enum AudioPortType {
+    NONE = 0,
+    DEVICE = 1,
+    MIX = 2,
+    SESSION = 3,
+}
diff --git a/media/libmediatranscoding/aidl/android/media/TranscodingJobPriority.aidl b/media/libaudioclient/aidl/android/media/AudioSessionType.aidl
similarity index 65%
copy from media/libmediatranscoding/aidl/android/media/TranscodingJobPriority.aidl
copy to media/libaudioclient/aidl/android/media/AudioSessionType.aidl
index 1a5d81a..d305c29 100644
--- a/media/libmediatranscoding/aidl/android/media/TranscodingJobPriority.aidl
+++ b/media/libaudioclient/aidl/android/media/AudioSessionType.aidl
@@ -13,25 +13,13 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
 package android.media;
 
-/**
- * Priority of a transcoding job.
- *
- * {@hide}
- */
 @Backing(type="int")
-enum TranscodingJobPriority {
-    // TODO(hkuang): define what each priority level actually mean.
-    kUnspecified = 0,
-    kLow = 1,
-    /**
-     * 2 ~ 20 is reserved for future use.
-     */
-    kNormal = 21,
-    /**
-     * 22 ~ 30 is reserved for future use.
-     */
-    kHigh = 31,
-}
\ No newline at end of file
+enum AudioSessionType {
+    DEVICE = -2,
+    OUTPUT_STAGE = -1,
+    OUTPUT_MIX = 0,
+    ALLOCATE = 0,
+    NONE = 0,
+}
diff --git a/media/libaudioclient/aidl/android/media/AudioSourceType.aidl b/media/libaudioclient/aidl/android/media/AudioSourceType.aidl
new file mode 100644
index 0000000..f6ecc46
--- /dev/null
+++ b/media/libaudioclient/aidl/android/media/AudioSourceType.aidl
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.media;
+
+@Backing(type="int")
+enum AudioSourceType {
+    DEFAULT = 0,
+    MIC = 1,
+    VOICE_UPLINK = 2,
+    VOICE_DOWNLINK = 3,
+    VOICE_CALL = 4,
+    CAMCORDER = 5,
+    VOICE_RECOGNITION = 6,
+    VOICE_COMMUNICATION = 7,
+    REMOTE_SUBMIX = 8,
+    UNPROCESSED = 9,
+    VOICE_PERFORMANCE = 10,
+    ECHO_REFERENCE = 1997,
+    FM_TUNER = 1998,
+    /**
+     * A low-priority, preemptible audio source for for background software
+     * hotword detection. Same tuning as VOICE_RECOGNITION.
+     * Used only internally by the framework.
+     */
+    HOTWORD = 1999,
+}
diff --git a/media/libaudioclient/aidl/android/media/AudioStreamType.aidl b/media/libaudioclient/aidl/android/media/AudioStreamType.aidl
new file mode 100644
index 0000000..803b87b
--- /dev/null
+++ b/media/libaudioclient/aidl/android/media/AudioStreamType.aidl
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.media;
+
+@Backing(type="int")
+enum AudioStreamType {
+    DEFAULT = -1,
+    VOICE_CALL = 0,
+    SYSTEM = 1,
+    RING = 2,
+    MUSIC = 3,
+    ALARM = 4,
+    NOTIFICATION = 5,
+    BLUETOOTH_SCO = 6,
+    ENFORCED_AUDIBLE = 7,
+    DTMF = 8,
+    TTS = 9,
+    ACCESSIBILITY = 10,
+    ASSISTANT = 11,
+    /** For dynamic policy output mixes. Only used by the audio policy */
+    REROUTING = 12,
+    /** For audio flinger tracks volume. Only used by the audioflinger */
+    PATCH = 13,
+    /** stream for corresponding to AUDIO_USAGE_CALL_ASSISTANT */
+    CALL_ASSISTANT = 14,
+}
diff --git a/media/libmediatranscoding/aidl/android/media/TranscodingJobPriority.aidl b/media/libaudioclient/aidl/android/media/IAudioFlingerClient.aidl
similarity index 65%
copy from media/libmediatranscoding/aidl/android/media/TranscodingJobPriority.aidl
copy to media/libaudioclient/aidl/android/media/IAudioFlingerClient.aidl
index 1a5d81a..421c31c 100644
--- a/media/libmediatranscoding/aidl/android/media/TranscodingJobPriority.aidl
+++ b/media/libaudioclient/aidl/android/media/IAudioFlingerClient.aidl
@@ -16,22 +16,15 @@
 
 package android.media;
 
+import android.media.AudioIoConfigEvent;
+import android.media.AudioIoDescriptor;
+
 /**
- * Priority of a transcoding job.
+ * A callback interface for AudioFlinger.
  *
  * {@hide}
  */
-@Backing(type="int")
-enum TranscodingJobPriority {
-    // TODO(hkuang): define what each priority level actually mean.
-    kUnspecified = 0,
-    kLow = 1,
-    /**
-     * 2 ~ 20 is reserved for future use.
-     */
-    kNormal = 21,
-    /**
-     * 22 ~ 30 is reserved for future use.
-     */
-    kHigh = 31,
-}
\ No newline at end of file
+interface IAudioFlingerClient {
+    oneway void ioConfigChanged(AudioIoConfigEvent event,
+                                in AudioIoDescriptor ioDesc);
+}
diff --git a/media/libaudioclient/include/media/AidlConversion.h b/media/libaudioclient/include/media/AidlConversion.h
new file mode 100644
index 0000000..a1b9b82
--- /dev/null
+++ b/media/libaudioclient/include/media/AidlConversion.h
@@ -0,0 +1,147 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <system/audio.h>
+
+#include <android-base/result.h>
+#include <android/media/AudioGainMode.h>
+#include <android/media/AudioInputFlags.h>
+#include <android/media/AudioIoConfigEvent.h>
+#include <android/media/AudioIoDescriptor.h>
+#include <android/media/AudioOutputFlags.h>
+#include <android/media/AudioPortConfigType.h>
+
+#include <media/AudioIoDescriptor.h>
+
+namespace android {
+
+template <typename T>
+using ConversionResult = base::expected<T, status_t>;
+
+// The legacy enum is unnamed. Thus, we use int.
+ConversionResult<int> aidl2legacy_AudioPortConfigType(media::AudioPortConfigType aidl);
+// The legacy enum is unnamed. Thus, we use int.
+ConversionResult<media::AudioPortConfigType> legacy2aidl_AudioPortConfigType(int legacy);
+
+ConversionResult<unsigned int> aidl2legacy_int32_t_config_mask(int32_t aidl);
+ConversionResult<int32_t> legacy2aidl_config_mask_int32_t(unsigned int legacy);
+
+ConversionResult<audio_channel_mask_t> aidl2legacy_int32_t_audio_channel_mask_t(int32_t aidl);
+ConversionResult<int32_t> legacy2aidl_audio_channel_mask_t_int32_t(audio_channel_mask_t legacy);
+
+ConversionResult<audio_io_config_event> aidl2legacy_AudioIoConfigEvent_audio_io_config_event(
+        media::AudioIoConfigEvent aidl);
+ConversionResult<media::AudioIoConfigEvent> legacy2aidl_audio_io_config_event_AudioIoConfigEvent(
+        audio_io_config_event legacy);
+
+ConversionResult<audio_port_role_t> aidl2legacy_AudioPortRole_audio_port_role_t(
+        media::AudioPortRole aidl);
+ConversionResult<media::AudioPortRole> legacy2aidl_audio_port_role_t_AudioPortRole(
+        audio_port_role_t legacy);
+
+ConversionResult<audio_port_type_t> aidl2legacy_AudioPortType_audio_port_type_t(
+        media::AudioPortType aidl);
+ConversionResult<media::AudioPortType> legacy2aidl_audio_port_type_t_AudioPortType(
+        audio_port_type_t legacy);
+
+ConversionResult<audio_format_t> aidl2legacy_AudioFormat_audio_format_t(
+        media::audio::common::AudioFormat aidl);
+ConversionResult<media::audio::common::AudioFormat> legacy2aidl_audio_format_t_AudioFormat(
+        audio_format_t legacy);
+
+ConversionResult<int> aidl2legacy_AudioGainMode_int(media::AudioGainMode aidl);
+ConversionResult<media::AudioGainMode> legacy2aidl_int_AudioGainMode(int legacy);
+
+ConversionResult<audio_gain_mode_t> aidl2legacy_int32_t_audio_gain_mode_t(int32_t aidl);
+ConversionResult<int32_t> legacy2aidl_audio_gain_mode_t_int32_t(audio_gain_mode_t legacy);
+
+ConversionResult<audio_devices_t> aidl2legacy_int32_t_audio_devices_t(int32_t aidl);
+ConversionResult<int32_t> legacy2aidl_audio_devices_t_int32_t(audio_devices_t legacy);
+
+ConversionResult<audio_gain_config> aidl2legacy_AudioGainConfig_audio_gain_config(
+        const media::AudioGainConfig& aidl, media::AudioPortRole role, media::AudioPortType type);
+ConversionResult<media::AudioGainConfig> legacy2aidl_audio_gain_config_AudioGainConfig(
+        const audio_gain_config& legacy, audio_port_role_t role, audio_port_type_t type);
+
+ConversionResult<audio_input_flags_t> aidl2legacy_AudioInputFlags_audio_input_flags_t(
+        media::AudioInputFlags aidl);
+ConversionResult<media::AudioInputFlags> legacy2aidl_audio_input_flags_t_AudioInputFlags(
+        audio_input_flags_t legacy);
+
+ConversionResult<audio_output_flags_t> aidl2legacy_AudioOutputFlags_audio_output_flags_t(
+        media::AudioOutputFlags aidl);
+ConversionResult<media::AudioOutputFlags> legacy2aidl_audio_output_flags_t_AudioOutputFlags(
+        audio_output_flags_t legacy);
+
+ConversionResult<audio_input_flags_t> aidl2legacy_audio_input_flags_mask(int32_t aidl);
+ConversionResult<int32_t> legacy2aidl_audio_input_flags_mask(audio_input_flags_t legacy);
+
+ConversionResult<audio_output_flags_t> aidl2legacy_audio_output_flags_mask(int32_t aidl);
+ConversionResult<int32_t> legacy2aidl_audio_output_flags_mask(audio_output_flags_t legacy);
+
+ConversionResult<audio_io_flags> aidl2legacy_AudioIoFlags_audio_io_flags(
+        const media::AudioIoFlags& aidl, media::AudioPortRole role, media::AudioPortType type);
+ConversionResult<media::AudioIoFlags> legacy2aidl_audio_io_flags_AudioIoFlags(
+        const audio_io_flags& legacy, audio_port_role_t role, audio_port_type_t type);
+
+ConversionResult<audio_port_config_device_ext> aidl2legacy_AudioPortConfigDeviceExt(
+        const media::AudioPortConfigDeviceExt& aidl);
+ConversionResult<media::AudioPortConfigDeviceExt> legacy2aidl_AudioPortConfigDeviceExt(
+        const audio_port_config_device_ext& legacy);
+
+ConversionResult<audio_stream_type_t> aidl2legacy_AudioStreamType_audio_stream_type_t(
+        media::AudioStreamType aidl);
+ConversionResult<media::AudioStreamType> legacy2aidl_audio_stream_type_t_AudioStreamType(
+        audio_stream_type_t legacy);
+
+ConversionResult<audio_source_t> aidl2legacy_AudioSourceType_audio_source_t(
+        media::AudioSourceType aidl);
+ConversionResult<media::AudioSourceType> legacy2aidl_audio_source_t_AudioSourceType(
+        audio_source_t legacy);
+
+ConversionResult<audio_session_t> aidl2legacy_AudioSessionType_audio_session_t(
+        media::AudioSessionType aidl);
+ConversionResult<media::AudioSessionType> legacy2aidl_audio_session_t_AudioSessionType(
+        audio_session_t legacy);
+
+ConversionResult<audio_port_config_mix_ext> aidl2legacy_AudioPortConfigMixExt(
+        const media::AudioPortConfigMixExt& aidl, media::AudioPortRole role);
+ConversionResult<media::AudioPortConfigMixExt> legacy2aidl_AudioPortConfigMixExt(
+        const audio_port_config_mix_ext& legacy, audio_port_role_t role);
+
+ConversionResult<audio_port_config_session_ext> aidl2legacy_AudioPortConfigSessionExt(
+        const media::AudioPortConfigSessionExt& aidl);
+ConversionResult<media::AudioPortConfigSessionExt> legacy2aidl_AudioPortConfigSessionExt(
+        const audio_port_config_session_ext& legacy);
+
+ConversionResult<audio_port_config> aidl2legacy_AudioPortConfig_audio_port_config(
+        const media::AudioPortConfig& aidl);
+ConversionResult<media::AudioPortConfig> legacy2aidl_audio_port_config_AudioPortConfig(
+        const audio_port_config& legacy);
+
+ConversionResult<struct audio_patch> aidl2legacy_AudioPatch_audio_patch(
+        const media::AudioPatch& aidl);
+ConversionResult<media::AudioPatch> legacy2aidl_audio_patch_AudioPatch(
+        const struct audio_patch& legacy);
+
+ConversionResult<sp<AudioIoDescriptor>> aidl2legacy_AudioIoDescriptor_AudioIoDescriptor(
+        const media::AudioIoDescriptor& aidl);
+ConversionResult<media::AudioIoDescriptor> legacy2aidl_AudioIoDescriptor_AudioIoDescriptor(
+        const sp<AudioIoDescriptor>& legacy);
+
+}  // namespace android
diff --git a/media/libaudioclient/include/media/AudioSystem.h b/media/libaudioclient/include/media/AudioSystem.h
index 848743a..dfc1982 100644
--- a/media/libaudioclient/include/media/AudioSystem.h
+++ b/media/libaudioclient/include/media/AudioSystem.h
@@ -19,12 +19,12 @@
 
 #include <sys/types.h>
 
+#include <android/media/BnAudioFlingerClient.h>
 #include <media/AudioDeviceTypeAddr.h>
 #include <media/AudioPolicy.h>
 #include <media/AudioProductStrategy.h>
 #include <media/AudioVolumeGroup.h>
 #include <media/AudioIoDescriptor.h>
-#include <media/IAudioFlingerClient.h>
 #include <media/IAudioPolicyServiceClient.h>
 #include <media/MicrophoneInfo.h>
 #include <set>
@@ -531,7 +531,7 @@
 
 private:
 
-    class AudioFlingerClient: public IBinder::DeathRecipient, public BnAudioFlingerClient
+    class AudioFlingerClient: public IBinder::DeathRecipient, public media::BnAudioFlingerClient
     {
     public:
         AudioFlingerClient() :
@@ -551,9 +551,9 @@
 
         // indicate a change in the configuration of an output or input: keeps the cached
         // values for output/input parameters up-to-date in client process
-        virtual void ioConfigChanged(audio_io_config_event event,
-                                     const sp<AudioIoDescriptor>& ioDesc);
-
+        binder::Status ioConfigChanged(
+                media::AudioIoConfigEvent event,
+                const media::AudioIoDescriptor& ioDesc) override;
 
         status_t addAudioDeviceCallback(const wp<AudioDeviceCallback>& callback,
                                                audio_io_handle_t audioIo,
diff --git a/media/libaudioclient/include/media/IAudioFlinger.h b/media/libaudioclient/include/media/IAudioFlinger.h
index a01b681..413db71 100644
--- a/media/libaudioclient/include/media/IAudioFlinger.h
+++ b/media/libaudioclient/include/media/IAudioFlinger.h
@@ -29,7 +29,6 @@
 #include <media/AudioClient.h>
 #include <media/DeviceDescriptorBase.h>
 #include <media/IAudioTrack.h>
-#include <media/IAudioFlingerClient.h>
 #include <system/audio.h>
 #include <system/audio_effect.h>
 #include <system/audio_policy.h>
@@ -39,6 +38,7 @@
 #include <vector>
 
 #include "android/media/IAudioRecord.h"
+#include "android/media/IAudioFlingerClient.h"
 #include "android/media/IAudioTrackCallback.h"
 #include "android/media/IEffect.h"
 #include "android/media/IEffectClient.h"
@@ -420,7 +420,7 @@
     // Register an object to receive audio input/output change and track notifications.
     // For a given calling pid, AudioFlinger disregards any registrations after the first.
     // Thus the IAudioFlingerClient must be a singleton per process.
-    virtual void registerClient(const sp<IAudioFlingerClient>& client) = 0;
+    virtual void registerClient(const sp<media::IAudioFlingerClient>& client) = 0;
 
     // retrieve the audio recording buffer size in bytes
     // FIXME This API assumes a route, and so should be deprecated.
diff --git a/media/libaudioclient/include/media/IAudioFlingerClient.h b/media/libaudioclient/include/media/IAudioFlingerClient.h
deleted file mode 100644
index 0080bc9..0000000
--- a/media/libaudioclient/include/media/IAudioFlingerClient.h
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright (C) 2009 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_IAUDIOFLINGERCLIENT_H
-#define ANDROID_IAUDIOFLINGERCLIENT_H
-
-
-#include <utils/RefBase.h>
-#include <binder/IInterface.h>
-#include <utils/KeyedVector.h>
-#include <system/audio.h>
-#include <media/AudioIoDescriptor.h>
-
-namespace android {
-
-// ----------------------------------------------------------------------------
-
-class IAudioFlingerClient : public IInterface
-{
-public:
-    DECLARE_META_INTERFACE(AudioFlingerClient);
-
-    // Notifies a change of audio input/output configuration.
-    virtual void ioConfigChanged(audio_io_config_event event,
-                                 const sp<AudioIoDescriptor>& ioDesc) = 0;
-
-};
-
-
-// ----------------------------------------------------------------------------
-
-class BnAudioFlingerClient : public BnInterface<IAudioFlingerClient>
-{
-public:
-    virtual status_t    onTransact( uint32_t code,
-                                    const Parcel& data,
-                                    Parcel* reply,
-                                    uint32_t flags = 0);
-};
-
-// ----------------------------------------------------------------------------
-
-}; // namespace android
-
-#endif // ANDROID_IAUDIOFLINGERCLIENT_H
diff --git a/media/libaudioprocessing/include/media/AudioResamplerPublic.h b/media/libaudioprocessing/include/media/AudioResamplerPublic.h
index 1b39067..200a4c8 100644
--- a/media/libaudioprocessing/include/media/AudioResamplerPublic.h
+++ b/media/libaudioprocessing/include/media/AudioResamplerPublic.h
@@ -59,7 +59,7 @@
 
 static inline bool isAudioPlaybackRateValid(const AudioPlaybackRate &playbackRate) {
     if (playbackRate.mFallbackMode == AUDIO_TIMESTRETCH_FALLBACK_FAIL &&
-            (playbackRate.mStretchMode == AUDIO_TIMESTRETCH_STRETCH_SPEECH ||
+            (playbackRate.mStretchMode == AUDIO_TIMESTRETCH_STRETCH_VOICE ||
                     playbackRate.mStretchMode == AUDIO_TIMESTRETCH_STRETCH_DEFAULT)) {
         //test sonic specific constraints
         return playbackRate.mSpeed >= TIMESTRETCH_SONIC_SPEED_MIN &&
diff --git a/media/libeffects/config/src/EffectsConfig.cpp b/media/libeffects/config/src/EffectsConfig.cpp
index 26eaaf8..1696233 100644
--- a/media/libeffects/config/src/EffectsConfig.cpp
+++ b/media/libeffects/config/src/EffectsConfig.cpp
@@ -138,7 +138,7 @@
 
 template <>
 bool stringToStreamType(const char *streamName, audio_devices_t* type) {
-    return deviceFromString(streamName, *type);
+    return DeviceConverter::fromString(streamName, *type);
 }
 
 /** Parse a library xml note and push the result in libraries or return false on failure. */
diff --git a/media/libeffects/lvm/wrapper/Android.bp b/media/libeffects/lvm/wrapper/Android.bp
index f08caec..be60aae 100644
--- a/media/libeffects/lvm/wrapper/Android.bp
+++ b/media/libeffects/lvm/wrapper/Android.bp
@@ -1,5 +1,5 @@
 // music bundle wrapper
-cc_library_shared {
+cc_library {
     name: "libbundlewrapper",
 
     arch: {
diff --git a/media/libmedia/IMediaPlayer.cpp b/media/libmedia/IMediaPlayer.cpp
index c08d187..8a4b17c 100644
--- a/media/libmedia/IMediaPlayer.cpp
+++ b/media/libmedia/IMediaPlayer.cpp
@@ -974,7 +974,7 @@
         case PREPARE_DRM: {
             CHECK_INTERFACE(IMediaPlayer, data, reply);
 
-            uint8_t uuid[16];
+            uint8_t uuid[16] = {};
             data.read(uuid, sizeof(uuid));
             Vector<uint8_t> drmSessionId;
             readVector(data, drmSessionId);
diff --git a/media/libmedia/TEST_MAPPING b/media/libmedia/TEST_MAPPING
new file mode 100644
index 0000000..65390ed
--- /dev/null
+++ b/media/libmedia/TEST_MAPPING
@@ -0,0 +1,6 @@
+// test_mapping for frameworks/av/media/libmedia
+{
+  "presubmit": [
+    { "name": "CodecListTest" }
+  ]
+}
diff --git a/media/libmedia/tests/codeclist/Android.bp b/media/libmedia/tests/codeclist/Android.bp
new file mode 100644
index 0000000..a930d6e
--- /dev/null
+++ b/media/libmedia/tests/codeclist/Android.bp
@@ -0,0 +1,48 @@
+/*
+ * 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.
+ */
+
+cc_test {
+    name: "CodecListTest",
+    test_suites: ["device-tests"],
+    gtest: true,
+
+    srcs: [
+        "CodecListTest.cpp",
+    ],
+
+    shared_libs: [
+        "libbinder",
+        "liblog",
+        "libmedia_codeclist",
+        "libstagefright",
+        "libstagefright_foundation",
+        "libstagefright_xmlparser",
+        "libutils",
+    ],
+
+    cflags: [
+        "-Werror",
+        "-Wall",
+    ],
+
+    sanitize: {
+        cfi: true,
+        misc_undefined: [
+            "unsigned-integer-overflow",
+            "signed-integer-overflow",
+        ],
+    },
+}
diff --git a/media/libmedia/tests/codeclist/CodecListTest.cpp b/media/libmedia/tests/codeclist/CodecListTest.cpp
new file mode 100644
index 0000000..bd2adf7
--- /dev/null
+++ b/media/libmedia/tests/codeclist/CodecListTest.cpp
@@ -0,0 +1,222 @@
+/*
+ * 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 "CodecListTest"
+#include <utils/Log.h>
+
+#include <gtest/gtest.h>
+
+#include <binder/Parcel.h>
+#include <media/stagefright/MediaCodecConstants.h>
+#include <media/stagefright/MediaCodecList.h>
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/AString.h>
+#include <media/stagefright/xmlparser/MediaCodecsXmlParser.h>
+
+#define kSwCodecXmlPath "/apex/com.android.media.swcodec/etc/"
+
+using namespace android;
+
+struct CddReq {
+    CddReq(const char *type, bool encoder) {
+        mediaType = type;
+        isEncoder = encoder;
+    }
+
+    const char *mediaType;
+    bool isEncoder;
+};
+
+TEST(CodecListTest, CodecListSanityTest) {
+    sp<IMediaCodecList> list = MediaCodecList::getInstance();
+    ASSERT_NE(list, nullptr) << "Unable to get MediaCodecList instance.";
+    EXPECT_GT(list->countCodecs(), 0) << "No codecs in CodecList";
+    for (size_t i = 0; i < list->countCodecs(); ++i) {
+        sp<MediaCodecInfo> info = list->getCodecInfo(i);
+        ASSERT_NE(info, nullptr) << "CodecInfo is null";
+        ssize_t index = list->findCodecByName(info->getCodecName());
+        EXPECT_GE(index, 0) << "Wasn't able to find existing codec: " << info->getCodecName();
+    }
+}
+
+TEST(CodecListTest, CodecListByTypeTest) {
+    sp<IMediaCodecList> list = MediaCodecList::getInstance();
+    ASSERT_NE(list, nullptr) << "Unable to get MediaCodecList instance.";
+
+    std::vector<CddReq> cddReq{
+            // media type, isEncoder
+            CddReq(MIMETYPE_AUDIO_AAC, false),
+            CddReq(MIMETYPE_AUDIO_AAC, true),
+
+            CddReq(MIMETYPE_VIDEO_AVC, false),
+            CddReq(MIMETYPE_VIDEO_HEVC, false),
+            CddReq(MIMETYPE_VIDEO_MPEG4, false),
+            CddReq(MIMETYPE_VIDEO_VP8, false),
+            CddReq(MIMETYPE_VIDEO_VP9, false),
+
+            CddReq(MIMETYPE_VIDEO_AVC, true),
+            CddReq(MIMETYPE_VIDEO_VP8, true),
+    };
+
+    for (CddReq codecReq : cddReq) {
+        ssize_t index = list->findCodecByType(codecReq.mediaType, codecReq.isEncoder);
+        EXPECT_GE(index, 0) << "Wasn't able to find codec for media type: " << codecReq.mediaType
+                            << (codecReq.isEncoder ? " encoder" : " decoder");
+    }
+}
+
+TEST(CodecInfoTest, ListInfoTest) {
+    ALOGV("Compare CodecInfo with info in XML");
+    MediaCodecsXmlParser parser;
+    status_t status = parser.parseXmlFilesInSearchDirs();
+    ASSERT_EQ(status, OK) << "XML Parsing failed for default paths";
+
+    const std::vector<std::string> &xmlFiles = MediaCodecsXmlParser::getDefaultXmlNames();
+    const std::vector<std::string> &searchDirsApex{std::string(kSwCodecXmlPath)};
+    status = parser.parseXmlFilesInSearchDirs(xmlFiles, searchDirsApex);
+    ASSERT_EQ(status, OK) << "XML Parsing of " << kSwCodecXmlPath << " failed";
+
+    MediaCodecsXmlParser::CodecMap codecMap = parser.getCodecMap();
+
+    sp<IMediaCodecList> list = MediaCodecList::getInstance();
+    ASSERT_NE(list, nullptr) << "Unable to get MediaCodecList instance";
+
+    // Compare CodecMap from XML to CodecList
+    for (auto mapIter : codecMap) {
+        ssize_t index = list->findCodecByName(mapIter.first.c_str());
+        if (index < 0) {
+            std::cout << "[   WARN   ] " << mapIter.first << " not found in CodecList \n";
+            continue;
+        }
+
+        sp<MediaCodecInfo> info = list->getCodecInfo(index);
+        ASSERT_NE(info, nullptr) << "CodecInfo is null";
+
+        MediaCodecsXmlParser::CodecProperties codecProperties = mapIter.second;
+        ASSERT_EQ(codecProperties.isEncoder, info->isEncoder()) << "Encoder property mismatch";
+
+        ALOGV("codec name: %s", info->getCodecName());
+        ALOGV("codec rank: %d", info->getRank());
+        ALOGV("codec ownername: %s", info->getOwnerName());
+        ALOGV("codec isEncoder: %d", info->isEncoder());
+
+        ALOGV("attributeFlags: kFlagIsHardwareAccelerated, kFlagIsSoftwareOnly, kFlagIsVendor, "
+              "kFlagIsEncoder");
+        std::bitset<4> attr(info->getAttributes());
+        ALOGV("codec attributes: %s", attr.to_string().c_str());
+
+        Vector<AString> mediaTypes;
+        info->getSupportedMediaTypes(&mediaTypes);
+        ALOGV("supported media types count: %zu", mediaTypes.size());
+        ASSERT_FALSE(mediaTypes.isEmpty())
+                << "no media type supported by codec: " << info->getCodecName();
+
+        MediaCodecsXmlParser::TypeMap typeMap = codecProperties.typeMap;
+        for (auto mediaType : mediaTypes) {
+            ALOGV("codec mediaTypes: %s", mediaType.c_str());
+            auto searchTypeMap = typeMap.find(mediaType.c_str());
+            ASSERT_NE(searchTypeMap, typeMap.end())
+                    << "CodecList doesn't contain codec media type: " << mediaType.c_str();
+            MediaCodecsXmlParser::AttributeMap attributeMap = searchTypeMap->second;
+
+            const sp<MediaCodecInfo::Capabilities> &capabilities =
+                    info->getCapabilitiesFor(mediaType.c_str());
+
+            Vector<uint32_t> colorFormats;
+            capabilities->getSupportedColorFormats(&colorFormats);
+            for (auto colorFormat : colorFormats) {
+                ALOGV("supported color formats: %d", colorFormat);
+            }
+
+            Vector<MediaCodecInfo::ProfileLevel> profileLevels;
+            capabilities->getSupportedProfileLevels(&profileLevels);
+            if (!profileLevels.empty()) {
+                ALOGV("supported profilelevel for media type: %s", mediaType.c_str());
+            }
+            for (auto profileLevel : profileLevels) {
+                ALOGV("profile: %d, level: %d", profileLevel.mProfile, profileLevel.mLevel);
+            }
+
+            sp<AMessage> details = capabilities->getDetails();
+            ASSERT_NE(details, nullptr) << "Details in codec capabilities is null";
+            ALOGV("no. of entries in details: %zu", details->countEntries());
+
+            for (size_t idxDetail = 0; idxDetail < details->countEntries(); idxDetail++) {
+                AMessage::Type type;
+                const char *name = details->getEntryNameAt(idxDetail, &type);
+                ALOGV("details entry name: %s", name);
+                AMessage::ItemData itemData = details->getEntryAt(idxDetail);
+                switch (type) {
+                    case AMessage::kTypeInt32:
+                        int32_t val32;
+                        if (itemData.find(&val32)) {
+                            ALOGV("entry int val: %d", val32);
+                            auto searchAttr = attributeMap.find(name);
+                            if (searchAttr == attributeMap.end()) {
+                                ALOGW("Parser doesn't have key: %s", name);
+                            } else if (stoi(searchAttr->second) != val32) {
+                                ALOGW("Values didn't match for key: %s", name);
+                                ALOGV("Values act/exp: %d / %d", val32, stoi(searchAttr->second));
+                            }
+                        }
+                        break;
+                    case AMessage::kTypeString:
+                        if (AString valStr; itemData.find(&valStr)) {
+                            ALOGV("entry str val: %s", valStr.c_str());
+                            auto searchAttr = attributeMap.find(name);
+                            if (searchAttr == attributeMap.end()) {
+                                ALOGW("Parser doesn't have key: %s", name);
+                            } else if (searchAttr->second != valStr.c_str()) {
+                                ALOGW("Values didn't match for key: %s", name);
+                                ALOGV("Values act/exp: %s / %s", valStr.c_str(),
+                                      searchAttr->second.c_str());
+                            }
+                        }
+                        break;
+                    default:
+                        ALOGV("data type: %d shouldn't be present in details", type);
+                        break;
+                }
+            }
+        }
+
+        Parcel *codecInfoParcel = new Parcel();
+        ASSERT_NE(codecInfoParcel, nullptr) << "Unable to create parcel";
+
+        status_t status = info->writeToParcel(codecInfoParcel);
+        ASSERT_EQ(status, OK) << "Writing to parcel failed";
+
+        codecInfoParcel->setDataPosition(0);
+        sp<MediaCodecInfo> parcelCodecInfo = info->FromParcel(*codecInfoParcel);
+        ASSERT_NE(parcelCodecInfo, nullptr) << "CodecInfo from parcel is null";
+        delete codecInfoParcel;
+
+        EXPECT_STREQ(info->getCodecName(), parcelCodecInfo->getCodecName())
+                << "Returned codec name in info doesn't match";
+        EXPECT_EQ(info->getRank(), parcelCodecInfo->getRank())
+                << "Returned component rank in info doesn't match";
+    }
+}
+
+TEST(CodecListTest, CodecListGlobalSettingsTest) {
+    sp<IMediaCodecList> list = MediaCodecList::getInstance();
+    ASSERT_NE(list, nullptr) << "Unable to get MediaCodecList instance";
+
+    sp<AMessage> globalSettings = list->getGlobalSettings();
+    ASSERT_NE(globalSettings, nullptr) << "GlobalSettings AMessage is null";
+    ALOGV("global settings: %s", globalSettings->debugString(0).c_str());
+}
diff --git a/media/libmediahelper/TEST_MAPPING b/media/libmediahelper/TEST_MAPPING
new file mode 100644
index 0000000..f9594bd
--- /dev/null
+++ b/media/libmediahelper/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+  "presubmit": [
+    {
+      "name": "libmedia_helper_tests"
+    }
+  ]
+}
diff --git a/media/libmediahelper/TypeConverter.cpp b/media/libmediahelper/TypeConverter.cpp
index 876dc45..d3a517f 100644
--- a/media/libmediahelper/TypeConverter.cpp
+++ b/media/libmediahelper/TypeConverter.cpp
@@ -18,315 +18,9 @@
 
 namespace android {
 
-#define MAKE_STRING_FROM_ENUM(string) { #string, string }
+#define MAKE_STRING_FROM_ENUM(enumval) { #enumval, enumval }
 #define TERMINATOR { .literal = nullptr }
 
-template <>
-const OutputDeviceConverter::Table OutputDeviceConverter::mTable[] = {
-    MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_NONE),
-    MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_OUT_EARPIECE),
-    MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_OUT_SPEAKER),
-    MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_OUT_SPEAKER_SAFE),
-    MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_OUT_WIRED_HEADSET),
-    MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_OUT_WIRED_HEADPHONE),
-    MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_OUT_BLUETOOTH_SCO),
-    MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET),
-    MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT),
-    // TODO(mnaganov): Remove from here, use 'audio_is_bluetooth_out_sco_device' function.
-    { "AUDIO_DEVICE_OUT_ALL_SCO", static_cast<audio_devices_t>(AUDIO_DEVICE_OUT_ALL_SCO) },
-    MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_OUT_BLUETOOTH_A2DP),
-    MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES),
-    MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER),
-    // TODO(mnaganov): Remove from here, use 'audio_is_a2dp_out_device' function.
-    { "AUDIO_DEVICE_OUT_ALL_A2DP", static_cast<audio_devices_t>(AUDIO_DEVICE_OUT_ALL_A2DP) },
-    MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_OUT_AUX_DIGITAL),
-    MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_OUT_HDMI),
-    MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET),
-    MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET),
-    MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_OUT_USB_ACCESSORY),
-    MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_OUT_USB_DEVICE),
-    // TODO(mnaganov): Remove from here, use 'audio_is_usb_out_device' function.
-    { "AUDIO_DEVICE_OUT_ALL_USB", static_cast<audio_devices_t>(AUDIO_DEVICE_OUT_ALL_USB) },
-    MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_OUT_REMOTE_SUBMIX),
-    MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_OUT_TELEPHONY_TX),
-    MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_OUT_LINE),
-    MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_OUT_HDMI_ARC),
-    MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_OUT_SPDIF),
-    MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_OUT_FM),
-    MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_OUT_AUX_LINE),
-    MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_OUT_IP),
-    MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_OUT_BUS),
-    MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_OUT_PROXY),
-    MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_OUT_USB_HEADSET),
-    MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_OUT_HEARING_AID),
-    MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_OUT_ECHO_CANCELLER),
-    MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_OUT_BLE_HEADSET),
-    MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_OUT_BLE_SPEAKER),
-    MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_OUT_DEFAULT),
-    // STUB must be after DEFAULT, so the latter is picked up by toString first.
-    MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_OUT_STUB),
-    TERMINATOR
-};
-
-template <>
-const InputDeviceConverter::Table InputDeviceConverter::mTable[] = {
-    MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_NONE),
-    MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_IN_COMMUNICATION),
-    MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_IN_AMBIENT),
-    MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_IN_BUILTIN_MIC),
-    MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET),
-    // TODO(mnaganov): Remove from here, use 'audio_is_bluetooth_in_sco_device' function.
-    { "AUDIO_DEVICE_IN_ALL_SCO", static_cast<audio_devices_t>(AUDIO_DEVICE_IN_ALL_SCO) },
-    MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_IN_WIRED_HEADSET),
-    MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_IN_AUX_DIGITAL),
-    MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_IN_HDMI),
-    MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_IN_HDMI_ARC),
-    MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_IN_TELEPHONY_RX),
-    MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_IN_VOICE_CALL),
-    MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_IN_BACK_MIC),
-    MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_IN_REMOTE_SUBMIX),
-    MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_IN_ANLG_DOCK_HEADSET),
-    MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_IN_DGTL_DOCK_HEADSET),
-    MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_IN_USB_ACCESSORY),
-    MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_IN_USB_DEVICE),
-    // TODO(mnaganov): Remove from here, use 'audio_is_usb_in_device' function.
-    { "AUDIO_DEVICE_IN_ALL_USB", static_cast<audio_devices_t>(AUDIO_DEVICE_IN_ALL_USB) },
-    MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_IN_FM_TUNER),
-    MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_IN_TV_TUNER),
-    MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_IN_LINE),
-    MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_IN_SPDIF),
-    MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_IN_BLUETOOTH_A2DP),
-    MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_IN_LOOPBACK),
-    MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_IN_IP),
-    MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_IN_BUS),
-    MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_IN_PROXY),
-    MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_IN_USB_HEADSET),
-    MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_IN_BLUETOOTH_BLE),
-    MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_IN_ECHO_REFERENCE),
-    MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_IN_BLE_HEADSET),
-    MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_IN_DEFAULT),
-    // STUB must be after DEFAULT, so the latter is picked up by toString first.
-    MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_IN_STUB),
-    TERMINATOR
-};
-
-
-template <>
-const OutputFlagConverter::Table OutputFlagConverter::mTable[] = {
-    MAKE_STRING_FROM_ENUM(AUDIO_OUTPUT_FLAG_NONE),
-    MAKE_STRING_FROM_ENUM(AUDIO_OUTPUT_FLAG_DIRECT),
-    MAKE_STRING_FROM_ENUM(AUDIO_OUTPUT_FLAG_PRIMARY),
-    MAKE_STRING_FROM_ENUM(AUDIO_OUTPUT_FLAG_FAST),
-    MAKE_STRING_FROM_ENUM(AUDIO_OUTPUT_FLAG_DEEP_BUFFER),
-    MAKE_STRING_FROM_ENUM(AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD),
-    MAKE_STRING_FROM_ENUM(AUDIO_OUTPUT_FLAG_NON_BLOCKING),
-    MAKE_STRING_FROM_ENUM(AUDIO_OUTPUT_FLAG_HW_AV_SYNC),
-    MAKE_STRING_FROM_ENUM(AUDIO_OUTPUT_FLAG_TTS),
-    MAKE_STRING_FROM_ENUM(AUDIO_OUTPUT_FLAG_RAW),
-    MAKE_STRING_FROM_ENUM(AUDIO_OUTPUT_FLAG_SYNC),
-    MAKE_STRING_FROM_ENUM(AUDIO_OUTPUT_FLAG_IEC958_NONAUDIO),
-    MAKE_STRING_FROM_ENUM(AUDIO_OUTPUT_FLAG_DIRECT_PCM),
-    MAKE_STRING_FROM_ENUM(AUDIO_OUTPUT_FLAG_MMAP_NOIRQ),
-    MAKE_STRING_FROM_ENUM(AUDIO_OUTPUT_FLAG_VOIP_RX),
-    MAKE_STRING_FROM_ENUM(AUDIO_OUTPUT_FLAG_INCALL_MUSIC),
-    TERMINATOR
-};
-
-
-template <>
-const InputFlagConverter::Table InputFlagConverter::mTable[] = {
-    MAKE_STRING_FROM_ENUM(AUDIO_INPUT_FLAG_NONE),
-    MAKE_STRING_FROM_ENUM(AUDIO_INPUT_FLAG_FAST),
-    MAKE_STRING_FROM_ENUM(AUDIO_INPUT_FLAG_HW_HOTWORD),
-    MAKE_STRING_FROM_ENUM(AUDIO_INPUT_FLAG_RAW),
-    MAKE_STRING_FROM_ENUM(AUDIO_INPUT_FLAG_SYNC),
-    MAKE_STRING_FROM_ENUM(AUDIO_INPUT_FLAG_MMAP_NOIRQ),
-    MAKE_STRING_FROM_ENUM(AUDIO_INPUT_FLAG_VOIP_TX),
-    MAKE_STRING_FROM_ENUM(AUDIO_INPUT_FLAG_HW_AV_SYNC),
-    MAKE_STRING_FROM_ENUM(AUDIO_INPUT_FLAG_DIRECT),
-    TERMINATOR
-};
-
-
-template <>
-const FormatConverter::Table FormatConverter::mTable[] = {
-    MAKE_STRING_FROM_ENUM(AUDIO_FORMAT_PCM_16_BIT),
-    MAKE_STRING_FROM_ENUM(AUDIO_FORMAT_PCM_8_BIT),
-    MAKE_STRING_FROM_ENUM(AUDIO_FORMAT_PCM_32_BIT),
-    MAKE_STRING_FROM_ENUM(AUDIO_FORMAT_PCM_8_24_BIT),
-    MAKE_STRING_FROM_ENUM(AUDIO_FORMAT_PCM_FLOAT),
-    MAKE_STRING_FROM_ENUM(AUDIO_FORMAT_PCM_24_BIT_PACKED),
-    MAKE_STRING_FROM_ENUM(AUDIO_FORMAT_MP3),
-    MAKE_STRING_FROM_ENUM(AUDIO_FORMAT_AMR_NB),
-    MAKE_STRING_FROM_ENUM(AUDIO_FORMAT_AMR_WB),
-    MAKE_STRING_FROM_ENUM(AUDIO_FORMAT_AAC),
-    MAKE_STRING_FROM_ENUM(AUDIO_FORMAT_AAC_MAIN),
-    MAKE_STRING_FROM_ENUM(AUDIO_FORMAT_AAC_LC),
-    MAKE_STRING_FROM_ENUM(AUDIO_FORMAT_AAC_SSR),
-    MAKE_STRING_FROM_ENUM(AUDIO_FORMAT_AAC_LTP),
-    MAKE_STRING_FROM_ENUM(AUDIO_FORMAT_AAC_HE_V1),
-    MAKE_STRING_FROM_ENUM(AUDIO_FORMAT_AAC_SCALABLE),
-    MAKE_STRING_FROM_ENUM(AUDIO_FORMAT_AAC_ERLC),
-    MAKE_STRING_FROM_ENUM(AUDIO_FORMAT_AAC_LD),
-    MAKE_STRING_FROM_ENUM(AUDIO_FORMAT_AAC_HE_V2),
-    MAKE_STRING_FROM_ENUM(AUDIO_FORMAT_AAC_ELD),
-    MAKE_STRING_FROM_ENUM(AUDIO_FORMAT_AAC_XHE),
-    MAKE_STRING_FROM_ENUM(AUDIO_FORMAT_AAC_ADTS_MAIN),
-    MAKE_STRING_FROM_ENUM(AUDIO_FORMAT_AAC_ADTS_LC),
-    MAKE_STRING_FROM_ENUM(AUDIO_FORMAT_AAC_ADTS_SSR),
-    MAKE_STRING_FROM_ENUM(AUDIO_FORMAT_AAC_ADTS_LTP),
-    MAKE_STRING_FROM_ENUM(AUDIO_FORMAT_AAC_ADTS_HE_V1),
-    MAKE_STRING_FROM_ENUM(AUDIO_FORMAT_AAC_ADTS_SCALABLE),
-    MAKE_STRING_FROM_ENUM(AUDIO_FORMAT_AAC_ADTS_ERLC),
-    MAKE_STRING_FROM_ENUM(AUDIO_FORMAT_AAC_ADTS_LD),
-    MAKE_STRING_FROM_ENUM(AUDIO_FORMAT_AAC_ADTS_HE_V2),
-    MAKE_STRING_FROM_ENUM(AUDIO_FORMAT_AAC_ADTS_ELD),
-    MAKE_STRING_FROM_ENUM(AUDIO_FORMAT_AAC_ADTS_XHE),
-    MAKE_STRING_FROM_ENUM(AUDIO_FORMAT_VORBIS),
-    MAKE_STRING_FROM_ENUM(AUDIO_FORMAT_HE_AAC_V1),
-    MAKE_STRING_FROM_ENUM(AUDIO_FORMAT_HE_AAC_V2),
-    MAKE_STRING_FROM_ENUM(AUDIO_FORMAT_OPUS),
-    MAKE_STRING_FROM_ENUM(AUDIO_FORMAT_AC3),
-    MAKE_STRING_FROM_ENUM(AUDIO_FORMAT_E_AC3),
-    MAKE_STRING_FROM_ENUM(AUDIO_FORMAT_DTS),
-    MAKE_STRING_FROM_ENUM(AUDIO_FORMAT_DTS_HD),
-    MAKE_STRING_FROM_ENUM(AUDIO_FORMAT_IEC61937),
-    MAKE_STRING_FROM_ENUM(AUDIO_FORMAT_DOLBY_TRUEHD),
-    MAKE_STRING_FROM_ENUM(AUDIO_FORMAT_EVRC),
-    MAKE_STRING_FROM_ENUM(AUDIO_FORMAT_EVRCB),
-    MAKE_STRING_FROM_ENUM(AUDIO_FORMAT_EVRCWB),
-    MAKE_STRING_FROM_ENUM(AUDIO_FORMAT_EVRCNW),
-    MAKE_STRING_FROM_ENUM(AUDIO_FORMAT_AAC_ADIF),
-    MAKE_STRING_FROM_ENUM(AUDIO_FORMAT_WMA),
-    MAKE_STRING_FROM_ENUM(AUDIO_FORMAT_WMA_PRO),
-    MAKE_STRING_FROM_ENUM(AUDIO_FORMAT_AMR_WB_PLUS),
-    MAKE_STRING_FROM_ENUM(AUDIO_FORMAT_MP2),
-    MAKE_STRING_FROM_ENUM(AUDIO_FORMAT_QCELP),
-    MAKE_STRING_FROM_ENUM(AUDIO_FORMAT_DSD),
-    MAKE_STRING_FROM_ENUM(AUDIO_FORMAT_FLAC),
-    MAKE_STRING_FROM_ENUM(AUDIO_FORMAT_ALAC),
-    MAKE_STRING_FROM_ENUM(AUDIO_FORMAT_APE),
-    MAKE_STRING_FROM_ENUM(AUDIO_FORMAT_AAC_ADTS),
-    MAKE_STRING_FROM_ENUM(AUDIO_FORMAT_SBC),
-    MAKE_STRING_FROM_ENUM(AUDIO_FORMAT_APTX),
-    MAKE_STRING_FROM_ENUM(AUDIO_FORMAT_APTX_HD),
-    MAKE_STRING_FROM_ENUM(AUDIO_FORMAT_AC4),
-    MAKE_STRING_FROM_ENUM(AUDIO_FORMAT_LDAC),
-    MAKE_STRING_FROM_ENUM(AUDIO_FORMAT_MAT),
-    MAKE_STRING_FROM_ENUM(AUDIO_FORMAT_E_AC3_JOC),
-    MAKE_STRING_FROM_ENUM(AUDIO_FORMAT_MAT_1_0),
-    MAKE_STRING_FROM_ENUM(AUDIO_FORMAT_MAT_2_0),
-    MAKE_STRING_FROM_ENUM(AUDIO_FORMAT_MAT_2_1),
-    MAKE_STRING_FROM_ENUM(AUDIO_FORMAT_AAC_LATM),
-    MAKE_STRING_FROM_ENUM(AUDIO_FORMAT_AAC_LATM_LC),
-    MAKE_STRING_FROM_ENUM(AUDIO_FORMAT_AAC_LATM_HE_V1),
-    MAKE_STRING_FROM_ENUM(AUDIO_FORMAT_AAC_LATM_HE_V2),
-    MAKE_STRING_FROM_ENUM(AUDIO_FORMAT_CELT),
-    MAKE_STRING_FROM_ENUM(AUDIO_FORMAT_APTX_ADAPTIVE),
-    MAKE_STRING_FROM_ENUM(AUDIO_FORMAT_LHDC),
-    MAKE_STRING_FROM_ENUM(AUDIO_FORMAT_LHDC_LL),
-    MAKE_STRING_FROM_ENUM(AUDIO_FORMAT_APTX_TWSP),
-    TERMINATOR
-};
-
-
-template <>
-const OutputChannelConverter::Table OutputChannelConverter::mTable[] = {
-    MAKE_STRING_FROM_ENUM(AUDIO_CHANNEL_OUT_MONO),
-    MAKE_STRING_FROM_ENUM(AUDIO_CHANNEL_OUT_STEREO),
-    MAKE_STRING_FROM_ENUM(AUDIO_CHANNEL_OUT_2POINT1),
-    MAKE_STRING_FROM_ENUM(AUDIO_CHANNEL_OUT_2POINT0POINT2),
-    MAKE_STRING_FROM_ENUM(AUDIO_CHANNEL_OUT_2POINT1POINT2),
-    MAKE_STRING_FROM_ENUM(AUDIO_CHANNEL_OUT_TRI),
-    MAKE_STRING_FROM_ENUM(AUDIO_CHANNEL_OUT_TRI_BACK),
-    MAKE_STRING_FROM_ENUM(AUDIO_CHANNEL_OUT_3POINT1),
-    MAKE_STRING_FROM_ENUM(AUDIO_CHANNEL_OUT_3POINT0POINT2),
-    MAKE_STRING_FROM_ENUM(AUDIO_CHANNEL_OUT_3POINT1POINT2),
-    MAKE_STRING_FROM_ENUM(AUDIO_CHANNEL_OUT_QUAD),
-    MAKE_STRING_FROM_ENUM(AUDIO_CHANNEL_OUT_QUAD_BACK),
-    MAKE_STRING_FROM_ENUM(AUDIO_CHANNEL_OUT_QUAD_SIDE),
-    MAKE_STRING_FROM_ENUM(AUDIO_CHANNEL_OUT_SURROUND),
-    MAKE_STRING_FROM_ENUM(AUDIO_CHANNEL_OUT_PENTA),
-    MAKE_STRING_FROM_ENUM(AUDIO_CHANNEL_OUT_5POINT1),
-    MAKE_STRING_FROM_ENUM(AUDIO_CHANNEL_OUT_5POINT1_BACK),
-    MAKE_STRING_FROM_ENUM(AUDIO_CHANNEL_OUT_5POINT1_SIDE),
-    MAKE_STRING_FROM_ENUM(AUDIO_CHANNEL_OUT_5POINT1POINT2),
-    MAKE_STRING_FROM_ENUM(AUDIO_CHANNEL_OUT_5POINT1POINT4),
-    MAKE_STRING_FROM_ENUM(AUDIO_CHANNEL_OUT_6POINT1),
-    MAKE_STRING_FROM_ENUM(AUDIO_CHANNEL_OUT_7POINT1),
-    MAKE_STRING_FROM_ENUM(AUDIO_CHANNEL_OUT_7POINT1POINT2),
-    MAKE_STRING_FROM_ENUM(AUDIO_CHANNEL_OUT_7POINT1POINT4),
-    MAKE_STRING_FROM_ENUM(AUDIO_CHANNEL_OUT_HAPTIC_A),
-    MAKE_STRING_FROM_ENUM(AUDIO_CHANNEL_OUT_MONO_HAPTIC_A),
-    MAKE_STRING_FROM_ENUM(AUDIO_CHANNEL_OUT_STEREO_HAPTIC_A),
-    MAKE_STRING_FROM_ENUM(AUDIO_CHANNEL_OUT_HAPTIC_AB),
-    MAKE_STRING_FROM_ENUM(AUDIO_CHANNEL_OUT_MONO_HAPTIC_AB),
-    MAKE_STRING_FROM_ENUM(AUDIO_CHANNEL_OUT_STEREO_HAPTIC_AB),
-    TERMINATOR
-};
-
-
-template <>
-const InputChannelConverter::Table InputChannelConverter::mTable[] = {
-    MAKE_STRING_FROM_ENUM(AUDIO_CHANNEL_IN_MONO),
-    MAKE_STRING_FROM_ENUM(AUDIO_CHANNEL_IN_STEREO),
-    MAKE_STRING_FROM_ENUM(AUDIO_CHANNEL_IN_FRONT_BACK),
-    MAKE_STRING_FROM_ENUM(AUDIO_CHANNEL_IN_6),
-    MAKE_STRING_FROM_ENUM(AUDIO_CHANNEL_IN_2POINT0POINT2),
-    MAKE_STRING_FROM_ENUM(AUDIO_CHANNEL_IN_2POINT1POINT2),
-    MAKE_STRING_FROM_ENUM(AUDIO_CHANNEL_IN_3POINT0POINT2),
-    MAKE_STRING_FROM_ENUM(AUDIO_CHANNEL_IN_3POINT1POINT2),
-    MAKE_STRING_FROM_ENUM(AUDIO_CHANNEL_IN_5POINT1),
-    MAKE_STRING_FROM_ENUM(AUDIO_CHANNEL_IN_VOICE_UPLINK_MONO),
-    MAKE_STRING_FROM_ENUM(AUDIO_CHANNEL_IN_VOICE_DNLINK_MONO),
-    MAKE_STRING_FROM_ENUM(AUDIO_CHANNEL_IN_VOICE_CALL_MONO),
-    TERMINATOR
-};
-
-template <>
-const ChannelIndexConverter::Table ChannelIndexConverter::mTable[] = {
-    {"AUDIO_CHANNEL_INDEX_MASK_1", static_cast<audio_channel_mask_t>(AUDIO_CHANNEL_INDEX_MASK_1)},
-    {"AUDIO_CHANNEL_INDEX_MASK_2", static_cast<audio_channel_mask_t>(AUDIO_CHANNEL_INDEX_MASK_2)},
-    {"AUDIO_CHANNEL_INDEX_MASK_3", static_cast<audio_channel_mask_t>(AUDIO_CHANNEL_INDEX_MASK_3)},
-    {"AUDIO_CHANNEL_INDEX_MASK_4", static_cast<audio_channel_mask_t>(AUDIO_CHANNEL_INDEX_MASK_4)},
-    {"AUDIO_CHANNEL_INDEX_MASK_5", static_cast<audio_channel_mask_t>(AUDIO_CHANNEL_INDEX_MASK_5)},
-    {"AUDIO_CHANNEL_INDEX_MASK_6", static_cast<audio_channel_mask_t>(AUDIO_CHANNEL_INDEX_MASK_6)},
-    {"AUDIO_CHANNEL_INDEX_MASK_7", static_cast<audio_channel_mask_t>(AUDIO_CHANNEL_INDEX_MASK_7)},
-    {"AUDIO_CHANNEL_INDEX_MASK_8", static_cast<audio_channel_mask_t>(AUDIO_CHANNEL_INDEX_MASK_8)},
-    TERMINATOR
-};
-
-
-template <>
-const GainModeConverter::Table GainModeConverter::mTable[] = {
-    MAKE_STRING_FROM_ENUM(AUDIO_GAIN_MODE_JOINT),
-    MAKE_STRING_FROM_ENUM(AUDIO_GAIN_MODE_CHANNELS),
-    MAKE_STRING_FROM_ENUM(AUDIO_GAIN_MODE_RAMP),
-    TERMINATOR
-};
-
-
-template <>
-const StreamTypeConverter::Table StreamTypeConverter::mTable[] = {
-    MAKE_STRING_FROM_ENUM(AUDIO_STREAM_DEFAULT),
-    MAKE_STRING_FROM_ENUM(AUDIO_STREAM_VOICE_CALL),
-    MAKE_STRING_FROM_ENUM(AUDIO_STREAM_SYSTEM),
-    MAKE_STRING_FROM_ENUM(AUDIO_STREAM_RING),
-    MAKE_STRING_FROM_ENUM(AUDIO_STREAM_MUSIC),
-    MAKE_STRING_FROM_ENUM(AUDIO_STREAM_ALARM),
-    MAKE_STRING_FROM_ENUM(AUDIO_STREAM_NOTIFICATION),
-    MAKE_STRING_FROM_ENUM(AUDIO_STREAM_BLUETOOTH_SCO ),
-    MAKE_STRING_FROM_ENUM(AUDIO_STREAM_ENFORCED_AUDIBLE),
-    MAKE_STRING_FROM_ENUM(AUDIO_STREAM_DTMF),
-    MAKE_STRING_FROM_ENUM(AUDIO_STREAM_TTS),
-    MAKE_STRING_FROM_ENUM(AUDIO_STREAM_ACCESSIBILITY),
-    MAKE_STRING_FROM_ENUM(AUDIO_STREAM_ASSISTANT),
-    MAKE_STRING_FROM_ENUM(AUDIO_STREAM_REROUTING),
-    MAKE_STRING_FROM_ENUM(AUDIO_STREAM_PATCH),
-    MAKE_STRING_FROM_ENUM(AUDIO_STREAM_CALL_ASSISTANT),
-    TERMINATOR
-};
-
 template<>
 const AudioModeConverter::Table AudioModeConverter::mTable[] = {
     MAKE_STRING_FROM_ENUM(AUDIO_MODE_INVALID),
@@ -339,62 +33,6 @@
     TERMINATOR
 };
 
-template<>
-const AudioContentTypeConverter::Table AudioContentTypeConverter::mTable[] = {
-    MAKE_STRING_FROM_ENUM(AUDIO_CONTENT_TYPE_UNKNOWN),
-    MAKE_STRING_FROM_ENUM(AUDIO_CONTENT_TYPE_SPEECH),
-    MAKE_STRING_FROM_ENUM(AUDIO_CONTENT_TYPE_MUSIC),
-    MAKE_STRING_FROM_ENUM(AUDIO_CONTENT_TYPE_MOVIE),
-    MAKE_STRING_FROM_ENUM(AUDIO_CONTENT_TYPE_SONIFICATION),
-    TERMINATOR
-};
-
-template <>
-const UsageTypeConverter::Table UsageTypeConverter::mTable[] = {
-    MAKE_STRING_FROM_ENUM(AUDIO_USAGE_UNKNOWN),
-    MAKE_STRING_FROM_ENUM(AUDIO_USAGE_MEDIA),
-    MAKE_STRING_FROM_ENUM(AUDIO_USAGE_VOICE_COMMUNICATION),
-    MAKE_STRING_FROM_ENUM(AUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING),
-    MAKE_STRING_FROM_ENUM(AUDIO_USAGE_ALARM),
-    MAKE_STRING_FROM_ENUM(AUDIO_USAGE_NOTIFICATION),
-    MAKE_STRING_FROM_ENUM(AUDIO_USAGE_NOTIFICATION_TELEPHONY_RINGTONE),
-    MAKE_STRING_FROM_ENUM(AUDIO_USAGE_NOTIFICATION_COMMUNICATION_REQUEST),
-    MAKE_STRING_FROM_ENUM(AUDIO_USAGE_NOTIFICATION_COMMUNICATION_INSTANT),
-    MAKE_STRING_FROM_ENUM(AUDIO_USAGE_NOTIFICATION_COMMUNICATION_DELAYED),
-    MAKE_STRING_FROM_ENUM(AUDIO_USAGE_NOTIFICATION_EVENT),
-    MAKE_STRING_FROM_ENUM(AUDIO_USAGE_ASSISTANCE_ACCESSIBILITY),
-    MAKE_STRING_FROM_ENUM(AUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE),
-    MAKE_STRING_FROM_ENUM(AUDIO_USAGE_ASSISTANCE_SONIFICATION),
-    MAKE_STRING_FROM_ENUM(AUDIO_USAGE_GAME),
-    MAKE_STRING_FROM_ENUM(AUDIO_USAGE_VIRTUAL_SOURCE),
-    MAKE_STRING_FROM_ENUM(AUDIO_USAGE_ASSISTANT),
-    MAKE_STRING_FROM_ENUM(AUDIO_USAGE_CALL_ASSISTANT),
-    MAKE_STRING_FROM_ENUM(AUDIO_USAGE_EMERGENCY),
-    MAKE_STRING_FROM_ENUM(AUDIO_USAGE_SAFETY),
-    MAKE_STRING_FROM_ENUM(AUDIO_USAGE_VEHICLE_STATUS),
-    MAKE_STRING_FROM_ENUM(AUDIO_USAGE_ANNOUNCEMENT),
-    TERMINATOR
-};
-
-template <>
-const SourceTypeConverter::Table SourceTypeConverter::mTable[] = {
-    MAKE_STRING_FROM_ENUM(AUDIO_SOURCE_DEFAULT),
-    MAKE_STRING_FROM_ENUM(AUDIO_SOURCE_MIC),
-    MAKE_STRING_FROM_ENUM(AUDIO_SOURCE_VOICE_UPLINK),
-    MAKE_STRING_FROM_ENUM(AUDIO_SOURCE_VOICE_DOWNLINK),
-    MAKE_STRING_FROM_ENUM(AUDIO_SOURCE_VOICE_CALL),
-    MAKE_STRING_FROM_ENUM(AUDIO_SOURCE_CAMCORDER),
-    MAKE_STRING_FROM_ENUM(AUDIO_SOURCE_VOICE_RECOGNITION),
-    MAKE_STRING_FROM_ENUM(AUDIO_SOURCE_VOICE_COMMUNICATION),
-    MAKE_STRING_FROM_ENUM(AUDIO_SOURCE_REMOTE_SUBMIX),
-    MAKE_STRING_FROM_ENUM(AUDIO_SOURCE_UNPROCESSED),
-    MAKE_STRING_FROM_ENUM(AUDIO_SOURCE_VOICE_PERFORMANCE),
-    MAKE_STRING_FROM_ENUM(AUDIO_SOURCE_ECHO_REFERENCE),
-    MAKE_STRING_FROM_ENUM(AUDIO_SOURCE_FM_TUNER),
-    MAKE_STRING_FROM_ENUM(AUDIO_SOURCE_HOTWORD),
-    TERMINATOR
-};
-
 template <>
 const AudioFlagConverter::Table AudioFlagConverter::mTable[] = {
     MAKE_STRING_FROM_ENUM(AUDIO_FLAG_NONE),
@@ -417,6 +55,7 @@
 
 template class TypeConverter<OutputDeviceTraits>;
 template class TypeConverter<InputDeviceTraits>;
+template class TypeConverter<DeviceTraits>;
 template class TypeConverter<OutputFlagTraits>;
 template class TypeConverter<InputFlagTraits>;
 template class TypeConverter<FormatTraits>;
@@ -430,11 +69,6 @@
 template class TypeConverter<SourceTraits>;
 template class TypeConverter<AudioFlagTraits>;
 
-bool deviceFromString(const std::string& literalDevice, audio_devices_t& device) {
-    return InputDeviceConverter::fromString(literalDevice, device) ||
-            OutputDeviceConverter::fromString(literalDevice, device);
-}
-
 SampleRateTraits::Collection samplingRatesFromString(
         const std::string &samplingRates, const char *del)
 {
@@ -454,21 +88,20 @@
 audio_format_t formatFromString(const std::string &literalFormat, audio_format_t defaultFormat)
 {
     audio_format_t format;
-    if (literalFormat.empty()) {
-        return defaultFormat;
+    if (!literalFormat.empty() && FormatConverter::fromString(literalFormat, format)) {
+        return format;
     }
-    FormatConverter::fromString(literalFormat, format);
-    return format;
+    return defaultFormat;
 }
 
 audio_channel_mask_t channelMaskFromString(const std::string &literalChannels)
 {
     audio_channel_mask_t channels;
-    if (!OutputChannelConverter::fromString(literalChannels, channels) &&
-            !InputChannelConverter::fromString(literalChannels, channels)) {
-        return AUDIO_CHANNEL_INVALID;
+    if (!literalChannels.empty() &&
+            audio_channel_mask_from_string(literalChannels.c_str(), &channels)) {
+        return channels;
     }
-    return channels;
+    return AUDIO_CHANNEL_INVALID;
 }
 
 ChannelTraits::Collection channelMasksFromString(
diff --git a/media/libmediahelper/include/media/TypeConverter.h b/media/libmediahelper/include/media/TypeConverter.h
index 011498a..42ccb5f 100644
--- a/media/libmediahelper/include/media/TypeConverter.h
+++ b/media/libmediahelper/include/media/TypeConverter.h
@@ -24,8 +24,6 @@
 
 #include <system/audio.h>
 #include <utils/Log.h>
-#include <utils/Vector.h>
-#include <utils/SortedVector.h>
 
 #include <media/AudioParameter.h>
 #include "convert.h"
@@ -43,16 +41,6 @@
     }
 };
 template <typename T>
-struct SortedVectorTraits
-{
-    typedef T Type;
-    typedef SortedVector<Type> Collection;
-    static void add(Collection &collection, Type value)
-    {
-        collection.add(value);
-    }
-};
-template <typename T>
 struct SetTraits
 {
     typedef T Type;
@@ -108,13 +96,20 @@
                                      typename Traits::Collection &collection,
                                      const char *del = AudioParameter::valueListSeparator);
 
-    static uint32_t maskFromString(
+    static typename Traits::Type maskFromString(
             const std::string &str, const char *del = AudioParameter::valueListSeparator);
 
     static void maskToString(
-            uint32_t mask, std::string &str, const char *del = AudioParameter::valueListSeparator);
+            typename Traits::Type mask, std::string &str,
+            const char *del = AudioParameter::valueListSeparator);
 
 protected:
+    // Default implementations use mTable for to/from string conversions
+    // of each individual enum value.
+    // These functions may be specialized to use external converters instead.
+    static bool toStringImpl(const typename Traits::Type &value, std::string &str);
+    static bool fromStringImpl(const std::string &str, typename Traits::Type &result);
+
     struct Table {
         const char *literal;
         typename Traits::Type value;
@@ -124,26 +119,22 @@
 };
 
 template <class Traits>
-inline bool TypeConverter<Traits>::toString(const typename Traits::Type &value, std::string &str)
-{
+inline bool TypeConverter<Traits>::toStringImpl(
+        const typename Traits::Type &value, std::string &str) {
     for (size_t i = 0; mTable[i].literal; i++) {
         if (mTable[i].value == value) {
             str = mTable[i].literal;
             return true;
         }
     }
-    char result[64];
-    snprintf(result, sizeof(result), "Unknown enum value %d", value);
-    str = result;
     return false;
 }
 
 template <class Traits>
-inline bool TypeConverter<Traits>::fromString(const std::string &str, typename Traits::Type &result)
-{
+inline bool TypeConverter<Traits>::fromStringImpl(
+        const std::string &str, typename Traits::Type &result) {
     for (size_t i = 0; mTable[i].literal; i++) {
         if (strcmp(mTable[i].literal, str.c_str()) == 0) {
-            ALOGV("stringToEnum() found %s", mTable[i].literal);
             result = mTable[i].value;
             return true;
         }
@@ -152,6 +143,26 @@
 }
 
 template <class Traits>
+inline bool TypeConverter<Traits>::toString(const typename Traits::Type &value, std::string &str)
+{
+    const bool success = toStringImpl(value, str);
+    if (!success) {
+        char result[64];
+        snprintf(result, sizeof(result), "Unknown enum value %d", value);
+        str = result;
+    }
+    return success;
+}
+
+template <class Traits>
+inline bool TypeConverter<Traits>::fromString(const std::string &str, typename Traits::Type &result)
+{
+    const bool success = fromStringImpl(str, result);
+    ALOGV_IF(success, "stringToEnum() found %s", str.c_str());
+    return success;
+}
+
+template <class Traits>
 inline void TypeConverter<Traits>::collectionFromString(const std::string &str,
         typename Traits::Collection &collection,
         const char *del)
@@ -168,7 +179,8 @@
 }
 
 template <class Traits>
-inline uint32_t TypeConverter<Traits>::maskFromString(const std::string &str, const char *del)
+inline typename Traits::Type TypeConverter<Traits>::maskFromString(
+        const std::string &str, const char *del)
 {
     char *literal = strdup(str.c_str());
     uint32_t value = 0;
@@ -179,20 +191,24 @@
         }
     }
     free(literal);
-    return value;
+    return static_cast<typename Traits::Type>(value);
 }
 
 template <class Traits>
-inline void TypeConverter<Traits>::maskToString(uint32_t mask, std::string &str, const char *del)
+inline void TypeConverter<Traits>::maskToString(
+        typename Traits::Type mask, std::string &str, const char *del)
 {
     if (mask != 0) {
         bool first_flag = true;
-        for (size_t i = 0; mTable[i].literal; i++) {
-            uint32_t value = static_cast<uint32_t>(mTable[i].value);
-            if (mTable[i].value != 0 && ((mask & value) == value)) {
-                if (!first_flag) str += del;
-                first_flag = false;
-                str += mTable[i].literal;
+        for (size_t bit = 0; bit < sizeof(uint32_t) * 8; ++bit) {
+            uint32_t flag = 1u << bit;
+            if ((flag & mask) == flag) {
+                std::string flag_str;
+                if (toString(static_cast<typename Traits::Type>(flag), flag_str)) {
+                    if (!first_flag) str += del;
+                    first_flag = false;
+                    str += flag_str;
+                }
             }
         }
     } else {
@@ -200,6 +216,7 @@
     }
 }
 
+typedef TypeConverter<DeviceTraits> DeviceConverter;
 typedef TypeConverter<OutputDeviceTraits> OutputDeviceConverter;
 typedef TypeConverter<InputDeviceTraits> InputDeviceConverter;
 typedef TypeConverter<OutputFlagTraits> OutputFlagConverter;
@@ -216,23 +233,227 @@
 typedef TypeConverter<SourceTraits> SourceTypeConverter;
 typedef TypeConverter<AudioFlagTraits> AudioFlagConverter;
 
-template<> const OutputDeviceConverter::Table OutputDeviceConverter::mTable[];
-template<> const InputDeviceConverter::Table InputDeviceConverter::mTable[];
-template<> const OutputFlagConverter::Table OutputFlagConverter::mTable[];
-template<> const InputFlagConverter::Table InputFlagConverter::mTable[];
-template<> const FormatConverter::Table FormatConverter::mTable[];
-template<> const OutputChannelConverter::Table OutputChannelConverter::mTable[];
-template<> const InputChannelConverter::Table InputChannelConverter::mTable[];
-template<> const ChannelIndexConverter::Table ChannelIndexConverter::mTable[];
-template<> const GainModeConverter::Table GainModeConverter::mTable[];
-template<> const StreamTypeConverter::Table StreamTypeConverter::mTable[];
 template<> const AudioModeConverter::Table AudioModeConverter::mTable[];
-template<> const AudioContentTypeConverter::Table AudioContentTypeConverter::mTable[];
-template<> const UsageTypeConverter::Table UsageTypeConverter::mTable[];
-template<> const SourceTypeConverter::Table SourceTypeConverter::mTable[];
 template<> const AudioFlagConverter::Table AudioFlagConverter::mTable[];
 
-bool deviceFromString(const std::string& literalDevice, audio_devices_t& device);
+template <>
+inline bool TypeConverter<DeviceTraits>::toStringImpl(
+        const DeviceTraits::Type &value, std::string &str) {
+    str = audio_device_to_string(value);
+    return !str.empty();
+}
+
+template <>
+inline bool TypeConverter<DeviceTraits>::fromStringImpl(
+        const std::string &str, DeviceTraits::Type &result) {
+    return audio_device_from_string(str.c_str(), &result);
+}
+
+template <>
+inline bool TypeConverter<OutputDeviceTraits>::toStringImpl(
+        const OutputDeviceTraits::Type &value, std::string &str) {
+    if (audio_is_output_device(value)) {
+        str = audio_device_to_string(value);
+        return !str.empty();
+    }
+    return false;
+}
+
+template <>
+inline bool TypeConverter<OutputDeviceTraits>::fromStringImpl(
+        const std::string &str, OutputDeviceTraits::Type &result) {
+    OutputDeviceTraits::Type temp;
+    if (audio_device_from_string(str.c_str(), &temp) &&
+            audio_is_output_device(temp)) {
+        result = temp;
+        return true;
+    }
+    return false;
+}
+
+template <>
+inline bool TypeConverter<InputDeviceTraits>::toStringImpl(
+        const InputDeviceTraits::Type &value, std::string &str) {
+    if (audio_is_input_device(value)) {
+        str = audio_device_to_string(value);
+        return !str.empty();
+    }
+    return false;
+}
+
+template <>
+inline bool TypeConverter<InputDeviceTraits>::fromStringImpl(
+        const std::string &str, InputDeviceTraits::Type &result) {
+    InputDeviceTraits::Type temp;
+    if (audio_device_from_string(str.c_str(), &temp) &&
+            audio_is_input_device(temp)) {
+        result = temp;
+        return true;
+    }
+    return false;
+}
+
+template <>
+inline bool TypeConverter<InputFlagTraits>::toStringImpl(
+        const audio_input_flags_t &value, std::string &str) {
+    str = audio_input_flag_to_string(value);
+    return !str.empty();
+}
+
+template <>
+inline bool TypeConverter<InputFlagTraits>::fromStringImpl(
+        const std::string &str, audio_input_flags_t &result) {
+    return audio_input_flag_from_string(str.c_str(), &result);
+}
+
+template <>
+inline bool TypeConverter<OutputFlagTraits>::toStringImpl(
+        const audio_output_flags_t &value, std::string &str) {
+    str = audio_output_flag_to_string(value);
+    return !str.empty();
+}
+
+template <>
+inline bool TypeConverter<OutputFlagTraits>::fromStringImpl(
+        const std::string &str, audio_output_flags_t &result) {
+    return audio_output_flag_from_string(str.c_str(), &result);
+}
+
+template <>
+inline bool TypeConverter<FormatTraits>::toStringImpl(
+        const audio_format_t &value, std::string &str) {
+    str = audio_format_to_string(value);
+    return !str.empty();
+}
+
+template <>
+inline bool TypeConverter<FormatTraits>::fromStringImpl(
+        const std::string &str, audio_format_t &result) {
+    return audio_format_from_string(str.c_str(), &result);
+}
+
+template <>
+inline bool TypeConverter<OutputChannelTraits>::toStringImpl(
+        const audio_channel_mask_t &value, std::string &str) {
+    str = audio_channel_out_mask_to_string(value);
+    return !str.empty();
+}
+
+template <>
+inline bool TypeConverter<OutputChannelTraits>::fromStringImpl(
+        const std::string &str, audio_channel_mask_t &result) {
+    OutputChannelTraits::Type temp;
+    if (audio_channel_mask_from_string(str.c_str(), &temp) &&
+            audio_is_output_channel(temp)) {
+        result = temp;
+        return true;
+    }
+    return false;
+}
+
+template <>
+inline bool TypeConverter<InputChannelTraits>::toStringImpl(
+        const audio_channel_mask_t &value, std::string &str) {
+    str = audio_channel_in_mask_to_string(value);
+    return !str.empty();
+}
+
+template <>
+inline bool TypeConverter<InputChannelTraits>::fromStringImpl(
+        const std::string &str, audio_channel_mask_t &result) {
+    InputChannelTraits::Type temp;
+    if (audio_channel_mask_from_string(str.c_str(), &temp) &&
+            audio_is_input_channel(temp)) {
+        result = temp;
+        return true;
+    }
+    return false;
+}
+
+template <>
+inline bool TypeConverter<ChannelIndexTraits>::toStringImpl(
+        const audio_channel_mask_t &value, std::string &str) {
+    str = audio_channel_index_mask_to_string(value);
+    return !str.empty();
+}
+
+template <>
+inline bool TypeConverter<ChannelIndexTraits>::fromStringImpl(
+        const std::string &str, audio_channel_mask_t &result) {
+    ChannelIndexTraits::Type temp;
+    if (audio_channel_mask_from_string(str.c_str(), &temp) &&
+            audio_channel_mask_get_representation(temp) == AUDIO_CHANNEL_REPRESENTATION_INDEX) {
+        result = temp;
+        return true;
+    }
+    return false;
+}
+
+template <>
+inline bool TypeConverter<StreamTraits>::toStringImpl(
+        const audio_stream_type_t &value, std::string &str) {
+    str = audio_stream_type_to_string(value);
+    return !str.empty();
+}
+
+template <>
+inline bool TypeConverter<StreamTraits>::fromStringImpl(
+        const std::string &str, audio_stream_type_t &result)
+{
+    return audio_stream_type_from_string(str.c_str(), &result);
+}
+
+template <>
+inline bool TypeConverter<GainModeTraits>::toStringImpl(
+        const audio_gain_mode_t &value, std::string &str) {
+    str = audio_gain_mode_to_string(value);
+    return !str.empty();
+}
+
+template <>
+inline bool TypeConverter<GainModeTraits>::fromStringImpl(
+        const std::string &str, audio_gain_mode_t &result) {
+    return audio_gain_mode_from_string(str.c_str(), &result);
+}
+
+template <>
+inline bool TypeConverter<AudioContentTraits>::toStringImpl(
+        const audio_content_type_t &value, std::string &str) {
+    str = audio_content_type_to_string(value);
+    return !str.empty();
+}
+
+template <>
+inline bool TypeConverter<AudioContentTraits>::fromStringImpl(
+        const std::string &str, audio_content_type_t &result) {
+    return audio_content_type_from_string(str.c_str(), &result);
+}
+
+template <>
+inline bool TypeConverter<UsageTraits>::toStringImpl(const audio_usage_t &value, std::string &str)
+{
+    str = audio_usage_to_string(value);
+    return !str.empty();
+}
+
+template <>
+inline bool TypeConverter<UsageTraits>::fromStringImpl(
+        const std::string &str, audio_usage_t &result) {
+    return audio_usage_from_string(str.c_str(), &result);
+}
+
+template <>
+inline bool TypeConverter<SourceTraits>::toStringImpl(const audio_source_t &value, std::string &str)
+{
+    str = audio_source_to_string(value);
+    return !str.empty();
+}
+
+template <>
+inline bool TypeConverter<SourceTraits>::fromStringImpl(
+        const std::string &str, audio_source_t &result) {
+    return audio_source_from_string(str.c_str(), &result);
+}
 
 SampleRateTraits::Collection samplingRatesFromString(
         const std::string &samplingRates, const char *del = AudioParameter::valueListSeparator);
@@ -256,6 +477,7 @@
 
 // counting enumerations
 template <typename T, std::enable_if_t<std::is_same<T, audio_content_type_t>::value
+                                    || std::is_same<T, audio_devices_t>::value
                                     || std::is_same<T, audio_mode_t>::value
                                     || std::is_same<T, audio_source_t>::value
                                     || std::is_same<T, audio_stream_type_t>::value
@@ -282,17 +504,6 @@
     return result;
 }
 
-static inline std::string toString(const audio_devices_t& devices)
-{
-    std::string result;
-    if ((devices & AUDIO_DEVICE_BIT_IN) != 0) {
-        InputDeviceConverter::maskToString(devices, result);
-    } else {
-        OutputDeviceConverter::maskToString(devices, result);
-    }
-    return result;
-}
-
 static inline std::string toString(const audio_attributes_t& attributes)
 {
     std::ostringstream result;
diff --git a/media/libmediahelper/tests/Android.bp b/media/libmediahelper/tests/Android.bp
new file mode 100644
index 0000000..c5ba122
--- /dev/null
+++ b/media/libmediahelper/tests/Android.bp
@@ -0,0 +1,22 @@
+cc_test {
+    name: "libmedia_helper_tests",
+
+    generated_headers: ["audio_policy_configuration_V7_0"],
+    generated_sources: ["audio_policy_configuration_V7_0"],
+    header_libs: ["libxsdc-utils"],
+    shared_libs: [
+        "libbase",
+        "liblog",
+        "libmedia_helper",
+        "libxml2",
+    ],
+
+    srcs: ["typeconverter_tests.cpp"],
+
+    cflags: [
+        "-Werror",
+        "-Wall",
+    ],
+
+    test_suites: ["device-tests"],
+}
diff --git a/media/libmediahelper/tests/typeconverter_tests.cpp b/media/libmediahelper/tests/typeconverter_tests.cpp
new file mode 100644
index 0000000..ab55c13
--- /dev/null
+++ b/media/libmediahelper/tests/typeconverter_tests.cpp
@@ -0,0 +1,226 @@
+/*
+ * 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.
+ */
+
+#include <gtest/gtest.h>
+
+#define LOG_TAG "TypeConverter_Test"
+#include <log/log.h>
+
+#include <audio_policy_configuration_V7_0.h>
+#include <media/TypeConverter.h>
+#include <system/audio.h>
+#include <xsdc/XsdcSupport.h>
+
+using namespace android;
+namespace xsd {
+using namespace audio::policy::configuration::V7_0;
+}
+
+TEST(TypeConverter, ParseChannelMasks) {
+    for (const auto enumVal : xsdc_enum_range<xsd::AudioChannelMask>{}) {
+        const std::string stringVal = toString(enumVal);
+        audio_channel_mask_t channelMask = channelMaskFromString(stringVal);
+        EXPECT_EQ(stringVal != "AUDIO_CHANNEL_NONE", audio_channel_mask_is_valid(channelMask))
+                << "Validity of \"" << stringVal << "\" is not as expected";
+    }
+}
+
+TEST(TypeConverter, ParseInputOutputIndexChannelMask) {
+    for (const auto enumVal : xsdc_enum_range<xsd::AudioChannelMask>{}) {
+        const std::string stringVal = toString(enumVal);
+        audio_channel_mask_t channelMask, channelMaskBack;
+        std::string stringValBack;
+        if (stringVal.find("_CHANNEL_IN_") != std::string::npos) {
+            EXPECT_TRUE(InputChannelConverter::fromString(stringVal, channelMask))
+                    << "Conversion of \"" << stringVal << "\" failed (as input channel mask)";
+            EXPECT_TRUE(InputChannelConverter::toString(channelMask, stringValBack))
+                    << "Conversion of input channel mask " << channelMask << " failed";
+            // Due to aliased values, the result of 'toString' might not be the same
+            // as 'stringVal', thus we need to compare the results of parsing instead.
+            EXPECT_TRUE(InputChannelConverter::fromString(stringValBack, channelMaskBack))
+                    << "Conversion of \"" << stringValBack << "\" failed (as input channel mask)";
+            EXPECT_EQ(channelMask, channelMaskBack);
+        } else if (stringVal.find("_CHANNEL_OUT_") != std::string::npos) {
+            EXPECT_TRUE(OutputChannelConverter::fromString(stringVal, channelMask))
+                    << "Conversion of \"" << stringVal << "\" failed (as output channel mask)";
+            EXPECT_TRUE(OutputChannelConverter::toString(channelMask, stringValBack))
+                    << "Conversion of output channel mask " << channelMask << " failed";
+            EXPECT_TRUE(OutputChannelConverter::fromString(stringValBack, channelMaskBack))
+                    << "Conversion of \"" << stringValBack << "\" failed (as output channel mask)";
+            EXPECT_EQ(channelMask, channelMaskBack);
+        } else if (stringVal.find("_CHANNEL_INDEX_") != std::string::npos) {
+            EXPECT_TRUE(ChannelIndexConverter::fromString(stringVal, channelMask))
+                    << "Conversion of \"" << stringVal << "\" failed (as indexed channel mask)";
+            EXPECT_TRUE(ChannelIndexConverter::toString(channelMask, stringValBack))
+                    << "Conversion of indexed channel mask " << channelMask << " failed";
+            EXPECT_EQ(stringVal, stringValBack);
+        } else if (stringVal == "AUDIO_CHANNEL_NONE") {
+            EXPECT_FALSE(InputChannelConverter::fromString(stringVal, channelMask))
+                    << "Conversion of \"" << stringVal << "\" succeeded (as input channel mask)";
+            EXPECT_FALSE(OutputChannelConverter::fromString(stringVal, channelMask))
+                    << "Conversion of \"" << stringVal << "\" succeeded (as output channel mask)";
+            EXPECT_FALSE(ChannelIndexConverter::fromString(stringVal, channelMask))
+                    << "Conversion of \"" << stringVal << "\" succeeded (as index channel mask)";
+            // None of Converters could parse this because 'NONE' isn't a 'valid' channel mask.
+            channelMask = AUDIO_CHANNEL_NONE;
+            // However they all must succeed in converting it back.
+            EXPECT_TRUE(InputChannelConverter::toString(channelMask, stringValBack))
+                    << "Conversion of input channel mask " << channelMask << " failed";
+            EXPECT_EQ(stringVal, stringValBack);
+            EXPECT_TRUE(OutputChannelConverter::toString(channelMask, stringValBack))
+                    << "Conversion of output channel mask " << channelMask << " failed";
+            EXPECT_EQ(stringVal, stringValBack);
+            EXPECT_TRUE(ChannelIndexConverter::toString(channelMask, stringValBack))
+                    << "Conversion of indexed channel mask " << channelMask << " failed";
+            EXPECT_EQ(stringVal, stringValBack);
+        }
+    }
+}
+
+TEST(TypeConverter, ParseContentTypes) {
+    for (const auto enumVal : xsdc_enum_range<xsd::AudioContentType>{}) {
+        const std::string stringVal = toString(enumVal);
+        audio_content_type_t contentType;
+        EXPECT_TRUE(AudioContentTypeConverter::fromString(stringVal, contentType))
+                << "Conversion of \"" << stringVal << "\" failed";
+        EXPECT_EQ(stringVal, toString(contentType));
+    }
+}
+
+TEST(TypeConverter, ParseDevices) {
+    for (const auto enumVal : xsdc_enum_range<xsd::AudioDevice>{}) {
+        const std::string stringVal = toString(enumVal);
+        audio_devices_t device, deviceBack;
+        std::string stringValBack;
+        EXPECT_TRUE(DeviceConverter::fromString(stringVal, device))
+                << "Conversion of \"" << stringVal << "\" failed";
+        if (stringVal != "AUDIO_DEVICE_NONE") {
+            EXPECT_TRUE(audio_is_input_device(device) || audio_is_output_device(device))
+                    << "Device \"" << stringVal << "\" is neither input, nor output device";
+        } else {
+            EXPECT_FALSE(audio_is_input_device(device));
+            EXPECT_FALSE(audio_is_output_device(device));
+        }
+        // Due to aliased values, the result of 'toString' might not be the same
+        // as 'stringVal', thus we need to compare the results of parsing instead.
+        stringValBack = toString(device);
+        EXPECT_TRUE(DeviceConverter::fromString(stringValBack, deviceBack))
+                << "Conversion of \"" << stringValBack << "\" failed";
+        EXPECT_EQ(device, deviceBack);
+    }
+}
+
+TEST(TypeConverter, ParseInOutDevices) {
+    for (const auto enumVal : xsdc_enum_range<xsd::AudioDevice>{}) {
+        const std::string stringVal = toString(enumVal);
+        audio_devices_t device, deviceBack;
+        std::string stringValBack;
+        if (stringVal.find("_DEVICE_IN_") != std::string::npos) {
+            EXPECT_TRUE(InputDeviceConverter::fromString(stringVal, device))
+                    << "Conversion of \"" << stringVal << "\" failed (as input device)";
+            // Due to aliased values, the result of 'toString' might not be the same
+            // as 'stringVal', thus we need to compare the results of parsing instead.
+            stringValBack = toString(device);
+            EXPECT_TRUE(InputDeviceConverter::fromString(stringValBack, deviceBack))
+                    << "Conversion of \"" << stringValBack << "\" failed";
+            EXPECT_EQ(device, deviceBack);
+        } else if (stringVal.find("_DEVICE_OUT_") != std::string::npos) {
+            EXPECT_TRUE(OutputDeviceConverter::fromString(stringVal, device))
+                    << "Conversion of \"" << stringVal << "\" failed (as output device)";
+            stringValBack = toString(device);
+            EXPECT_TRUE(OutputDeviceConverter::fromString(stringValBack, deviceBack))
+                    << "Conversion of \"" << stringValBack << "\" failed";
+            EXPECT_EQ(device, deviceBack);
+        } else if (stringVal == "AUDIO_DEVICE_NONE") {
+            EXPECT_FALSE(InputDeviceConverter::fromString(stringVal, device))
+                    << "Conversion of \"" << stringVal << "\" succeeded (as input device)";
+            EXPECT_FALSE(OutputDeviceConverter::fromString(stringVal, device))
+                    << "Conversion of \"" << stringVal << "\" succeeded (as output device)";
+            EXPECT_EQ(stringVal, toString(device));
+        }
+    }
+}
+
+TEST (TypeConverter, ParseInOutFlags) {
+    for (const auto enumVal : xsdc_enum_range<xsd::AudioInOutFlag>{}) {
+        const std::string stringVal = toString(enumVal);
+        if (stringVal.find("_INPUT_FLAG_") != std::string::npos) {
+            audio_input_flags_t flag;
+            EXPECT_TRUE(InputFlagConverter::fromString(stringVal, flag))
+                    << "Conversion of \"" << stringVal << "\" failed (as input flag)";
+            EXPECT_EQ(stringVal, toString(flag));
+        } else {
+            audio_output_flags_t flag;
+            EXPECT_TRUE(OutputFlagConverter::fromString(stringVal, flag))
+                    << "Conversion of \"" << stringVal << "\" failed (as output flag)";
+            EXPECT_EQ(stringVal, toString(flag));
+        }
+    }
+}
+
+TEST(TypeConverter, ParseFormats) {
+    for (const auto enumVal : xsdc_enum_range<xsd::AudioFormat>{}) {
+        const std::string stringVal = toString(enumVal);
+        audio_format_t format;
+        EXPECT_TRUE(FormatConverter::fromString(stringVal, format))
+                << "Conversion of \"" << stringVal << "\" failed";
+        EXPECT_TRUE(audio_is_valid_format(format))
+                << "Converted format \"" << stringVal << "\" is invalid";
+        EXPECT_EQ(stringVal, toString(format));
+    }
+}
+
+TEST(TypeConverter, ParseGainModes) {
+    for (const auto enumVal : xsdc_enum_range<xsd::AudioGainMode>{}) {
+        const std::string stringVal = toString(enumVal);
+        audio_gain_mode_t gainMode;
+        EXPECT_TRUE(GainModeConverter::fromString(stringVal, gainMode))
+                << "Conversion of \"" << stringVal << "\" failed";
+        EXPECT_EQ(stringVal, toString(gainMode));
+    }
+}
+
+TEST(TypeConverter, ParseSources) {
+    for (const auto enumVal : xsdc_enum_range<xsd::AudioSource>{}) {
+        const std::string stringVal = toString(enumVal);
+        audio_source_t source;
+        EXPECT_TRUE(SourceTypeConverter::fromString(stringVal, source))
+                << "Conversion of \"" << stringVal << "\" failed";
+        EXPECT_EQ(source != AUDIO_SOURCE_DEFAULT, audio_is_valid_audio_source(source))
+                << "Validity of \"" << stringVal << "\" is not as expected";
+        EXPECT_EQ(stringVal, toString(source));
+    }
+}
+
+TEST(TypeConverter, ParseStreamTypes) {
+    for (const auto enumVal : xsdc_enum_range<xsd::AudioStreamType>{}) {
+        const std::string stringVal = toString(enumVal);
+        audio_stream_type_t streamType;
+        EXPECT_TRUE(StreamTypeConverter::fromString(stringVal, streamType))
+                << "Conversion of \"" << stringVal << "\" failed";
+        EXPECT_EQ(stringVal, toString(streamType));
+    }
+}
+
+TEST(TypeConverter, ParseUsages) {
+    for (const auto enumVal : xsdc_enum_range<xsd::AudioUsage>{}) {
+        const std::string stringVal = toString(enumVal);
+        audio_usage_t usage;
+        EXPECT_TRUE(UsageTypeConverter::fromString(stringVal, usage))
+                << "Conversion of \"" << stringVal << "\" failed";
+        EXPECT_EQ(stringVal, toString(usage));
+    }
+}
diff --git a/media/libmediaplayerservice/StagefrightMetadataRetriever.cpp b/media/libmediaplayerservice/StagefrightMetadataRetriever.cpp
index 09b9145..02fb6bb 100644
--- a/media/libmediaplayerservice/StagefrightMetadataRetriever.cpp
+++ b/media/libmediaplayerservice/StagefrightMetadataRetriever.cpp
@@ -174,9 +174,7 @@
         ALOGV("getting track %zu of %zu, meta=%s", i, n, meta->toString().c_str());
 
         const char *mime;
-        CHECK(meta->findCString(kKeyMIMEType, &mime));
-
-        if (!strncasecmp(mime, "image/", 6)) {
+        if (meta->findCString(kKeyMIMEType, &mime) && !strncasecmp(mime, "image/", 6)) {
             int32_t isPrimary;
             if ((index < 0 && meta->findInt32(
                     kKeyTrackIsDefault, &isPrimary) && isPrimary)
@@ -208,12 +206,19 @@
     }
 
     const char *mime;
-    CHECK(trackMeta->findCString(kKeyMIMEType, &mime));
+    if (!trackMeta->findCString(kKeyMIMEType, &mime)) {
+        ALOGE("image track has no mime type");
+        return NULL;
+    }
     ALOGV("extracting from %s track", mime);
     if (!strcasecmp(mime, MEDIA_MIMETYPE_IMAGE_ANDROID_HEIC)) {
         mime = MEDIA_MIMETYPE_VIDEO_HEVC;
         trackMeta = new MetaData(*trackMeta);
         trackMeta->setCString(kKeyMIMEType, mime);
+    } else if (!strcasecmp(mime, MEDIA_MIMETYPE_IMAGE_AVIF)) {
+        mime = MEDIA_MIMETYPE_VIDEO_AV1;
+        trackMeta = new MetaData(*trackMeta);
+        trackMeta->setCString(kKeyMIMEType, mime);
     }
 
     bool preferhw = property_get_bool(
@@ -299,9 +304,7 @@
         }
 
         const char *mime;
-        CHECK(meta->findCString(kKeyMIMEType, &mime));
-
-        if (!strncasecmp(mime, "video/", 6)) {
+        if (meta->findCString(kKeyMIMEType, &mime) && !strncasecmp(mime, "video/", 6)) {
             break;
         }
     }
@@ -337,7 +340,10 @@
     }
 
     const char *mime;
-    CHECK(trackMeta->findCString(kKeyMIMEType, &mime));
+    if (!trackMeta->findCString(kKeyMIMEType, &mime)) {
+        ALOGE("video track has no mime information.");
+        return NULL;
+    }
 
     bool preferhw = property_get_bool(
             "media.stagefright.thumbnail.prefer_hw_codecs", false);
@@ -531,7 +537,7 @@
     int32_t audioBitrate = -1;
     int32_t rotationAngle = -1;
     int32_t imageCount = 0;
-    int32_t imagePrimary = 0;
+    int32_t imagePrimary = -1;
     int32_t imageWidth = -1;
     int32_t imageHeight = -1;
     int32_t imageRotation = -1;
@@ -574,29 +580,33 @@
                     mMetaData.add(METADATA_KEY_SAMPLERATE, String8(tmp));
                 }
             } else if (!hasVideo && !strncasecmp("video/", mime, 6)) {
-                hasVideo = true;
-                videoMime = String8(mime);
-
-                CHECK(trackMeta->findInt32(kKeyWidth, &videoWidth));
-                CHECK(trackMeta->findInt32(kKeyHeight, &videoHeight));
                 if (!trackMeta->findInt32(kKeyRotation, &rotationAngle)) {
                     rotationAngle = 0;
                 }
                 if (!trackMeta->findInt32(kKeyFrameCount, &videoFrameCount)) {
                     videoFrameCount = 0;
                 }
-
-                parseColorAspects(trackMeta);
+                if (trackMeta->findInt32(kKeyWidth, &videoWidth)
+                    && trackMeta->findInt32(kKeyHeight, &videoHeight)) {
+                    hasVideo = true;
+                    videoMime = String8(mime);
+                    parseColorAspects(trackMeta);
+                } else {
+                    ALOGE("video track ignored for missing dimensions");
+                }
             } else if (!strncasecmp("image/", mime, 6)) {
                 int32_t isPrimary;
                 if (trackMeta->findInt32(
                         kKeyTrackIsDefault, &isPrimary) && isPrimary) {
-                    imagePrimary = imageCount;
-                    CHECK(trackMeta->findInt32(kKeyWidth, &imageWidth));
-                    CHECK(trackMeta->findInt32(kKeyHeight, &imageHeight));
                     if (!trackMeta->findInt32(kKeyRotation, &imageRotation)) {
                         imageRotation = 0;
                     }
+                    if (trackMeta->findInt32(kKeyWidth, &imageWidth)
+                        && trackMeta->findInt32(kKeyHeight, &imageHeight)) {
+                        imagePrimary = imageCount;
+                    } else {
+                        ALOGE("primary image track ignored for missing dimensions");
+                    }
                 }
                 imageCount++;
             } else if (!strcasecmp(mime, MEDIA_MIMETYPE_TEXT_3GPP)) {
@@ -629,9 +639,11 @@
     if (hasVideo) {
         mMetaData.add(METADATA_KEY_HAS_VIDEO, String8("yes"));
 
+        CHECK(videoWidth >= 0);
         sprintf(tmp, "%d", videoWidth);
         mMetaData.add(METADATA_KEY_VIDEO_WIDTH, String8(tmp));
 
+        CHECK(videoHeight >= 0);
         sprintf(tmp, "%d", videoHeight);
         mMetaData.add(METADATA_KEY_VIDEO_HEIGHT, String8(tmp));
 
@@ -646,7 +658,8 @@
         }
     }
 
-    if (imageCount > 0) {
+    // only if we have a primary image
+    if (imageCount > 0 && imagePrimary >= 0) {
         mMetaData.add(METADATA_KEY_HAS_IMAGE, String8("yes"));
 
         sprintf(tmp, "%d", imageCount);
@@ -655,9 +668,11 @@
         sprintf(tmp, "%d", imagePrimary);
         mMetaData.add(METADATA_KEY_IMAGE_PRIMARY, String8(tmp));
 
+        CHECK(imageWidth >= 0);
         sprintf(tmp, "%d", imageWidth);
         mMetaData.add(METADATA_KEY_IMAGE_WIDTH, String8(tmp));
 
+        CHECK(imageHeight >= 0);
         sprintf(tmp, "%d", imageHeight);
         mMetaData.add(METADATA_KEY_IMAGE_HEIGHT, String8(tmp));
 
@@ -685,10 +700,9 @@
                 !strcasecmp(fileMIME, "video/x-matroska")) {
             sp<MetaData> trackMeta = mExtractor->getTrackMetaData(0);
             const char *trackMIME;
-            if (trackMeta != nullptr) {
-                CHECK(trackMeta->findCString(kKeyMIMEType, &trackMIME));
-            }
-            if (!strncasecmp("audio/", trackMIME, 6)) {
+            if (trackMeta != nullptr
+                && trackMeta->findCString(kKeyMIMEType, &trackMIME)
+                && !strncasecmp("audio/", trackMIME, 6)) {
                 // The matroska file only contains a single audio track,
                 // rewrite its mime type.
                 mMetaData.add(
diff --git a/media/libmediaplayerservice/tests/stagefrightRecorder/StagefrightRecorderTest.cpp b/media/libmediaplayerservice/tests/stagefrightRecorder/StagefrightRecorderTest.cpp
index ac17ef3..5751631 100644
--- a/media/libmediaplayerservice/tests/stagefrightRecorder/StagefrightRecorderTest.cpp
+++ b/media/libmediaplayerservice/tests/stagefrightRecorder/StagefrightRecorderTest.cpp
@@ -29,7 +29,7 @@
 #include <MediaPlayerService.h>
 #include <media/NdkMediaExtractor.h>
 #include <media/stagefright/MediaCodec.h>
-#include <system/audio-base.h>
+#include <system/audio.h>
 
 #include "StagefrightRecorder.h"
 
diff --git a/media/libmediatranscoding/Android.bp b/media/libmediatranscoding/Android.bp
index 128d0d8..7329c63 100644
--- a/media/libmediatranscoding/Android.bp
+++ b/media/libmediatranscoding/Android.bp
@@ -21,12 +21,12 @@
         "aidl/android/media/ITranscodingClient.aidl",
         "aidl/android/media/ITranscodingClientCallback.aidl",
         "aidl/android/media/TranscodingErrorCode.aidl",
-        "aidl/android/media/TranscodingJobPriority.aidl",
-        "aidl/android/media/TranscodingJobStats.aidl",
+        "aidl/android/media/TranscodingSessionPriority.aidl",
+        "aidl/android/media/TranscodingSessionStats.aidl",
         "aidl/android/media/TranscodingType.aidl",
         "aidl/android/media/TranscodingVideoCodecType.aidl",
         "aidl/android/media/TranscodingVideoTrackFormat.aidl",
-        "aidl/android/media/TranscodingJobParcel.aidl",
+        "aidl/android/media/TranscodingSessionParcel.aidl",
         "aidl/android/media/TranscodingRequestParcel.aidl",
         "aidl/android/media/TranscodingResultParcel.aidl",
         "aidl/android/media/TranscodingTestConfig.aidl",
@@ -53,7 +53,7 @@
 
     srcs: [
         "TranscodingClientManager.cpp",
-        "TranscodingJobScheduler.cpp",
+        "TranscodingSessionController.cpp",
         "TranscodingResourcePolicy.cpp",
         "TranscodingUidPolicy.cpp",
         "TranscoderWrapper.cpp",
diff --git a/media/libmediatranscoding/TranscoderWrapper.cpp b/media/libmediatranscoding/TranscoderWrapper.cpp
index 8062fcf..fffbfe9 100644
--- a/media/libmediatranscoding/TranscoderWrapper.cpp
+++ b/media/libmediatranscoding/TranscoderWrapper.cpp
@@ -117,7 +117,7 @@
         return "(unknown)";
     }
     std::string result;
-    result = "job {" + std::to_string(event.clientId) + "," + std::to_string(event.jobId) +
+    result = "session {" + std::to_string(event.clientId) + "," + std::to_string(event.sessionId) +
              "}: " + typeStr;
     if (event.type == Event::Error || event.type == Event::Progress) {
         result += " " + std::to_string(event.arg);
@@ -128,13 +128,13 @@
 class TranscoderWrapper::CallbackImpl : public MediaTranscoder::CallbackInterface {
 public:
     CallbackImpl(const std::shared_ptr<TranscoderWrapper>& owner, ClientIdType clientId,
-                 JobIdType jobId)
-          : mOwner(owner), mClientId(clientId), mJobId(jobId) {}
+                 SessionIdType sessionId)
+          : mOwner(owner), mClientId(clientId), mSessionId(sessionId) {}
 
     virtual void onFinished(const MediaTranscoder* transcoder __unused) override {
         auto owner = mOwner.lock();
         if (owner != nullptr) {
-            owner->onFinish(mClientId, mJobId);
+            owner->onFinish(mClientId, mSessionId);
         }
     }
 
@@ -142,7 +142,7 @@
                          media_status_t error) override {
         auto owner = mOwner.lock();
         if (owner != nullptr) {
-            owner->onError(mClientId, mJobId, error);
+            owner->onError(mClientId, mSessionId, error);
         }
     }
 
@@ -150,23 +150,23 @@
                                   int32_t progress) override {
         auto owner = mOwner.lock();
         if (owner != nullptr) {
-            owner->onProgress(mClientId, mJobId, progress);
+            owner->onProgress(mClientId, mSessionId, progress);
         }
     }
 
     virtual void onCodecResourceLost(const MediaTranscoder* transcoder __unused,
-                                     const std::shared_ptr<const Parcel>& pausedState
+                                     const std::shared_ptr<ndk::ScopedAParcel>& pausedState
                                              __unused) override {
-        ALOGV("%s: job {%lld, %d}", __FUNCTION__, (long long)mClientId, mJobId);
+        ALOGV("%s: session {%lld, %d}", __FUNCTION__, (long long)mClientId, mSessionId);
     }
 
 private:
     std::weak_ptr<TranscoderWrapper> mOwner;
     ClientIdType mClientId;
-    JobIdType mJobId;
+    SessionIdType mSessionId;
 };
 
-TranscoderWrapper::TranscoderWrapper() : mCurrentClientId(0), mCurrentJobId(-1) {
+TranscoderWrapper::TranscoderWrapper() : mCurrentClientId(0), mCurrentSessionId(-1) {
     std::thread(&TranscoderWrapper::threadLoop, this).detach();
 }
 
@@ -178,83 +178,85 @@
     return err == AMEDIACODEC_ERROR_RECLAIMED || err == AMEDIACODEC_ERROR_INSUFFICIENT_RESOURCE;
 }
 
-void TranscoderWrapper::reportError(ClientIdType clientId, JobIdType jobId, media_status_t err) {
+void TranscoderWrapper::reportError(ClientIdType clientId, SessionIdType sessionId,
+                                    media_status_t err) {
     auto callback = mCallback.lock();
     if (callback != nullptr) {
         if (isResourceError(err)) {
             // Add a placeholder pause state to mPausedStateMap. This is required when resuming.
             // TODO: remove this when transcoder pause/resume logic is ready. New logic will
             // no longer use the pause states.
-            auto it = mPausedStateMap.find(JobKeyType(clientId, jobId));
+            auto it = mPausedStateMap.find(SessionKeyType(clientId, sessionId));
             if (it == mPausedStateMap.end()) {
-                mPausedStateMap.emplace(JobKeyType(clientId, jobId),
-                                        std::shared_ptr<const Parcel>());
+                mPausedStateMap.emplace(SessionKeyType(clientId, sessionId),
+                                        new ndk::ScopedAParcel());
             }
 
             callback->onResourceLost();
         } else {
-            callback->onError(clientId, jobId, toTranscodingError(err));
+            callback->onError(clientId, sessionId, toTranscodingError(err));
         }
     }
 }
 
-void TranscoderWrapper::start(ClientIdType clientId, JobIdType jobId,
+void TranscoderWrapper::start(ClientIdType clientId, SessionIdType sessionId,
                               const TranscodingRequestParcel& request,
                               const std::shared_ptr<ITranscodingClientCallback>& clientCb) {
-    queueEvent(Event::Start, clientId, jobId, [=] {
-        media_status_t err = handleStart(clientId, jobId, request, clientCb);
+    queueEvent(Event::Start, clientId, sessionId, [=] {
+        media_status_t err = handleStart(clientId, sessionId, request, clientCb);
 
         if (err != AMEDIA_OK) {
             cleanup();
-            reportError(clientId, jobId, err);
+            reportError(clientId, sessionId, err);
         } else {
             auto callback = mCallback.lock();
             if (callback != nullptr) {
-                callback->onStarted(clientId, jobId);
+                callback->onStarted(clientId, sessionId);
             }
         }
     });
 }
 
-void TranscoderWrapper::pause(ClientIdType clientId, JobIdType jobId) {
-    queueEvent(Event::Pause, clientId, jobId, [=] {
-        media_status_t err = handlePause(clientId, jobId);
+void TranscoderWrapper::pause(ClientIdType clientId, SessionIdType sessionId) {
+    queueEvent(Event::Pause, clientId, sessionId, [=] {
+        media_status_t err = handlePause(clientId, sessionId);
 
         cleanup();
 
         if (err != AMEDIA_OK) {
-            reportError(clientId, jobId, err);
+            reportError(clientId, sessionId, err);
         } else {
             auto callback = mCallback.lock();
             if (callback != nullptr) {
-                callback->onPaused(clientId, jobId);
+                callback->onPaused(clientId, sessionId);
             }
         }
     });
 }
 
-void TranscoderWrapper::resume(ClientIdType clientId, JobIdType jobId,
+void TranscoderWrapper::resume(ClientIdType clientId, SessionIdType sessionId,
                                const TranscodingRequestParcel& request,
                                const std::shared_ptr<ITranscodingClientCallback>& clientCb) {
-    queueEvent(Event::Resume, clientId, jobId, [=] {
-        media_status_t err = handleResume(clientId, jobId, request, clientCb);
+    queueEvent(Event::Resume, clientId, sessionId, [=] {
+        media_status_t err = handleResume(clientId, sessionId, request, clientCb);
 
         if (err != AMEDIA_OK) {
             cleanup();
-            reportError(clientId, jobId, err);
+            reportError(clientId, sessionId, err);
         } else {
             auto callback = mCallback.lock();
             if (callback != nullptr) {
-                callback->onResumed(clientId, jobId);
+                callback->onResumed(clientId, sessionId);
             }
         }
     });
 }
 
-void TranscoderWrapper::stop(ClientIdType clientId, JobIdType jobId) {
-    queueEvent(Event::Stop, clientId, jobId, [=] {
-        if (mTranscoder != nullptr && clientId == mCurrentClientId && jobId == mCurrentJobId) {
-            // Cancelling the currently running job.
+void TranscoderWrapper::stop(ClientIdType clientId, SessionIdType sessionId) {
+    queueEvent(Event::Stop, clientId, sessionId, [=] {
+        if (mTranscoder != nullptr && clientId == mCurrentClientId &&
+            sessionId == mCurrentSessionId) {
+            // Cancelling the currently running session.
             media_status_t err = mTranscoder->cancel();
             if (err != AMEDIA_OK) {
                 ALOGW("failed to stop transcoder: %d", err);
@@ -263,55 +265,58 @@
             }
             cleanup();
         } else {
-            // For jobs that's not currently running, release any pausedState for the job.
-            mPausedStateMap.erase(JobKeyType(clientId, jobId));
+            // For sessions that's not currently running, release any pausedState for the session.
+            mPausedStateMap.erase(SessionKeyType(clientId, sessionId));
         }
         // No callback needed for stop.
     });
 }
 
-void TranscoderWrapper::onFinish(ClientIdType clientId, JobIdType jobId) {
-    queueEvent(Event::Finish, clientId, jobId, [=] {
-        if (mTranscoder != nullptr && clientId == mCurrentClientId && jobId == mCurrentJobId) {
+void TranscoderWrapper::onFinish(ClientIdType clientId, SessionIdType sessionId) {
+    queueEvent(Event::Finish, clientId, sessionId, [=] {
+        if (mTranscoder != nullptr && clientId == mCurrentClientId &&
+            sessionId == mCurrentSessionId) {
             cleanup();
         }
 
         auto callback = mCallback.lock();
         if (callback != nullptr) {
-            callback->onFinish(clientId, jobId);
+            callback->onFinish(clientId, sessionId);
         }
     });
 }
 
-void TranscoderWrapper::onError(ClientIdType clientId, JobIdType jobId, media_status_t error) {
+void TranscoderWrapper::onError(ClientIdType clientId, SessionIdType sessionId,
+                                media_status_t error) {
     queueEvent(
-            Event::Error, clientId, jobId,
+            Event::Error, clientId, sessionId,
             [=] {
                 if (mTranscoder != nullptr && clientId == mCurrentClientId &&
-                    jobId == mCurrentJobId) {
+                    sessionId == mCurrentSessionId) {
                     cleanup();
                 }
-                reportError(clientId, jobId, error);
+                reportError(clientId, sessionId, error);
             },
             error);
 }
 
-void TranscoderWrapper::onProgress(ClientIdType clientId, JobIdType jobId, int32_t progress) {
+void TranscoderWrapper::onProgress(ClientIdType clientId, SessionIdType sessionId,
+                                   int32_t progress) {
     queueEvent(
-            Event::Progress, clientId, jobId,
+            Event::Progress, clientId, sessionId,
             [=] {
                 auto callback = mCallback.lock();
                 if (callback != nullptr) {
-                    callback->onProgressUpdate(clientId, jobId, progress);
+                    callback->onProgressUpdate(clientId, sessionId, progress);
                 }
             },
             progress);
 }
 
 media_status_t TranscoderWrapper::setupTranscoder(
-        ClientIdType clientId, JobIdType jobId, const TranscodingRequestParcel& request,
+        ClientIdType clientId, SessionIdType sessionId, const TranscodingRequestParcel& request,
         const std::shared_ptr<ITranscodingClientCallback>& clientCb,
-        const std::shared_ptr<const Parcel>& pausedState) {
+        const std::shared_ptr<ndk::ScopedAParcel>& pausedState) {
     if (clientCb == nullptr) {
         ALOGE("client callback is null");
         return AMEDIA_ERROR_INVALID_PARAMETER;
@@ -340,8 +345,8 @@
     }
 
     mCurrentClientId = clientId;
-    mCurrentJobId = jobId;
-    mTranscoderCb = std::make_shared<CallbackImpl>(shared_from_this(), clientId, jobId);
+    mCurrentSessionId = sessionId;
+    mTranscoderCb = std::make_shared<CallbackImpl>(shared_from_this(), clientId, sessionId);
     mTranscoder = MediaTranscoder::create(mTranscoderCb, pausedState);
     if (mTranscoder == nullptr) {
         ALOGE("failed to create transcoder");
@@ -389,10 +394,10 @@
 }
 
 media_status_t TranscoderWrapper::handleStart(
-        ClientIdType clientId, JobIdType jobId, const TranscodingRequestParcel& request,
+        ClientIdType clientId, SessionIdType sessionId, const TranscodingRequestParcel& request,
         const std::shared_ptr<ITranscodingClientCallback>& clientCb) {
     ALOGI("%s: setting up transcoder for start", __FUNCTION__);
-    media_status_t err = setupTranscoder(clientId, jobId, request, clientCb);
+    media_status_t err = setupTranscoder(clientId, sessionId, request, clientCb);
     if (err != AMEDIA_OK) {
         ALOGI("%s: failed to setup transcoder", __FUNCTION__);
         return err;
@@ -408,36 +413,36 @@
     return AMEDIA_OK;
 }
 
-media_status_t TranscoderWrapper::handlePause(ClientIdType clientId, JobIdType jobId) {
+media_status_t TranscoderWrapper::handlePause(ClientIdType clientId, SessionIdType sessionId) {
     if (mTranscoder == nullptr) {
         ALOGE("%s: transcoder is not running", __FUNCTION__);
         return AMEDIA_ERROR_INVALID_OPERATION;
     }
 
-    if (clientId != mCurrentClientId || jobId != mCurrentJobId) {
-        ALOGW("%s: stopping job {%lld, %d} that's not current job {%lld, %d}", __FUNCTION__,
-              (long long)clientId, jobId, (long long)mCurrentClientId, mCurrentJobId);
+    if (clientId != mCurrentClientId || sessionId != mCurrentSessionId) {
+        ALOGW("%s: stopping session {%lld, %d} that's not current session {%lld, %d}", __FUNCTION__,
+              (long long)clientId, sessionId, (long long)mCurrentClientId, mCurrentSessionId);
     }
 
     ALOGI("%s: pausing transcoder", __FUNCTION__);
 
-    std::shared_ptr<const Parcel> pauseStates;
+    std::shared_ptr<ndk::ScopedAParcel> pauseStates;
     media_status_t err = mTranscoder->pause(&pauseStates);
     if (err != AMEDIA_OK) {
         ALOGE("%s: failed to pause transcoder: %d", __FUNCTION__, err);
         return err;
     }
-    mPausedStateMap[JobKeyType(clientId, jobId)] = pauseStates;
+    mPausedStateMap[SessionKeyType(clientId, sessionId)] = pauseStates;
 
     ALOGI("%s: transcoder paused", __FUNCTION__);
     return AMEDIA_OK;
 }
 
 media_status_t TranscoderWrapper::handleResume(
-        ClientIdType clientId, JobIdType jobId, const TranscodingRequestParcel& request,
+        ClientIdType clientId, SessionIdType sessionId, const TranscodingRequestParcel& request,
         const std::shared_ptr<ITranscodingClientCallback>& clientCb) {
-    std::shared_ptr<const Parcel> pausedState;
-    auto it = mPausedStateMap.find(JobKeyType(clientId, jobId));
+    std::shared_ptr<ndk::ScopedAParcel> pausedState;
+    auto it = mPausedStateMap.find(SessionKeyType(clientId, sessionId));
     if (it != mPausedStateMap.end()) {
         pausedState = it->second;
         mPausedStateMap.erase(it);
@@ -447,7 +452,7 @@
     }
 
     ALOGI("%s: setting up transcoder for resume", __FUNCTION__);
-    media_status_t err = setupTranscoder(clientId, jobId, request, clientCb, pausedState);
+    media_status_t err = setupTranscoder(clientId, sessionId, request, clientCb, pausedState);
     if (err != AMEDIA_OK) {
         ALOGE("%s: failed to setup transcoder: %d", __FUNCTION__, err);
         return err;
@@ -465,16 +470,16 @@
 
 void TranscoderWrapper::cleanup() {
     mCurrentClientId = 0;
-    mCurrentJobId = -1;
+    mCurrentSessionId = -1;
     mTranscoderCb = nullptr;
     mTranscoder = nullptr;
 }
 
-void TranscoderWrapper::queueEvent(Event::Type type, ClientIdType clientId, JobIdType jobId,
+void TranscoderWrapper::queueEvent(Event::Type type, ClientIdType clientId, SessionIdType sessionId,
                                    const std::function<void()> runnable, int32_t arg) {
     std::scoped_lock lock{mLock};
 
-    mQueue.push_back({type, clientId, jobId, runnable, arg});
+    mQueue.push_back({type, clientId, sessionId, runnable, arg});
     mCondition.notify_one();
 }
 
diff --git a/media/libmediatranscoding/TranscodingClientManager.cpp b/media/libmediatranscoding/TranscodingClientManager.cpp
index 82a6dde..ae1f7a5 100644
--- a/media/libmediatranscoding/TranscodingClientManager.cpp
+++ b/media/libmediatranscoding/TranscodingClientManager.cpp
@@ -31,12 +31,15 @@
 
 static_assert(sizeof(ClientIdType) == sizeof(void*), "ClientIdType should be pointer-sized");
 
-static constexpr const char* MEDIA_PROVIDER_PKG_NAME = "com.google.android.providers.media.module";
+static constexpr const char* MEDIA_PROVIDER_PKG_NAMES[] = {
+        "com.android.providers.media.module",
+        "com.google.android.providers.media.module",
+};
 
 using ::aidl::android::media::BnTranscodingClient;
 using ::aidl::android::media::IMediaTranscodingService;  // For service error codes
-using ::aidl::android::media::TranscodingJobParcel;
 using ::aidl::android::media::TranscodingRequestParcel;
+using ::aidl::android::media::TranscodingSessionParcel;
 using Status = ::ndk::ScopedAStatus;
 using ::ndk::SpAIBinder;
 
@@ -74,8 +77,8 @@
     std::string mClientName;
     std::string mClientOpPackageName;
 
-    // Next jobId to assign.
-    std::atomic<int32_t> mNextJobId;
+    // Next sessionId to assign.
+    std::atomic<int32_t> mNextSessionId;
     // Whether this client has been unregistered already.
     std::atomic<bool> mAbandoned;
     // Weak pointer to the client manager for this client.
@@ -86,12 +89,13 @@
                const std::weak_ptr<TranscodingClientManager>& owner);
 
     Status submitRequest(const TranscodingRequestParcel& /*in_request*/,
-                         TranscodingJobParcel* /*out_job*/, bool* /*_aidl_return*/) override;
+                         TranscodingSessionParcel* /*out_session*/,
+                         bool* /*_aidl_return*/) override;
 
-    Status cancelJob(int32_t /*in_jobId*/, bool* /*_aidl_return*/) override;
+    Status cancelSession(int32_t /*in_sessionId*/, bool* /*_aidl_return*/) override;
 
-    Status getJobWithId(int32_t /*in_jobId*/, TranscodingJobParcel* /*out_job*/,
-                        bool* /*_aidl_return*/) override;
+    Status getSessionWithId(int32_t /*in_sessionId*/, TranscodingSessionParcel* /*out_session*/,
+                            bool* /*_aidl_return*/) override;
 
     Status unregister() override;
 };
@@ -104,12 +108,12 @@
         mClientId(sCookieCounter.fetch_add(1, std::memory_order_relaxed)),
         mClientName(clientName),
         mClientOpPackageName(opPackageName),
-        mNextJobId(0),
+        mNextSessionId(0),
         mAbandoned(false),
         mOwner(owner) {}
 
 Status TranscodingClientManager::ClientImpl::submitRequest(
-        const TranscodingRequestParcel& in_request, TranscodingJobParcel* out_job,
+        const TranscodingRequestParcel& in_request, TranscodingSessionParcel* out_session,
         bool* _aidl_return) {
     *_aidl_return = false;
 
@@ -161,23 +165,24 @@
                 in_clientPid, in_clientUid, callingUid);
     }
 
-    int32_t jobId = mNextJobId.fetch_add(1);
+    int32_t sessionId = mNextSessionId.fetch_add(1);
 
-    *_aidl_return = owner->mJobScheduler->submit(mClientId, jobId, in_clientUid, in_request,
-                                                 mClientCallback);
+    *_aidl_return = owner->mSessionController->submit(mClientId, sessionId, in_clientUid,
+                                                      in_request, mClientCallback);
 
     if (*_aidl_return) {
-        out_job->jobId = jobId;
+        out_session->sessionId = sessionId;
 
-        // TODO(chz): is some of this coming from JobScheduler?
-        *(TranscodingRequest*)&out_job->request = in_request;
-        out_job->awaitNumberOfJobs = 0;
+        // TODO(chz): is some of this coming from SessionController?
+        *(TranscodingRequest*)&out_session->request = in_request;
+        out_session->awaitNumberOfSessions = 0;
     }
 
     return Status::ok();
 }
 
-Status TranscodingClientManager::ClientImpl::cancelJob(int32_t in_jobId, bool* _aidl_return) {
+Status TranscodingClientManager::ClientImpl::cancelSession(int32_t in_sessionId,
+                                                           bool* _aidl_return) {
     *_aidl_return = false;
 
     std::shared_ptr<TranscodingClientManager> owner;
@@ -185,17 +190,17 @@
         return Status::fromServiceSpecificError(IMediaTranscodingService::ERROR_DISCONNECTED);
     }
 
-    if (in_jobId < 0) {
+    if (in_sessionId < 0) {
         return Status::ok();
     }
 
-    *_aidl_return = owner->mJobScheduler->cancel(mClientId, in_jobId);
+    *_aidl_return = owner->mSessionController->cancel(mClientId, in_sessionId);
     return Status::ok();
 }
 
-Status TranscodingClientManager::ClientImpl::getJobWithId(int32_t in_jobId,
-                                                          TranscodingJobParcel* out_job,
-                                                          bool* _aidl_return) {
+Status TranscodingClientManager::ClientImpl::getSessionWithId(int32_t in_sessionId,
+                                                              TranscodingSessionParcel* out_session,
+                                                              bool* _aidl_return) {
     *_aidl_return = false;
 
     std::shared_ptr<TranscodingClientManager> owner;
@@ -203,15 +208,16 @@
         return Status::fromServiceSpecificError(IMediaTranscodingService::ERROR_DISCONNECTED);
     }
 
-    if (in_jobId < 0) {
+    if (in_sessionId < 0) {
         return Status::ok();
     }
 
-    *_aidl_return = owner->mJobScheduler->getJob(mClientId, in_jobId, &out_job->request);
+    *_aidl_return =
+            owner->mSessionController->getSession(mClientId, in_sessionId, &out_session->request);
 
     if (*_aidl_return) {
-        out_job->jobId = in_jobId;
-        out_job->awaitNumberOfJobs = 0;
+        out_session->sessionId = in_sessionId;
+        out_session->awaitNumberOfSessions = 0;
     }
     return Status::ok();
 }
@@ -224,8 +230,8 @@
         return Status::fromServiceSpecificError(IMediaTranscodingService::ERROR_DISCONNECTED);
     }
 
-    // Use jobId == -1 to cancel all realtime jobs for this client with the scheduler.
-    owner->mJobScheduler->cancel(mClientId, -1);
+    // Use sessionId == -1 to cancel all realtime sessions for this client with the controller.
+    owner->mSessionController->cancel(mClientId, -1);
     owner->removeClient(mClientId);
 
     return Status::ok();
@@ -256,18 +262,18 @@
 }
 
 TranscodingClientManager::TranscodingClientManager(
-        const std::shared_ptr<SchedulerClientInterface>& scheduler)
+        const std::shared_ptr<ControllerClientInterface>& controller)
       : mDeathRecipient(AIBinder_DeathRecipient_new(BinderDiedCallback)),
-        mJobScheduler(scheduler),
-        mMediaProviderUid(-1) {
+        mSessionController(controller) {
     ALOGD("TranscodingClientManager started");
     uid_t mpuid;
-    if (TranscodingUidPolicy::getUidForPackage(String16(MEDIA_PROVIDER_PKG_NAME), mpuid) ==
-        NO_ERROR) {
-        ALOGI("Found MediaProvider uid: %d", mpuid);
-        mMediaProviderUid = mpuid;
-    } else {
-        ALOGW("Couldn't get uid for MediaProvider.");
+    for (const char* pkgName : MEDIA_PROVIDER_PKG_NAMES) {
+        if (TranscodingUidPolicy::getUidForPackage(String16(pkgName), mpuid) == NO_ERROR) {
+            ALOGI("Found %s's uid: %d", pkgName, mpuid);
+            mMediaProviderUid.insert(mpuid);
+        } else {
+            ALOGW("Couldn't get uid for %s.", pkgName);
+        }
     }
 }
 
@@ -300,7 +306,7 @@
 }
 
 bool TranscodingClientManager::isTrustedCallingUid(uid_t uid) {
-    if (uid > 0 && uid == mMediaProviderUid) {
+    if (uid > 0 && mMediaProviderUid.count(uid) > 0) {
         return true;
     }
 
diff --git a/media/libmediatranscoding/TranscodingJobScheduler.cpp b/media/libmediatranscoding/TranscodingJobScheduler.cpp
deleted file mode 100644
index d3a1836..0000000
--- a/media/libmediatranscoding/TranscodingJobScheduler.cpp
+++ /dev/null
@@ -1,567 +0,0 @@
-/*
- * 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 "TranscodingJobScheduler"
-
-#define VALIDATE_STATE 1
-
-#include <inttypes.h>
-#include <media/TranscodingJobScheduler.h>
-#include <media/TranscodingUidPolicy.h>
-#include <utils/Log.h>
-
-#include <utility>
-
-namespace android {
-
-static_assert((JobIdType)-1 < 0, "JobIdType should be signed");
-
-constexpr static uid_t OFFLINE_UID = -1;
-
-//static
-String8 TranscodingJobScheduler::jobToString(const JobKeyType& jobKey) {
-    return String8::format("{client:%lld, job:%d}", (long long)jobKey.first, jobKey.second);
-}
-
-//static
-const char* TranscodingJobScheduler::jobStateToString(const Job::State jobState) {
-    switch (jobState) {
-    case Job::State::NOT_STARTED:
-        return "NOT_STARTED";
-    case Job::State::RUNNING:
-        return "RUNNING";
-    case Job::State::PAUSED:
-        return "PAUSED";
-    default:
-        break;
-    }
-    return "(unknown)";
-}
-
-TranscodingJobScheduler::TranscodingJobScheduler(
-        const std::shared_ptr<TranscoderInterface>& transcoder,
-        const std::shared_ptr<UidPolicyInterface>& uidPolicy,
-        const std::shared_ptr<ResourcePolicyInterface>& resourcePolicy)
-      : mTranscoder(transcoder),
-        mUidPolicy(uidPolicy),
-        mResourcePolicy(resourcePolicy),
-        mCurrentJob(nullptr),
-        mResourceLost(false) {
-    // Only push empty offline queue initially. Realtime queues are added when requests come in.
-    mUidSortedList.push_back(OFFLINE_UID);
-    mOfflineUidIterator = mUidSortedList.begin();
-    mJobQueues.emplace(OFFLINE_UID, JobQueueType());
-}
-
-TranscodingJobScheduler::~TranscodingJobScheduler() {}
-
-void TranscodingJobScheduler::dumpAllJobs(int fd, const Vector<String16>& args __unused) {
-    String8 result;
-
-    const size_t SIZE = 256;
-    char buffer[SIZE];
-    std::scoped_lock lock{mLock};
-
-    snprintf(buffer, SIZE, "\n========== Dumping all jobs queues =========\n");
-    result.append(buffer);
-    snprintf(buffer, SIZE, "  Total num of Jobs: %zu\n", mJobMap.size());
-    result.append(buffer);
-
-    std::vector<int32_t> uids(mUidSortedList.begin(), mUidSortedList.end());
-    // Exclude last uid, which is for offline queue
-    uids.pop_back();
-    std::vector<std::string> packageNames;
-    if (TranscodingUidPolicy::getNamesForUids(uids, &packageNames)) {
-        uids.push_back(OFFLINE_UID);
-        packageNames.push_back("(offline)");
-    }
-
-    for (int32_t i = 0; i < uids.size(); i++) {
-        const uid_t uid = uids[i];
-
-        if (mJobQueues[uid].empty()) {
-            continue;
-        }
-        snprintf(buffer, SIZE, "    Uid: %d, pkg: %s\n", uid,
-                 packageNames.empty() ? "(unknown)" : packageNames[i].c_str());
-        result.append(buffer);
-        snprintf(buffer, SIZE, "      Num of jobs: %zu\n", mJobQueues[uid].size());
-        result.append(buffer);
-        for (auto& jobKey : mJobQueues[uid]) {
-            auto jobIt = mJobMap.find(jobKey);
-            if (jobIt == mJobMap.end()) {
-                snprintf(buffer, SIZE, "Failed to look up Job %s  \n", jobToString(jobKey).c_str());
-                result.append(buffer);
-                continue;
-            }
-            Job& job = jobIt->second;
-            TranscodingRequestParcel& request = job.request;
-            snprintf(buffer, SIZE, "      Job: %s, %s, %d%%\n", jobToString(jobKey).c_str(),
-                     jobStateToString(job.state), job.lastProgress);
-            result.append(buffer);
-            snprintf(buffer, SIZE, "        Src: %s\n", request.sourceFilePath.c_str());
-            result.append(buffer);
-            snprintf(buffer, SIZE, "        Dst: %s\n", request.destinationFilePath.c_str());
-            result.append(buffer);
-        }
-    }
-
-    write(fd, result.string(), result.size());
-}
-
-TranscodingJobScheduler::Job* TranscodingJobScheduler::getTopJob_l() {
-    if (mJobMap.empty()) {
-        return nullptr;
-    }
-    uid_t topUid = *mUidSortedList.begin();
-    JobKeyType topJobKey = *mJobQueues[topUid].begin();
-    return &mJobMap[topJobKey];
-}
-
-void TranscodingJobScheduler::updateCurrentJob_l() {
-    Job* topJob = getTopJob_l();
-    Job* curJob = mCurrentJob;
-    ALOGV("updateCurrentJob: topJob is %s, curJob is %s",
-          topJob == nullptr ? "null" : jobToString(topJob->key).c_str(),
-          curJob == nullptr ? "null" : jobToString(curJob->key).c_str());
-
-    // If we found a topJob that should be run, and it's not already running,
-    // take some actions to ensure it's running.
-    if (topJob != nullptr && (topJob != curJob || topJob->state != Job::RUNNING)) {
-        // If another job is currently running, pause it first.
-        if (curJob != nullptr && curJob->state == Job::RUNNING) {
-            mTranscoder->pause(curJob->key.first, curJob->key.second);
-            curJob->state = Job::PAUSED;
-        }
-        // If we are not experiencing resource loss, we can start or resume
-        // the topJob now.
-        if (!mResourceLost) {
-            if (topJob->state == Job::NOT_STARTED) {
-                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, topJob->request,
-                                    topJob->callback.lock());
-            }
-            topJob->state = Job::RUNNING;
-        }
-    }
-    mCurrentJob = topJob;
-}
-
-void TranscodingJobScheduler::removeJob_l(const JobKeyType& jobKey) {
-    ALOGV("%s: job %s", __FUNCTION__, jobToString(jobKey).c_str());
-
-    if (mJobMap.count(jobKey) == 0) {
-        ALOGE("job %s doesn't exist", jobToString(jobKey).c_str());
-        return;
-    }
-
-    // Remove job from uid's queue.
-    const uid_t uid = mJobMap[jobKey].uid;
-    JobQueueType& jobQueue = mJobQueues[uid];
-    auto it = std::find(jobQueue.begin(), jobQueue.end(), jobKey);
-    if (it == jobQueue.end()) {
-        ALOGE("couldn't find job %s in queue for uid %d", jobToString(jobKey).c_str(), uid);
-        return;
-    }
-    jobQueue.erase(it);
-
-    // If this is the last job in a real-time queue, remove this uid's queue.
-    if (uid != OFFLINE_UID && jobQueue.empty()) {
-        mUidSortedList.remove(uid);
-        mJobQueues.erase(uid);
-        mUidPolicy->unregisterMonitorUid(uid);
-
-        std::unordered_set<uid_t> topUids = mUidPolicy->getTopUids();
-        moveUidsToTop_l(topUids, false /*preserveTopUid*/);
-    }
-
-    // Clear current job.
-    if (mCurrentJob == &mJobMap[jobKey]) {
-        mCurrentJob = nullptr;
-    }
-
-    // Remove job from job map.
-    mJobMap.erase(jobKey);
-}
-
-/**
- * Moves the set of uids to the front of mUidSortedList (which is used to pick
- * the next job to run).
- *
- * This is called when 1) we received a onTopUidsChanged() callbcak from UidPolicy,
- * or 2) we removed the job queue for a uid because it becomes empty.
- *
- * In case of 1), if there are multiple uids in the set, and the current front
- * uid in mUidSortedList is still in the set, we try to keep that uid at front
- * so that current job run is not interrupted. (This is not a concern for case 2)
- * because the queue for a uid was just removed entirely.)
- */
-void TranscodingJobScheduler::moveUidsToTop_l(const std::unordered_set<uid_t>& uids,
-                                              bool preserveTopUid) {
-    // If uid set is empty, nothing to do. Do not change the queue status.
-    if (uids.empty()) {
-        return;
-    }
-
-    // Save the current top uid.
-    uid_t curTopUid = *mUidSortedList.begin();
-    bool pushCurTopToFront = false;
-    int32_t numUidsMoved = 0;
-
-    // Go through the sorted uid list once, and move the ones in top set to front.
-    for (auto it = mUidSortedList.begin(); it != mUidSortedList.end();) {
-        uid_t uid = *it;
-
-        if (uid != OFFLINE_UID && uids.count(uid) > 0) {
-            it = mUidSortedList.erase(it);
-
-            // If this is the top we're preserving, don't push it here, push
-            // it after the for-loop.
-            if (uid == curTopUid && preserveTopUid) {
-                pushCurTopToFront = true;
-            } else {
-                mUidSortedList.push_front(uid);
-            }
-
-            // If we found all uids in the set, break out.
-            if (++numUidsMoved == uids.size()) {
-                break;
-            }
-        } else {
-            ++it;
-        }
-    }
-
-    if (pushCurTopToFront) {
-        mUidSortedList.push_front(curTopUid);
-    }
-}
-
-bool TranscodingJobScheduler::submit(ClientIdType clientId, JobIdType jobId, uid_t uid,
-                                     const TranscodingRequestParcel& request,
-                                     const std::weak_ptr<ITranscodingClientCallback>& callback) {
-    JobKeyType jobKey = std::make_pair(clientId, jobId);
-
-    ALOGV("%s: job %s, uid %d, prioirty %d", __FUNCTION__, jobToString(jobKey).c_str(), uid,
-          (int32_t)request.priority);
-
-    std::scoped_lock lock{mLock};
-
-    if (mJobMap.count(jobKey) > 0) {
-        ALOGE("job %s already exists", jobToString(jobKey).c_str());
-        return false;
-    }
-
-    // TODO(chz): only support offline vs real-time for now. All kUnspecified jobs
-    // go to offline queue.
-    if (request.priority == TranscodingJobPriority::kUnspecified) {
-        uid = OFFLINE_UID;
-    }
-
-    // Add job to job map.
-    mJobMap[jobKey].key = jobKey;
-    mJobMap[jobKey].uid = uid;
-    mJobMap[jobKey].state = Job::NOT_STARTED;
-    mJobMap[jobKey].lastProgress = 0;
-    mJobMap[jobKey].request = request;
-    mJobMap[jobKey].callback = callback;
-
-    // If it's an offline job, the queue was already added in constructor.
-    // If it's a real-time jobs, check if a queue is already present for the uid,
-    // and add a new queue if needed.
-    if (uid != OFFLINE_UID) {
-        if (mJobQueues.count(uid) == 0) {
-            mUidPolicy->registerMonitorUid(uid);
-            if (mUidPolicy->isUidOnTop(uid)) {
-                mUidSortedList.push_front(uid);
-            } else {
-                // Shouldn't be submitting real-time requests from non-top app,
-                // put it in front of the offline queue.
-                mUidSortedList.insert(mOfflineUidIterator, uid);
-            }
-        } else if (uid != *mUidSortedList.begin()) {
-            if (mUidPolicy->isUidOnTop(uid)) {
-                mUidSortedList.remove(uid);
-                mUidSortedList.push_front(uid);
-            }
-        }
-    }
-    // Append this job to the uid's queue.
-    mJobQueues[uid].push_back(jobKey);
-
-    updateCurrentJob_l();
-
-    validateState_l();
-    return true;
-}
-
-bool TranscodingJobScheduler::cancel(ClientIdType clientId, JobIdType jobId) {
-    JobKeyType jobKey = std::make_pair(clientId, jobId);
-
-    ALOGV("%s: job %s", __FUNCTION__, jobToString(jobKey).c_str());
-
-    std::list<JobKeyType> jobsToRemove;
-
-    std::scoped_lock lock{mLock};
-
-    if (jobId < 0) {
-        for (auto it = mJobMap.begin(); it != mJobMap.end(); ++it) {
-            if (it->first.first == clientId && it->second.uid != OFFLINE_UID) {
-                jobsToRemove.push_back(it->first);
-            }
-        }
-    } else {
-        if (mJobMap.count(jobKey) == 0) {
-            ALOGE("job %s doesn't exist", jobToString(jobKey).c_str());
-            return false;
-        }
-        jobsToRemove.push_back(jobKey);
-    }
-
-    for (auto it = jobsToRemove.begin(); it != jobsToRemove.end(); ++it) {
-        // If the job has ever been started, stop it now.
-        // Note that stop() is needed even if the job is currently paused. This instructs
-        // the transcoder to discard any states for the job, otherwise the states may
-        // never be discarded.
-        if (mJobMap[*it].state != Job::NOT_STARTED) {
-            mTranscoder->stop(it->first, it->second);
-        }
-
-        // Remove the job.
-        removeJob_l(*it);
-    }
-
-    // Start next job.
-    updateCurrentJob_l();
-
-    validateState_l();
-    return true;
-}
-
-bool TranscodingJobScheduler::getJob(ClientIdType clientId, JobIdType jobId,
-                                     TranscodingRequestParcel* request) {
-    JobKeyType jobKey = std::make_pair(clientId, jobId);
-
-    std::scoped_lock lock{mLock};
-
-    if (mJobMap.count(jobKey) == 0) {
-        ALOGE("job %s doesn't exist", jobToString(jobKey).c_str());
-        return false;
-    }
-
-    *(TranscodingRequest*)request = mJobMap[jobKey].request;
-    return true;
-}
-
-void TranscodingJobScheduler::notifyClient(ClientIdType clientId, JobIdType jobId,
-                                           const char* reason,
-                                           std::function<void(const JobKeyType&)> func) {
-    JobKeyType jobKey = std::make_pair(clientId, jobId);
-
-    std::scoped_lock lock{mLock};
-
-    if (mJobMap.count(jobKey) == 0) {
-        ALOGW("%s: ignoring %s for job %s that doesn't exist", __FUNCTION__, reason,
-              jobToString(jobKey).c_str());
-        return;
-    }
-
-    // Only ignore if job was never started. In particular, propagate the status
-    // to client if the job is paused. Transcoder could have posted finish when
-    // we're pausing it, and the finish arrived after we changed current job.
-    if (mJobMap[jobKey].state == Job::NOT_STARTED) {
-        ALOGW("%s: ignoring %s for job %s that was never started", __FUNCTION__, reason,
-              jobToString(jobKey).c_str());
-        return;
-    }
-
-    ALOGV("%s: job %s %s", __FUNCTION__, jobToString(jobKey).c_str(), reason);
-    func(jobKey);
-}
-
-void TranscodingJobScheduler::onStarted(ClientIdType clientId, JobIdType jobId) {
-    notifyClient(clientId, jobId, "started", [=](const JobKeyType& jobKey) {
-        auto callback = mJobMap[jobKey].callback.lock();
-        if (callback != nullptr) {
-            callback->onTranscodingStarted(jobId);
-        }
-    });
-}
-
-void TranscodingJobScheduler::onPaused(ClientIdType clientId, JobIdType jobId) {
-    notifyClient(clientId, jobId, "paused", [=](const JobKeyType& jobKey) {
-        auto callback = mJobMap[jobKey].callback.lock();
-        if (callback != nullptr) {
-            callback->onTranscodingPaused(jobId);
-        }
-    });
-}
-
-void TranscodingJobScheduler::onResumed(ClientIdType clientId, JobIdType jobId) {
-    notifyClient(clientId, jobId, "resumed", [=](const JobKeyType& jobKey) {
-        auto callback = mJobMap[jobKey].callback.lock();
-        if (callback != nullptr) {
-            callback->onTranscodingResumed(jobId);
-        }
-    });
-}
-
-void TranscodingJobScheduler::onFinish(ClientIdType clientId, JobIdType jobId) {
-    notifyClient(clientId, jobId, "finish", [=](const JobKeyType& jobKey) {
-        {
-            auto clientCallback = mJobMap[jobKey].callback.lock();
-            if (clientCallback != nullptr) {
-                clientCallback->onTranscodingFinished(
-                        jobId, TranscodingResultParcel({jobId, -1 /*actualBitrateBps*/,
-                                                        std::nullopt /*jobStats*/}));
-            }
-        }
-
-        // Remove the job.
-        removeJob_l(jobKey);
-
-        // Start next job.
-        updateCurrentJob_l();
-
-        validateState_l();
-    });
-}
-
-void TranscodingJobScheduler::onError(ClientIdType clientId, JobIdType jobId,
-                                      TranscodingErrorCode err) {
-    notifyClient(clientId, jobId, "error", [=](const JobKeyType& jobKey) {
-        {
-            auto clientCallback = mJobMap[jobKey].callback.lock();
-            if (clientCallback != nullptr) {
-                clientCallback->onTranscodingFailed(jobId, err);
-            }
-        }
-
-        // Remove the job.
-        removeJob_l(jobKey);
-
-        // Start next job.
-        updateCurrentJob_l();
-
-        validateState_l();
-    });
-}
-
-void TranscodingJobScheduler::onProgressUpdate(ClientIdType clientId, JobIdType jobId,
-                                               int32_t progress) {
-    notifyClient(clientId, jobId, "progress", [=](const JobKeyType& jobKey) {
-        auto callback = mJobMap[jobKey].callback.lock();
-        if (callback != nullptr) {
-            callback->onProgressUpdate(jobId, progress);
-        }
-        mJobMap[jobKey].lastProgress = progress;
-    });
-}
-
-void TranscodingJobScheduler::onResourceLost() {
-    ALOGI("%s", __FUNCTION__);
-
-    std::scoped_lock lock{mLock};
-
-    if (mResourceLost) {
-        return;
-    }
-
-    // If we receive a resource loss event, the TranscoderLibrary already paused
-    // the transcoding, so we don't need to call onPaused to notify it to pause.
-    // Only need to update the job state here.
-    if (mCurrentJob != nullptr && mCurrentJob->state == Job::RUNNING) {
-        mCurrentJob->state = Job::PAUSED;
-        // Notify the client as a paused event.
-        auto clientCallback = mCurrentJob->callback.lock();
-        if (clientCallback != nullptr) {
-            clientCallback->onTranscodingPaused(mCurrentJob->key.second);
-        }
-    }
-    mResourceLost = true;
-
-    validateState_l();
-}
-
-void TranscodingJobScheduler::onTopUidsChanged(const std::unordered_set<uid_t>& uids) {
-    if (uids.empty()) {
-        ALOGW("%s: ignoring empty uids", __FUNCTION__);
-        return;
-    }
-
-    std::string uidStr;
-    for (auto it = uids.begin(); it != uids.end(); it++) {
-        if (!uidStr.empty()) {
-            uidStr += ", ";
-        }
-        uidStr += std::to_string(*it);
-    }
-
-    ALOGD("%s: topUids: size %zu, uids: %s", __FUNCTION__, uids.size(), uidStr.c_str());
-
-    std::scoped_lock lock{mLock};
-
-    moveUidsToTop_l(uids, true /*preserveTopUid*/);
-
-    updateCurrentJob_l();
-
-    validateState_l();
-}
-
-void TranscodingJobScheduler::onResourceAvailable() {
-    std::scoped_lock lock{mLock};
-
-    if (!mResourceLost) {
-        return;
-    }
-
-    ALOGI("%s", __FUNCTION__);
-
-    mResourceLost = false;
-    updateCurrentJob_l();
-
-    validateState_l();
-}
-
-void TranscodingJobScheduler::validateState_l() {
-#ifdef VALIDATE_STATE
-    LOG_ALWAYS_FATAL_IF(mJobQueues.count(OFFLINE_UID) != 1,
-                        "mJobQueues offline queue number is not 1");
-    LOG_ALWAYS_FATAL_IF(*mOfflineUidIterator != OFFLINE_UID,
-                        "mOfflineUidIterator not pointing to offline uid");
-    LOG_ALWAYS_FATAL_IF(mUidSortedList.size() != mJobQueues.size(),
-                        "mUidList and mJobQueues size mismatch");
-
-    int32_t totalJobs = 0;
-    for (auto uid : mUidSortedList) {
-        LOG_ALWAYS_FATAL_IF(mJobQueues.count(uid) != 1, "mJobQueues count for uid %d is not 1",
-                            uid);
-        for (auto& jobKey : mJobQueues[uid]) {
-            LOG_ALWAYS_FATAL_IF(mJobMap.count(jobKey) != 1, "mJobs count for job %s is not 1",
-                                jobToString(jobKey).c_str());
-        }
-
-        totalJobs += mJobQueues[uid].size();
-    }
-    LOG_ALWAYS_FATAL_IF(mJobMap.size() != totalJobs,
-                        "mJobs size doesn't match total jobs counted from uid queues");
-#endif  // VALIDATE_STATE
-}
-
-}  // namespace android
diff --git a/media/libmediatranscoding/TranscodingSessionController.cpp b/media/libmediatranscoding/TranscodingSessionController.cpp
new file mode 100644
index 0000000..1c3ee7e
--- /dev/null
+++ b/media/libmediatranscoding/TranscodingSessionController.cpp
@@ -0,0 +1,579 @@
+/*
+ * 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 "TranscodingSessionController"
+
+#define VALIDATE_STATE 1
+
+#include <inttypes.h>
+#include <media/TranscodingSessionController.h>
+#include <media/TranscodingUidPolicy.h>
+#include <utils/Log.h>
+
+#include <utility>
+
+namespace android {
+
+static_assert((SessionIdType)-1 < 0, "SessionIdType should be signed");
+
+constexpr static uid_t OFFLINE_UID = -1;
+
+//static
+String8 TranscodingSessionController::sessionToString(const SessionKeyType& sessionKey) {
+    return String8::format("{client:%lld, session:%d}", (long long)sessionKey.first,
+                           sessionKey.second);
+}
+
+//static
+const char* TranscodingSessionController::sessionStateToString(const Session::State sessionState) {
+    switch (sessionState) {
+    case Session::State::NOT_STARTED:
+        return "NOT_STARTED";
+    case Session::State::RUNNING:
+        return "RUNNING";
+    case Session::State::PAUSED:
+        return "PAUSED";
+    default:
+        break;
+    }
+    return "(unknown)";
+}
+
+TranscodingSessionController::TranscodingSessionController(
+        const std::shared_ptr<TranscoderInterface>& transcoder,
+        const std::shared_ptr<UidPolicyInterface>& uidPolicy,
+        const std::shared_ptr<ResourcePolicyInterface>& resourcePolicy)
+      : mTranscoder(transcoder),
+        mUidPolicy(uidPolicy),
+        mResourcePolicy(resourcePolicy),
+        mCurrentSession(nullptr),
+        mResourceLost(false) {
+    // Only push empty offline queue initially. Realtime queues are added when requests come in.
+    mUidSortedList.push_back(OFFLINE_UID);
+    mOfflineUidIterator = mUidSortedList.begin();
+    mSessionQueues.emplace(OFFLINE_UID, SessionQueueType());
+    mUidPackageNames[OFFLINE_UID] = "(offline)";
+}
+
+TranscodingSessionController::~TranscodingSessionController() {}
+
+void TranscodingSessionController::dumpAllSessions(int fd, const Vector<String16>& args __unused) {
+    String8 result;
+
+    const size_t SIZE = 256;
+    char buffer[SIZE];
+    std::scoped_lock lock{mLock};
+
+    snprintf(buffer, SIZE, "\n========== Dumping all sessions queues =========\n");
+    result.append(buffer);
+    snprintf(buffer, SIZE, "  Total num of Sessions: %zu\n", mSessionMap.size());
+    result.append(buffer);
+
+    std::vector<int32_t> uids(mUidSortedList.begin(), mUidSortedList.end());
+
+    for (int32_t i = 0; i < uids.size(); i++) {
+        const uid_t uid = uids[i];
+
+        if (mSessionQueues[uid].empty()) {
+            continue;
+        }
+        snprintf(buffer, SIZE, "    Uid: %d, pkg: %s\n", uid,
+                 mUidPackageNames.count(uid) > 0 ? mUidPackageNames[uid].c_str() : "(unknown)");
+        result.append(buffer);
+        snprintf(buffer, SIZE, "      Num of sessions: %zu\n", mSessionQueues[uid].size());
+        result.append(buffer);
+        for (auto& sessionKey : mSessionQueues[uid]) {
+            auto sessionIt = mSessionMap.find(sessionKey);
+            if (sessionIt == mSessionMap.end()) {
+                snprintf(buffer, SIZE, "Failed to look up Session %s  \n",
+                         sessionToString(sessionKey).c_str());
+                result.append(buffer);
+                continue;
+            }
+            Session& session = sessionIt->second;
+            TranscodingRequestParcel& request = session.request;
+            snprintf(buffer, SIZE, "      Session: %s, %s, %d%%\n",
+                     sessionToString(sessionKey).c_str(), sessionStateToString(session.state),
+                     session.lastProgress);
+            result.append(buffer);
+            snprintf(buffer, SIZE, "        Src: %s\n", request.sourceFilePath.c_str());
+            result.append(buffer);
+            snprintf(buffer, SIZE, "        Dst: %s\n", request.destinationFilePath.c_str());
+            result.append(buffer);
+            // For the offline queue, print out the original client.
+            if (uid == OFFLINE_UID) {
+                snprintf(buffer, SIZE, "        Original Client: %s\n",
+                         request.clientPackageName.c_str());
+                result.append(buffer);
+            }
+        }
+    }
+
+    write(fd, result.string(), result.size());
+}
+
+TranscodingSessionController::Session* TranscodingSessionController::getTopSession_l() {
+    if (mSessionMap.empty()) {
+        return nullptr;
+    }
+    uid_t topUid = *mUidSortedList.begin();
+    SessionKeyType topSessionKey = *mSessionQueues[topUid].begin();
+    return &mSessionMap[topSessionKey];
+}
+
+void TranscodingSessionController::updateCurrentSession_l() {
+    Session* topSession = getTopSession_l();
+    Session* curSession = mCurrentSession;
+    ALOGV("updateCurrentSession: topSession is %s, curSession is %s",
+          topSession == nullptr ? "null" : sessionToString(topSession->key).c_str(),
+          curSession == nullptr ? "null" : sessionToString(curSession->key).c_str());
+
+    // If we found a topSession that should be run, and it's not already running,
+    // take some actions to ensure it's running.
+    if (topSession != nullptr &&
+        (topSession != curSession || topSession->state != Session::RUNNING)) {
+        // If another session is currently running, pause it first.
+        if (curSession != nullptr && curSession->state == Session::RUNNING) {
+            mTranscoder->pause(curSession->key.first, curSession->key.second);
+            curSession->state = Session::PAUSED;
+        }
+        // If we are not experiencing resource loss, we can start or resume
+        // the topSession now.
+        if (!mResourceLost) {
+            if (topSession->state == Session::NOT_STARTED) {
+                mTranscoder->start(topSession->key.first, topSession->key.second,
+                                   topSession->request, topSession->callback.lock());
+            } else if (topSession->state == Session::PAUSED) {
+                mTranscoder->resume(topSession->key.first, topSession->key.second,
+                                    topSession->request, topSession->callback.lock());
+            }
+            topSession->state = Session::RUNNING;
+        }
+    }
+    mCurrentSession = topSession;
+}
+
+void TranscodingSessionController::removeSession_l(const SessionKeyType& sessionKey) {
+    ALOGV("%s: session %s", __FUNCTION__, sessionToString(sessionKey).c_str());
+
+    if (mSessionMap.count(sessionKey) == 0) {
+        ALOGE("session %s doesn't exist", sessionToString(sessionKey).c_str());
+        return;
+    }
+
+    // Remove session from uid's queue.
+    const uid_t uid = mSessionMap[sessionKey].uid;
+    SessionQueueType& sessionQueue = mSessionQueues[uid];
+    auto it = std::find(sessionQueue.begin(), sessionQueue.end(), sessionKey);
+    if (it == sessionQueue.end()) {
+        ALOGE("couldn't find session %s in queue for uid %d", sessionToString(sessionKey).c_str(),
+              uid);
+        return;
+    }
+    sessionQueue.erase(it);
+
+    // If this is the last session in a real-time queue, remove this uid's queue.
+    if (uid != OFFLINE_UID && sessionQueue.empty()) {
+        mUidSortedList.remove(uid);
+        mSessionQueues.erase(uid);
+        mUidPolicy->unregisterMonitorUid(uid);
+
+        std::unordered_set<uid_t> topUids = mUidPolicy->getTopUids();
+        moveUidsToTop_l(topUids, false /*preserveTopUid*/);
+    }
+
+    // Clear current session.
+    if (mCurrentSession == &mSessionMap[sessionKey]) {
+        mCurrentSession = nullptr;
+    }
+
+    // Remove session from session map.
+    mSessionMap.erase(sessionKey);
+}
+
+/**
+ * Moves the set of uids to the front of mUidSortedList (which is used to pick
+ * the next session to run).
+ *
+ * This is called when 1) we received a onTopUidsChanged() callback from UidPolicy,
+ * or 2) we removed the session queue for a uid because it becomes empty.
+ *
+ * In case of 1), if there are multiple uids in the set, and the current front
+ * uid in mUidSortedList is still in the set, we try to keep that uid at front
+ * so that current session run is not interrupted. (This is not a concern for case 2)
+ * because the queue for a uid was just removed entirely.)
+ */
+void TranscodingSessionController::moveUidsToTop_l(const std::unordered_set<uid_t>& uids,
+                                                   bool preserveTopUid) {
+    // If uid set is empty, nothing to do. Do not change the queue status.
+    if (uids.empty()) {
+        return;
+    }
+
+    // Save the current top uid.
+    uid_t curTopUid = *mUidSortedList.begin();
+    bool pushCurTopToFront = false;
+    int32_t numUidsMoved = 0;
+
+    // Go through the sorted uid list once, and move the ones in top set to front.
+    for (auto it = mUidSortedList.begin(); it != mUidSortedList.end();) {
+        uid_t uid = *it;
+
+        if (uid != OFFLINE_UID && uids.count(uid) > 0) {
+            it = mUidSortedList.erase(it);
+
+            // If this is the top we're preserving, don't push it here, push
+            // it after the for-loop.
+            if (uid == curTopUid && preserveTopUid) {
+                pushCurTopToFront = true;
+            } else {
+                mUidSortedList.push_front(uid);
+            }
+
+            // If we found all uids in the set, break out.
+            if (++numUidsMoved == uids.size()) {
+                break;
+            }
+        } else {
+            ++it;
+        }
+    }
+
+    if (pushCurTopToFront) {
+        mUidSortedList.push_front(curTopUid);
+    }
+}
+
+bool TranscodingSessionController::submit(
+        ClientIdType clientId, SessionIdType sessionId, uid_t uid,
+        const TranscodingRequestParcel& request,
+        const std::weak_ptr<ITranscodingClientCallback>& callback) {
+    SessionKeyType sessionKey = std::make_pair(clientId, sessionId);
+
+    ALOGV("%s: session %s, uid %d, prioirty %d", __FUNCTION__, sessionToString(sessionKey).c_str(),
+          uid, (int32_t)request.priority);
+
+    std::scoped_lock lock{mLock};
+
+    if (mSessionMap.count(sessionKey) > 0) {
+        ALOGE("session %s already exists", sessionToString(sessionKey).c_str());
+        return false;
+    }
+
+    // Add the uid package name to the store of package names we already know.
+    if (mUidPackageNames.count(uid) == 0) {
+        mUidPackageNames.emplace(uid, request.clientPackageName);
+    }
+
+    // TODO(chz): only support offline vs real-time for now. All kUnspecified sessions
+    // go to offline queue.
+    if (request.priority == TranscodingSessionPriority::kUnspecified) {
+        uid = OFFLINE_UID;
+    }
+
+    // Add session to session map.
+    mSessionMap[sessionKey].key = sessionKey;
+    mSessionMap[sessionKey].uid = uid;
+    mSessionMap[sessionKey].state = Session::NOT_STARTED;
+    mSessionMap[sessionKey].lastProgress = 0;
+    mSessionMap[sessionKey].request = request;
+    mSessionMap[sessionKey].callback = callback;
+
+    // If it's an offline session, the queue was already added in constructor.
+    // If it's a real-time sessions, check if a queue is already present for the uid,
+    // and add a new queue if needed.
+    if (uid != OFFLINE_UID) {
+        if (mSessionQueues.count(uid) == 0) {
+            mUidPolicy->registerMonitorUid(uid);
+            if (mUidPolicy->isUidOnTop(uid)) {
+                mUidSortedList.push_front(uid);
+            } else {
+                // Shouldn't be submitting real-time requests from non-top app,
+                // put it in front of the offline queue.
+                mUidSortedList.insert(mOfflineUidIterator, uid);
+            }
+        } else if (uid != *mUidSortedList.begin()) {
+            if (mUidPolicy->isUidOnTop(uid)) {
+                mUidSortedList.remove(uid);
+                mUidSortedList.push_front(uid);
+            }
+        }
+    }
+    // Append this session to the uid's queue.
+    mSessionQueues[uid].push_back(sessionKey);
+
+    updateCurrentSession_l();
+
+    validateState_l();
+    return true;
+}
+
+bool TranscodingSessionController::cancel(ClientIdType clientId, SessionIdType sessionId) {
+    SessionKeyType sessionKey = std::make_pair(clientId, sessionId);
+
+    ALOGV("%s: session %s", __FUNCTION__, sessionToString(sessionKey).c_str());
+
+    std::list<SessionKeyType> sessionsToRemove;
+
+    std::scoped_lock lock{mLock};
+
+    if (sessionId < 0) {
+        for (auto it = mSessionMap.begin(); it != mSessionMap.end(); ++it) {
+            if (it->first.first == clientId && it->second.uid != OFFLINE_UID) {
+                sessionsToRemove.push_back(it->first);
+            }
+        }
+    } else {
+        if (mSessionMap.count(sessionKey) == 0) {
+            ALOGE("session %s doesn't exist", sessionToString(sessionKey).c_str());
+            return false;
+        }
+        sessionsToRemove.push_back(sessionKey);
+    }
+
+    for (auto it = sessionsToRemove.begin(); it != sessionsToRemove.end(); ++it) {
+        // If the session has ever been started, stop it now.
+        // Note that stop() is needed even if the session is currently paused. This instructs
+        // the transcoder to discard any states for the session, otherwise the states may
+        // never be discarded.
+        if (mSessionMap[*it].state != Session::NOT_STARTED) {
+            mTranscoder->stop(it->first, it->second);
+        }
+
+        // Remove the session.
+        removeSession_l(*it);
+    }
+
+    // Start next session.
+    updateCurrentSession_l();
+
+    validateState_l();
+    return true;
+}
+
+bool TranscodingSessionController::getSession(ClientIdType clientId, SessionIdType sessionId,
+                                              TranscodingRequestParcel* request) {
+    SessionKeyType sessionKey = std::make_pair(clientId, sessionId);
+
+    std::scoped_lock lock{mLock};
+
+    if (mSessionMap.count(sessionKey) == 0) {
+        ALOGE("session %s doesn't exist", sessionToString(sessionKey).c_str());
+        return false;
+    }
+
+    *(TranscodingRequest*)request = mSessionMap[sessionKey].request;
+    return true;
+}
+
+void TranscodingSessionController::notifyClient(ClientIdType clientId, SessionIdType sessionId,
+                                                const char* reason,
+                                                std::function<void(const SessionKeyType&)> func) {
+    SessionKeyType sessionKey = std::make_pair(clientId, sessionId);
+
+    std::scoped_lock lock{mLock};
+
+    if (mSessionMap.count(sessionKey) == 0) {
+        ALOGW("%s: ignoring %s for session %s that doesn't exist", __FUNCTION__, reason,
+              sessionToString(sessionKey).c_str());
+        return;
+    }
+
+    // Only ignore if session was never started. In particular, propagate the status
+    // to client if the session is paused. Transcoder could have posted finish when
+    // we're pausing it, and the finish arrived after we changed current session.
+    if (mSessionMap[sessionKey].state == Session::NOT_STARTED) {
+        ALOGW("%s: ignoring %s for session %s that was never started", __FUNCTION__, reason,
+              sessionToString(sessionKey).c_str());
+        return;
+    }
+
+    ALOGV("%s: session %s %s", __FUNCTION__, sessionToString(sessionKey).c_str(), reason);
+    func(sessionKey);
+}
+
+void TranscodingSessionController::onStarted(ClientIdType clientId, SessionIdType sessionId) {
+    notifyClient(clientId, sessionId, "started", [=](const SessionKeyType& sessionKey) {
+        auto callback = mSessionMap[sessionKey].callback.lock();
+        if (callback != nullptr) {
+            callback->onTranscodingStarted(sessionId);
+        }
+    });
+}
+
+void TranscodingSessionController::onPaused(ClientIdType clientId, SessionIdType sessionId) {
+    notifyClient(clientId, sessionId, "paused", [=](const SessionKeyType& sessionKey) {
+        auto callback = mSessionMap[sessionKey].callback.lock();
+        if (callback != nullptr) {
+            callback->onTranscodingPaused(sessionId);
+        }
+    });
+}
+
+void TranscodingSessionController::onResumed(ClientIdType clientId, SessionIdType sessionId) {
+    notifyClient(clientId, sessionId, "resumed", [=](const SessionKeyType& sessionKey) {
+        auto callback = mSessionMap[sessionKey].callback.lock();
+        if (callback != nullptr) {
+            callback->onTranscodingResumed(sessionId);
+        }
+    });
+}
+
+void TranscodingSessionController::onFinish(ClientIdType clientId, SessionIdType sessionId) {
+    notifyClient(clientId, sessionId, "finish", [=](const SessionKeyType& sessionKey) {
+        {
+            auto clientCallback = mSessionMap[sessionKey].callback.lock();
+            if (clientCallback != nullptr) {
+                clientCallback->onTranscodingFinished(
+                        sessionId, TranscodingResultParcel({sessionId, -1 /*actualBitrateBps*/,
+                                                            std::nullopt /*sessionStats*/}));
+            }
+        }
+
+        // Remove the session.
+        removeSession_l(sessionKey);
+
+        // Start next session.
+        updateCurrentSession_l();
+
+        validateState_l();
+    });
+}
+
+void TranscodingSessionController::onError(ClientIdType clientId, SessionIdType sessionId,
+                                           TranscodingErrorCode err) {
+    notifyClient(clientId, sessionId, "error", [=](const SessionKeyType& sessionKey) {
+        {
+            auto clientCallback = mSessionMap[sessionKey].callback.lock();
+            if (clientCallback != nullptr) {
+                clientCallback->onTranscodingFailed(sessionId, err);
+            }
+        }
+
+        // Remove the session.
+        removeSession_l(sessionKey);
+
+        // Start next session.
+        updateCurrentSession_l();
+
+        validateState_l();
+    });
+}
+
+void TranscodingSessionController::onProgressUpdate(ClientIdType clientId, SessionIdType sessionId,
+                                                    int32_t progress) {
+    notifyClient(clientId, sessionId, "progress", [=](const SessionKeyType& sessionKey) {
+        auto callback = mSessionMap[sessionKey].callback.lock();
+        if (callback != nullptr) {
+            callback->onProgressUpdate(sessionId, progress);
+        }
+        mSessionMap[sessionKey].lastProgress = progress;
+    });
+}
+
+void TranscodingSessionController::onResourceLost() {
+    ALOGI("%s", __FUNCTION__);
+
+    std::scoped_lock lock{mLock};
+
+    if (mResourceLost) {
+        return;
+    }
+
+    // If we receive a resource loss event, the TranscoderLibrary already paused
+    // the transcoding, so we don't need to call onPaused to notify it to pause.
+    // Only need to update the session state here.
+    if (mCurrentSession != nullptr && mCurrentSession->state == Session::RUNNING) {
+        mCurrentSession->state = Session::PAUSED;
+        // Notify the client as a paused event.
+        auto clientCallback = mCurrentSession->callback.lock();
+        if (clientCallback != nullptr) {
+            clientCallback->onTranscodingPaused(mCurrentSession->key.second);
+        }
+    }
+    mResourceLost = true;
+
+    validateState_l();
+}
+
+void TranscodingSessionController::onTopUidsChanged(const std::unordered_set<uid_t>& uids) {
+    if (uids.empty()) {
+        ALOGW("%s: ignoring empty uids", __FUNCTION__);
+        return;
+    }
+
+    std::string uidStr;
+    for (auto it = uids.begin(); it != uids.end(); it++) {
+        if (!uidStr.empty()) {
+            uidStr += ", ";
+        }
+        uidStr += std::to_string(*it);
+    }
+
+    ALOGD("%s: topUids: size %zu, uids: %s", __FUNCTION__, uids.size(), uidStr.c_str());
+
+    std::scoped_lock lock{mLock};
+
+    moveUidsToTop_l(uids, true /*preserveTopUid*/);
+
+    updateCurrentSession_l();
+
+    validateState_l();
+}
+
+void TranscodingSessionController::onResourceAvailable() {
+    std::scoped_lock lock{mLock};
+
+    if (!mResourceLost) {
+        return;
+    }
+
+    ALOGI("%s", __FUNCTION__);
+
+    mResourceLost = false;
+    updateCurrentSession_l();
+
+    validateState_l();
+}
+
+void TranscodingSessionController::validateState_l() {
+#ifdef VALIDATE_STATE
+    LOG_ALWAYS_FATAL_IF(mSessionQueues.count(OFFLINE_UID) != 1,
+                        "mSessionQueues offline queue number is not 1");
+    LOG_ALWAYS_FATAL_IF(*mOfflineUidIterator != OFFLINE_UID,
+                        "mOfflineUidIterator not pointing to offline uid");
+    LOG_ALWAYS_FATAL_IF(mUidSortedList.size() != mSessionQueues.size(),
+                        "mUidList and mSessionQueues size mismatch");
+
+    int32_t totalSessions = 0;
+    for (auto uid : mUidSortedList) {
+        LOG_ALWAYS_FATAL_IF(mSessionQueues.count(uid) != 1,
+                            "mSessionQueues count for uid %d is not 1", uid);
+        for (auto& sessionKey : mSessionQueues[uid]) {
+            LOG_ALWAYS_FATAL_IF(mSessionMap.count(sessionKey) != 1,
+                                "mSessions count for session %s is not 1",
+                                sessionToString(sessionKey).c_str());
+        }
+
+        totalSessions += mSessionQueues[uid].size();
+    }
+    LOG_ALWAYS_FATAL_IF(mSessionMap.size() != totalSessions,
+                        "mSessions size doesn't match total sessions counted from uid queues");
+#endif  // VALIDATE_STATE
+}
+
+}  // namespace android
diff --git a/media/libmediatranscoding/TranscodingUidPolicy.cpp b/media/libmediatranscoding/TranscodingUidPolicy.cpp
index 9763921..084a871 100644
--- a/media/libmediatranscoding/TranscodingUidPolicy.cpp
+++ b/media/libmediatranscoding/TranscodingUidPolicy.cpp
@@ -114,28 +114,6 @@
 ////////////////////////////////////////////////////////////////////////////
 
 //static
-bool TranscodingUidPolicy::getNamesForUids(const std::vector<int32_t>& uids,
-                                           std::vector<std::string>* names) {
-    names->clear();
-    sp<IServiceManager> sm(defaultServiceManager());
-    sp<IBinder> binder(sm->getService(String16("package_native")));
-    if (binder == nullptr) {
-        ALOGE("getService package_native failed");
-        return false;
-    }
-
-    sp<content::pm::IPackageManagerNative> packageMgr =
-            interface_cast<content::pm::IPackageManagerNative>(binder);
-    binder::Status status = packageMgr->getNamesForUids(uids, names);
-
-    if (!status.isOk() || names->size() != uids.size()) {
-        names->clear();
-        return false;
-    }
-    return true;
-}
-
-//static
 status_t TranscodingUidPolicy::getUidForPackage(String16 packageName, /*inout*/ uid_t& uid) {
     PermissionController pc;
     uid = pc.getPackageUid(packageName, 0);
diff --git a/media/libmediatranscoding/aidl/android/media/IMediaTranscodingService.aidl b/media/libmediatranscoding/aidl/android/media/IMediaTranscodingService.aidl
index 7fc7748..ad2358e 100644
--- a/media/libmediatranscoding/aidl/android/media/IMediaTranscodingService.aidl
+++ b/media/libmediatranscoding/aidl/android/media/IMediaTranscodingService.aidl
@@ -18,7 +18,7 @@
 
 import android.media.ITranscodingClient;
 import android.media.ITranscodingClientCallback;
-import android.media.TranscodingJobParcel;
+import android.media.TranscodingSessionParcel;
 import android.media.TranscodingRequestParcel;
 
 /**
diff --git a/media/libmediatranscoding/aidl/android/media/ITranscodingClient.aidl b/media/libmediatranscoding/aidl/android/media/ITranscodingClient.aidl
index 37b5147..151e3d0 100644
--- a/media/libmediatranscoding/aidl/android/media/ITranscodingClient.aidl
+++ b/media/libmediatranscoding/aidl/android/media/ITranscodingClient.aidl
@@ -16,7 +16,7 @@
 
 package android.media;
 
-import android.media.TranscodingJobParcel;
+import android.media.TranscodingSessionParcel;
 import android.media.TranscodingRequestParcel;
 
 /**
@@ -31,28 +31,28 @@
      * Submits a transcoding request to MediaTranscodingService.
      *
      * @param request a TranscodingRequest contains transcoding configuration.
-     * @param job(output variable) a TranscodingJob generated by the MediaTranscodingService.
+     * @param session(output variable) a TranscodingSession generated by MediaTranscodingService.
      * @return true if success, false otherwise.
      */
     boolean submitRequest(in TranscodingRequestParcel request,
-                          out TranscodingJobParcel job);
+                          out TranscodingSessionParcel session);
 
     /**
-     * Cancels a transcoding job.
+     * Cancels a transcoding session.
      *
-     * @param jobId a TranscodingJob generated by the MediaTranscodingService.
+     * @param sessionId a TranscodingSession generated by the MediaTranscodingService.
      * @return true if succeeds, false otherwise.
      */
-    boolean cancelJob(in int jobId);
+    boolean cancelSession(in int sessionId);
 
     /**
-     * Queries the job detail associated with a jobId.
+     * Queries the session detail associated with a sessionId.
      *
-     * @param jobId a TranscodingJob generated by the MediaTranscodingService.
-     * @param job(output variable) the TranscodingJob associated with the jobId.
+     * @param sessionId a TranscodingSession generated by the MediaTranscodingService.
+     * @param session(output variable) the TranscodingSession associated with the sessionId.
      * @return true if succeeds, false otherwise.
      */
-    boolean getJobWithId(in int jobId, out TranscodingJobParcel job);
+    boolean getSessionWithId(in int sessionId, out TranscodingSessionParcel session);
 
     /**
     * Unregister the client with the MediaTranscodingService.
diff --git a/media/libmediatranscoding/aidl/android/media/ITranscodingClientCallback.aidl b/media/libmediatranscoding/aidl/android/media/ITranscodingClientCallback.aidl
index 73edb95..d7d9b6f 100644
--- a/media/libmediatranscoding/aidl/android/media/ITranscodingClientCallback.aidl
+++ b/media/libmediatranscoding/aidl/android/media/ITranscodingClientCallback.aidl
@@ -17,7 +17,7 @@
 package android.media;
 
 import android.media.TranscodingErrorCode;
-import android.media.TranscodingJobParcel;
+import android.media.TranscodingSessionParcel;
 import android.media.TranscodingResultParcel;
 import android.os.ParcelFileDescriptor;
 
@@ -40,68 +40,68 @@
                                             in @utf8InCpp String mode);
 
     /**
-    * Called when the transcoding associated with the jobId finished.
-    * This will only be called if client request to get all the status of the job.
+    * Called when the transcoding associated with the sessionId finished.
+    * This will only be called if client request to get all the status of the session.
     *
-    * @param jobId jobId assigned by the MediaTranscodingService upon receiving request.
+    * @param sessionId sessionId assigned by the MediaTranscodingService upon receiving request.
     */
-    oneway void onTranscodingStarted(in int jobId);
+    oneway void onTranscodingStarted(in int sessionId);
 
     /**
-    * Called when the transcoding associated with the jobId is paused.
-    * This will only be called if client request to get all the status of the job.
+    * Called when the transcoding associated with the sessionId is paused.
+    * This will only be called if client request to get all the status of the session.
     *
-    * @param jobId jobId assigned by the MediaTranscodingService upon receiving request.
+    * @param sessionId sessionId assigned by the MediaTranscodingService upon receiving request.
     */
-    oneway void onTranscodingPaused(in int jobId);
+    oneway void onTranscodingPaused(in int sessionId);
 
     /**
-    * Called when the transcoding associated with the jobId is resumed.
-    * This will only be called if client request to get all the status of the job.
+    * Called when the transcoding associated with the sessionId is resumed.
+    * This will only be called if client request to get all the status of the session.
     *
-    * @param jobId jobId assigned by the MediaTranscodingService upon receiving request.
+    * @param sessionId sessionId assigned by the MediaTranscodingService upon receiving request.
     */
-    oneway void onTranscodingResumed(in int jobId);
+    oneway void onTranscodingResumed(in int sessionId);
 
     /**
-    * Called when the transcoding associated with the jobId finished.
+    * Called when the transcoding associated with the sessionId finished.
     *
-    * @param jobId jobId assigned by the MediaTranscodingService upon receiving request.
+    * @param sessionId sessionId assigned by the MediaTranscodingService upon receiving request.
     * @param result contains the transcoded file stats and other transcoding metrics if requested.
     */
-    oneway void onTranscodingFinished(in int jobId, in TranscodingResultParcel result);
+    oneway void onTranscodingFinished(in int sessionId, in TranscodingResultParcel result);
 
     /**
-    * Called when the transcoding associated with the jobId failed.
+    * Called when the transcoding associated with the sessionId failed.
     *
-    * @param jobId jobId assigned by the MediaTranscodingService upon receiving request.
+    * @param sessionId sessionId assigned by the MediaTranscodingService upon receiving request.
     * @param errorCode error code that indicates the error.
     */
-    oneway void onTranscodingFailed(in int jobId, in TranscodingErrorCode errorCode);
+    oneway void onTranscodingFailed(in int sessionId, in TranscodingErrorCode errorCode);
 
     /**
-    * Called when the transcoding configuration associated with the jobId gets updated, i.e. wait
-    * number in the job queue.
+    * Called when the transcoding configuration associated with the sessionId gets updated, i.e. wait
+    * number in the session queue.
     *
     * <p> This will only be called if client set requestUpdate to be true in the TranscodingRequest
     * submitted to the MediaTranscodingService.
     *
-    * @param jobId jobId assigned by the MediaTranscodingService upon receiving request.
-    * @param oldAwaitNumber previous number of jobs ahead of current job.
-    * @param newAwaitNumber updated number of jobs ahead of current job.
+    * @param sessionId sessionId assigned by the MediaTranscodingService upon receiving request.
+    * @param oldAwaitNumber previous number of sessions ahead of current session.
+    * @param newAwaitNumber updated number of sessions ahead of current session.
     */
-    oneway void onAwaitNumberOfJobsChanged(in int jobId,
+    oneway void onAwaitNumberOfSessionsChanged(in int sessionId,
                                            in int oldAwaitNumber,
                                            in int newAwaitNumber);
 
     /**
-    * Called when there is an update on the progress of the TranscodingJob.
+    * Called when there is an update on the progress of the TranscodingSession.
     *
     * <p> This will only be called if client set requestUpdate to be true in the TranscodingRequest
     * submitted to the MediaTranscodingService.
     *
-    * @param jobId jobId assigned by the MediaTranscodingService upon receiving request.
+    * @param sessionId sessionId assigned by the MediaTranscodingService upon receiving request.
     * @param progress an integer number ranging from 0 ~ 100 inclusive.
     */
-    oneway void onProgressUpdate(in int jobId, in int progress);
+    oneway void onProgressUpdate(in int sessionId, in int progress);
 }
diff --git a/media/libmediatranscoding/aidl/android/media/TranscodingRequestParcel.aidl b/media/libmediatranscoding/aidl/android/media/TranscodingRequestParcel.aidl
index 14d19ba..4b19f6a 100644
--- a/media/libmediatranscoding/aidl/android/media/TranscodingRequestParcel.aidl
+++ b/media/libmediatranscoding/aidl/android/media/TranscodingRequestParcel.aidl
@@ -16,7 +16,7 @@
 
 package android.media;
 
-import android.media.TranscodingJobPriority;
+import android.media.TranscodingSessionPriority;
 import android.media.TranscodingTestConfig;
 import android.media.TranscodingType;
 import android.media.TranscodingVideoTrackFormat;
@@ -53,6 +53,11 @@
     int clientPid = -1;
 
     /**
+     * The package name of the client whom this transcoding request is for.
+     */
+    @utf8InCpp String clientPackageName;
+
+    /**
      * Type of the transcoding.
      */
     TranscodingType transcodingType;
@@ -61,26 +66,26 @@
      * Requested video track format for the transcoding.
      * Note that the transcoding service will try to fulfill the requested format as much as
      * possbile, while subject to hardware and software limitation. The final video track format
-     * will be available in the TranscodingJobParcel when the job is finished.
+     * will be available in the TranscodingSessionParcel when the session is finished.
      */
     @nullable TranscodingVideoTrackFormat requestedVideoTrackFormat;
 
     /**
      * Priority of this transcoding. Service will schedule the transcoding based on the priority.
      */
-    TranscodingJobPriority priority;
+    TranscodingSessionPriority priority;
 
     /**
-     * Whether to receive update on progress and change of awaitNumJobs.
+     * Whether to receive update on progress and change of awaitNumSessions.
      * Default to false.
      */
     boolean requestProgressUpdate = false;
 
     /**
-     * Whether to receive update on job's start/stop/pause/resume.
+     * Whether to receive update on session's start/stop/pause/resume.
      * Default to false.
      */
-    boolean requestJobEventUpdate = false;
+    boolean requestSessionEventUpdate = false;
 
     /**
      * Whether this request is for testing.
@@ -94,7 +99,7 @@
 
      /**
       * Whether to get the stats of the transcoding.
-      * If this is enabled, the TranscodingJobStats will be returned in TranscodingResultParcel
+      * If this is enabled, the TranscodingSessionStats will be returned in TranscodingResultParcel
       * upon transcoding finishes.
       */
     boolean enableStats = false;
diff --git a/media/libmediatranscoding/aidl/android/media/TranscodingResultParcel.aidl b/media/libmediatranscoding/aidl/android/media/TranscodingResultParcel.aidl
index a20c8b1..7826e25 100644
--- a/media/libmediatranscoding/aidl/android/media/TranscodingResultParcel.aidl
+++ b/media/libmediatranscoding/aidl/android/media/TranscodingResultParcel.aidl
@@ -16,7 +16,7 @@
 
 package android.media;
 
-import android.media.TranscodingJobStats;
+import android.media.TranscodingSessionStats;
 
 /**
  * Result of the transcoding.
@@ -26,9 +26,9 @@
 //TODO(hkuang): Implement the parcelable.
 parcelable TranscodingResultParcel {
     /**
-     * The jobId associated with the TranscodingResult.
+     * The sessionId associated with the TranscodingResult.
      */
-    int jobId;
+    int sessionId;
 
     /**
      * Actual bitrate of the transcoded video in bits per second. This will only present for video
@@ -37,8 +37,8 @@
     int actualBitrateBps;
 
     /**
-     * Stats of the transcoding job. This will only be available when client requests to get the
+     * Stats of the transcoding session. This will only be available when client requests to get the
      * stats in TranscodingRequestParcel.
      */
-    @nullable TranscodingJobStats jobStats;
+    @nullable TranscodingSessionStats sessionStats;
 }
\ No newline at end of file
diff --git a/media/libmediatranscoding/aidl/android/media/TranscodingJobParcel.aidl b/media/libmediatranscoding/aidl/android/media/TranscodingSessionParcel.aidl
similarity index 61%
rename from media/libmediatranscoding/aidl/android/media/TranscodingJobParcel.aidl
rename to media/libmediatranscoding/aidl/android/media/TranscodingSessionParcel.aidl
index baf4381..3a4a500 100644
--- a/media/libmediatranscoding/aidl/android/media/TranscodingJobParcel.aidl
+++ b/media/libmediatranscoding/aidl/android/media/TranscodingSessionParcel.aidl
@@ -20,34 +20,34 @@
 import android.media.TranscodingVideoTrackFormat;
 
 /**
- * TranscodingJob is generated by the MediaTranscodingService upon receiving a TranscodingRequest.
- * It contains all the necessary configuration generated by the MediaTranscodingService for the
- * TranscodingRequest.
+ * TranscodingSession is generated by the MediaTranscodingService upon receiving a
+ * TranscodingRequest. It contains all the necessary configuration generated by the
+ * MediaTranscodingService for the TranscodingRequest.
  *
  * {@hide}
  */
 //TODO(hkuang): Implement the parcelable.
-parcelable TranscodingJobParcel {
+parcelable TranscodingSessionParcel {
     /**
      * A unique positive Id generated by the MediaTranscodingService.
      */
-    int jobId;
+    int sessionId;
 
     /**
-     * The request associated with the TranscodingJob.
+     * The request associated with the TranscodingSession.
      */
     TranscodingRequestParcel request;
 
     /**
      * Output video track's format. This will only be avaiable for video transcoding and it will
-     * be avaiable when the job is finished.
+     * be avaiable when the session is finished.
      */
     @nullable TranscodingVideoTrackFormat videoTrackFormat;
 
     /**
-    * Current number of jobs ahead of this job. The service schedules the job based on the priority
-    * passed from the client. Client could specify whether to receive updates when the
-    * awaitNumberOfJobs changes through setting requestProgressUpdate in the TranscodingRequest.
+    * Current number of sessions ahead of this session. The service schedules the session based on
+    * the priority passed from the client. Client could specify whether to receive updates when the
+    * awaitNumberOfSessions changes through setting requestProgressUpdate in the TranscodingRequest.
     */
-    int awaitNumberOfJobs;
+    int awaitNumberOfSessions;
 }
diff --git a/media/libmediatranscoding/aidl/android/media/TranscodingJobPriority.aidl b/media/libmediatranscoding/aidl/android/media/TranscodingSessionPriority.aidl
similarity index 92%
rename from media/libmediatranscoding/aidl/android/media/TranscodingJobPriority.aidl
rename to media/libmediatranscoding/aidl/android/media/TranscodingSessionPriority.aidl
index 1a5d81a..f001484 100644
--- a/media/libmediatranscoding/aidl/android/media/TranscodingJobPriority.aidl
+++ b/media/libmediatranscoding/aidl/android/media/TranscodingSessionPriority.aidl
@@ -17,12 +17,12 @@
 package android.media;
 
 /**
- * Priority of a transcoding job.
+ * Priority of a transcoding session.
  *
  * {@hide}
  */
 @Backing(type="int")
-enum TranscodingJobPriority {
+enum TranscodingSessionPriority {
     // TODO(hkuang): define what each priority level actually mean.
     kUnspecified = 0,
     kLow = 1,
diff --git a/media/libmediatranscoding/aidl/android/media/TranscodingJobStats.aidl b/media/libmediatranscoding/aidl/android/media/TranscodingSessionStats.aidl
similarity index 65%
rename from media/libmediatranscoding/aidl/android/media/TranscodingJobStats.aidl
rename to media/libmediatranscoding/aidl/android/media/TranscodingSessionStats.aidl
index 1b41b87..b3e7eea 100644
--- a/media/libmediatranscoding/aidl/android/media/TranscodingJobStats.aidl
+++ b/media/libmediatranscoding/aidl/android/media/TranscodingSessionStats.aidl
@@ -17,20 +17,20 @@
 package android.media;
 
 /**
- * TranscodingJobStats encapsulated the stats of the a TranscodingJob.
+ * TranscodingSessionStats encapsulated the stats of the a TranscodingSession.
  *
  * {@hide}
  */
-parcelable TranscodingJobStats {
+parcelable TranscodingSessionStats {
     /**
-     * System time of when the job is created.
+     * System time of when the session is created.
      */
-    long jobCreatedTimeUs;
+    long sessionCreatedTimeUs;
 
     /**
-     * System time of when the job is finished.
+     * System time of when the session is finished.
      */
-    long jobFinishedTimeUs;
+    long sessionFinishedTimeUs;
 
     /**
      * Total time spend on transcoding, exclude the time in pause.
@@ -38,8 +38,8 @@
     long totalProcessingTimeUs;
 
     /**
-     * Total time spend on handling the job, include the time in pause.
-     * The totaltimeUs is actually the same as jobFinishedTimeUs - jobCreatedTimeUs.
+     * Total time spend on handling the session, include the time in pause.
+     * The totaltimeUs is actually the same as sessionFinishedTimeUs - sessionCreatedTimeUs.
      */
     long totalTimeUs;
 }
diff --git a/media/libmediatranscoding/aidl/android/media/TranscodingTestConfig.aidl b/media/libmediatranscoding/aidl/android/media/TranscodingTestConfig.aidl
index a564799..12e0e94 100644
--- a/media/libmediatranscoding/aidl/android/media/TranscodingTestConfig.aidl
+++ b/media/libmediatranscoding/aidl/android/media/TranscodingTestConfig.aidl
@@ -24,7 +24,7 @@
 parcelable TranscodingTestConfig {
     /**
      * Whether to use SimulatedTranscoder for testing. Note that SimulatedTranscoder does not send
-     * transcoding jobs to real MediaTranscoder.
+     * transcoding sessions to real MediaTranscoder.
      */
     boolean useSimulatedTranscoder = false;
 
@@ -35,9 +35,10 @@
     boolean passThroughMode = false;
 
     /**
-     * Time of processing the job in milliseconds. Service will return the job result at least after
-     * processingTotalTimeMs from the time it starts to process the job. Note that if service uses
-     * real MediaTranscoder to do transcoding, the time spent on transcoding may be more than that.
+     * Time of processing the session in milliseconds. Service will return the session result at
+     * least after processingTotalTimeMs from the time it starts to process the session. Note that
+     * if service uses real MediaTranscoder to do transcoding, the time spent on transcoding may be
+     * more than that.
      */
     int processingTotalTimeMs = 0;
 }
diff --git a/media/libmediatranscoding/aidl/android/media/TranscodingVideoTrackFormat.aidl b/media/libmediatranscoding/aidl/android/media/TranscodingVideoTrackFormat.aidl
index 90502cd..8ed241a 100644
--- a/media/libmediatranscoding/aidl/android/media/TranscodingVideoTrackFormat.aidl
+++ b/media/libmediatranscoding/aidl/android/media/TranscodingVideoTrackFormat.aidl
@@ -25,13 +25,13 @@
  * aidl_interface
  *
  * Note that TranscodingVideoTrackFormat is used in TranscodingRequestParcel for the  client to
- * specify the desired transcoded video format, and is also used in TranscodingJobParcel for the
+ * specify the desired transcoded video format, and is also used in TranscodingSessionParcel for the
  * service to notify client of the final video format for transcoding.
  * When used as input in TranscodingRequestParcel, the client only needs to specify the config that
  * they want to change, e.g. codec or resolution, and all the missing configs will be extracted
  * from the source video and applied to the destination video.
- * When used as output in TranscodingJobParcel, all the configs will be populated to indicate the
- * final encoder configs used for transcoding.
+ * When used as output in TranscodingSessionParcel, all the configs will be populated to indicate
+ * the final encoder configs used for transcoding.
  *
  * {@hide}
  */
diff --git a/media/libmediatranscoding/include/media/AdjustableMaxPriorityQueue.h b/media/libmediatranscoding/include/media/AdjustableMaxPriorityQueue.h
index 9ca2ee9..5ba1ee2 100644
--- a/media/libmediatranscoding/include/media/AdjustableMaxPriorityQueue.h
+++ b/media/libmediatranscoding/include/media/AdjustableMaxPriorityQueue.h
@@ -26,7 +26,7 @@
 namespace android {
 
 /*
- * AdjustableMaxPriorityQueue is a custom max priority queue that helps managing jobs for
+ * AdjustableMaxPriorityQueue is a custom max priority queue that helps managing sessions for
  * MediaTranscodingService.
  *
  * AdjustableMaxPriorityQueue is a wrapper template around the STL's *_heap() functions.
diff --git a/media/libmediatranscoding/include/media/ControllerClientInterface.h b/media/libmediatranscoding/include/media/ControllerClientInterface.h
new file mode 100644
index 0000000..3fd4f0c
--- /dev/null
+++ b/media/libmediatranscoding/include/media/ControllerClientInterface.h
@@ -0,0 +1,68 @@
+/*
+ * 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_CONTROLLER_CLIENT_INTERFACE_H
+#define ANDROID_MEDIA_CONTROLLER_CLIENT_INTERFACE_H
+
+#include <aidl/android/media/ITranscodingClientCallback.h>
+#include <aidl/android/media/TranscodingRequestParcel.h>
+#include <media/TranscodingDefs.h>
+
+namespace android {
+
+using ::aidl::android::media::ITranscodingClientCallback;
+using ::aidl::android::media::TranscodingRequestParcel;
+
+// Interface for a client to call the controller to schedule or retrieve
+// the status of a session.
+class ControllerClientInterface {
+public:
+    /**
+     * Submits one request to the controller.
+     *
+     * Returns true on success and false on failure. This call will fail is a session identified
+     * by <clientId, sessionId> already exists.
+     */
+    virtual bool submit(ClientIdType clientId, SessionIdType sessionId, uid_t uid,
+                        const TranscodingRequestParcel& request,
+                        const std::weak_ptr<ITranscodingClientCallback>& clientCallback) = 0;
+
+    /**
+     * Cancels a session identified by <clientId, sessionId>.
+     *
+     * If sessionId is negative (<0), all sessions with a specified priority (that's not
+     * TranscodingSessionPriority::kUnspecified) will be cancelled. Otherwise, only the single
+     * session <clientId, sessionId> will be cancelled.
+     *
+     * Returns false if a single session is being cancelled but it doesn't exist. Returns
+     * true otherwise.
+     */
+    virtual bool cancel(ClientIdType clientId, SessionIdType sessionId) = 0;
+
+    /**
+     * Retrieves information about a session.
+     *
+     * Returns true and the session if it exists, and false otherwise.
+     */
+    virtual bool getSession(ClientIdType clientId, SessionIdType sessionId,
+                            TranscodingRequestParcel* request) = 0;
+
+protected:
+    virtual ~ControllerClientInterface() = default;
+};
+
+}  // namespace android
+#endif  // ANDROID_MEDIA_CONTROLLER_CLIENT_INTERFACE_H
diff --git a/media/libmediatranscoding/include/media/ResourcePolicyInterface.h b/media/libmediatranscoding/include/media/ResourcePolicyInterface.h
index 8bd7d6b..4a92af8 100644
--- a/media/libmediatranscoding/include/media/ResourcePolicyInterface.h
+++ b/media/libmediatranscoding/include/media/ResourcePolicyInterface.h
@@ -21,7 +21,7 @@
 
 class ResourcePolicyCallbackInterface;
 
-// Interface for the JobScheduler to control the resource status updates.
+// Interface for the SessionController to control the resource status updates.
 class ResourcePolicyInterface {
 public:
     // Set the associated callback interface to send the events when resource
@@ -32,11 +32,11 @@
     virtual ~ResourcePolicyInterface() = default;
 };
 
-// Interface for notifying the JobScheduler of a change in resource status.
+// Interface for notifying the SessionController of a change in resource status.
 class ResourcePolicyCallbackInterface {
 public:
-    // Called when codec resources become available. The scheduler may use this
-    // as a signal to attempt restart transcoding jobs that were previously
+    // Called when codec resources become available. The controller may use this
+    // as a signal to attempt restart transcoding sessions that were previously
     // paused due to temporary resource loss.
     virtual void onResourceAvailable() = 0;
 
diff --git a/media/libmediatranscoding/include/media/SchedulerClientInterface.h b/media/libmediatranscoding/include/media/SchedulerClientInterface.h
deleted file mode 100644
index e00cfb2..0000000
--- a/media/libmediatranscoding/include/media/SchedulerClientInterface.h
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * 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_SCHEDULER_CLIENT_INTERFACE_H
-#define ANDROID_MEDIA_SCHEDULER_CLIENT_INTERFACE_H
-
-#include <aidl/android/media/ITranscodingClientCallback.h>
-#include <aidl/android/media/TranscodingRequestParcel.h>
-#include <media/TranscodingDefs.h>
-
-namespace android {
-
-using ::aidl::android::media::ITranscodingClientCallback;
-using ::aidl::android::media::TranscodingRequestParcel;
-
-// Interface for a client to call the scheduler to schedule or retrieve
-// the status of a job.
-class SchedulerClientInterface {
-public:
-    /**
-     * Submits one request to the scheduler.
-     *
-     * Returns true on success and false on failure. This call will fail is a job identified
-     * by <clientId, jobId> already exists.
-     */
-    virtual bool submit(ClientIdType clientId, JobIdType jobId, uid_t uid,
-                        const TranscodingRequestParcel& request,
-                        const std::weak_ptr<ITranscodingClientCallback>& clientCallback) = 0;
-
-    /**
-     * Cancels a job identified by <clientId, jobId>.
-     *
-     * If jobId is negative (<0), all jobs with a specified priority (that's not
-     * TranscodingJobPriority::kUnspecified) will be cancelled. Otherwise, only the single job
-     * <clientId, jobId> will be cancelled.
-     *
-     * Returns false if a single job is being cancelled but it doesn't exist. Returns
-     * true otherwise.
-     */
-    virtual bool cancel(ClientIdType clientId, JobIdType jobId) = 0;
-
-    /**
-     * Retrieves information about a job.
-     *
-     * Returns true and the job if it exists, and false otherwise.
-     */
-    virtual bool getJob(ClientIdType clientId, JobIdType jobId,
-                        TranscodingRequestParcel* request) = 0;
-
-protected:
-    virtual ~SchedulerClientInterface() = default;
-};
-
-}  // namespace android
-#endif  // ANDROID_MEDIA_SCHEDULER_CLIENT_INTERFACE_H
diff --git a/media/libmediatranscoding/include/media/TranscoderInterface.h b/media/libmediatranscoding/include/media/TranscoderInterface.h
index 1a3f505..e17cd5a 100644
--- a/media/libmediatranscoding/include/media/TranscoderInterface.h
+++ b/media/libmediatranscoding/include/media/TranscoderInterface.h
@@ -29,39 +29,41 @@
 using ::aidl::android::media::TranscodingRequestParcel;
 class TranscoderCallbackInterface;
 
-// Interface for the scheduler to call the transcoder to take actions.
+// Interface for the controller to call the transcoder to take actions.
 class TranscoderInterface {
 public:
     virtual void setCallback(const std::shared_ptr<TranscoderCallbackInterface>& cb) = 0;
-    virtual void start(ClientIdType clientId, JobIdType jobId,
+    virtual void start(ClientIdType clientId, SessionIdType sessionId,
                        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,
+    virtual void pause(ClientIdType clientId, SessionIdType sessionId) = 0;
+    virtual void resume(ClientIdType clientId, SessionIdType sessionId,
                         const TranscodingRequestParcel& request,
                         const std::shared_ptr<ITranscodingClientCallback>& clientCallback) = 0;
-    virtual void stop(ClientIdType clientId, JobIdType jobId) = 0;
+    virtual void stop(ClientIdType clientId, SessionIdType sessionId) = 0;
 
 protected:
     virtual ~TranscoderInterface() = default;
 };
 
-// Interface for the transcoder to notify the scheduler of the status of
-// the currently running job, or temporary loss of transcoding resources.
+// Interface for the transcoder to notify the controller of the status of
+// the currently running session, or temporary loss of transcoding resources.
 class TranscoderCallbackInterface {
 public:
     // TODO(chz): determine what parameters are needed here.
-    virtual void onStarted(ClientIdType clientId, JobIdType jobId) = 0;
-    virtual void onPaused(ClientIdType clientId, JobIdType jobId) = 0;
-    virtual void onResumed(ClientIdType clientId, JobIdType jobId) = 0;
-    virtual void onFinish(ClientIdType clientId, JobIdType jobId) = 0;
-    virtual void onError(ClientIdType clientId, JobIdType jobId, TranscodingErrorCode err) = 0;
-    virtual void onProgressUpdate(ClientIdType clientId, JobIdType jobId, int32_t progress) = 0;
+    virtual void onStarted(ClientIdType clientId, SessionIdType sessionId) = 0;
+    virtual void onPaused(ClientIdType clientId, SessionIdType sessionId) = 0;
+    virtual void onResumed(ClientIdType clientId, SessionIdType sessionId) = 0;
+    virtual void onFinish(ClientIdType clientId, SessionIdType sessionId) = 0;
+    virtual void onError(ClientIdType clientId, SessionIdType sessionId,
+                         TranscodingErrorCode err) = 0;
+    virtual void onProgressUpdate(ClientIdType clientId, SessionIdType sessionId,
+                                  int32_t progress) = 0;
 
     // Called when transcoding becomes temporarily inaccessible due to loss of resource.
-    // If there is any job currently running, it will be paused. When resource contention
-    // is solved, the scheduler should call TranscoderInterface's to either start a new job,
-    // or resume a paused job.
+    // If there is any session currently running, it will be paused. When resource contention
+    // is solved, the controller should call TranscoderInterface's to either start a new session,
+    // or resume a paused session.
     virtual void onResourceLost() = 0;
 
 protected:
diff --git a/media/libmediatranscoding/include/media/TranscoderWrapper.h b/media/libmediatranscoding/include/media/TranscoderWrapper.h
index c956042..9ec32d7 100644
--- a/media/libmediatranscoding/include/media/TranscoderWrapper.h
+++ b/media/libmediatranscoding/include/media/TranscoderWrapper.h
@@ -32,7 +32,7 @@
 
 /*
  * Wrapper class around MediaTranscoder.
- * Implements TranscoderInterface for TranscodingJobScheduler to use.
+ * Implements TranscoderInterface for TranscodingSessionController to use.
  */
 class TranscoderWrapper : public TranscoderInterface,
                           public std::enable_shared_from_this<TranscoderWrapper> {
@@ -40,25 +40,25 @@
     TranscoderWrapper();
 
     virtual void setCallback(const std::shared_ptr<TranscoderCallbackInterface>& cb) override;
-    virtual void start(ClientIdType clientId, JobIdType jobId,
+    virtual void start(ClientIdType clientId, SessionIdType sessionId,
                        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,
+    virtual void pause(ClientIdType clientId, SessionIdType sessionId) override;
+    virtual void resume(ClientIdType clientId, SessionIdType sessionId,
                         const TranscodingRequestParcel& request,
                         const std::shared_ptr<ITranscodingClientCallback>& clientCallback) override;
-    virtual void stop(ClientIdType clientId, JobIdType jobId) override;
+    virtual void stop(ClientIdType clientId, SessionIdType sessionId) override;
 
 private:
     class CallbackImpl;
     struct Event {
         enum Type { NoEvent, Start, Pause, Resume, Stop, Finish, Error, Progress } type;
         ClientIdType clientId;
-        JobIdType jobId;
+        SessionIdType sessionId;
         std::function<void()> runnable;
         int32_t arg;
     };
-    using JobKeyType = std::pair<ClientIdType, JobIdType>;
+    using SessionKeyType = std::pair<ClientIdType, SessionIdType>;
 
     std::shared_ptr<CallbackImpl> mTranscoderCb;
     std::shared_ptr<MediaTranscoder> mTranscoder;
@@ -66,30 +66,30 @@
     std::mutex mLock;
     std::condition_variable mCondition;
     std::list<Event> mQueue;  // GUARDED_BY(mLock);
-    std::map<JobKeyType, std::shared_ptr<const Parcel>> mPausedStateMap;
+    std::map<SessionKeyType, std::shared_ptr<ndk::ScopedAParcel>> mPausedStateMap;
     ClientIdType mCurrentClientId;
-    JobIdType mCurrentJobId;
+    SessionIdType mCurrentSessionId;
 
     static std::string toString(const Event& event);
-    void onFinish(ClientIdType clientId, JobIdType jobId);
-    void onError(ClientIdType clientId, JobIdType jobId, media_status_t status);
-    void onProgress(ClientIdType clientId, JobIdType jobId, int32_t progress);
+    void onFinish(ClientIdType clientId, SessionIdType sessionId);
+    void onError(ClientIdType clientId, SessionIdType sessionId, media_status_t status);
+    void onProgress(ClientIdType clientId, SessionIdType sessionId, int32_t progress);
 
-    media_status_t handleStart(ClientIdType clientId, JobIdType jobId,
+    media_status_t handleStart(ClientIdType clientId, SessionIdType sessionId,
                                const TranscodingRequestParcel& request,
                                const std::shared_ptr<ITranscodingClientCallback>& callback);
-    media_status_t handlePause(ClientIdType clientId, JobIdType jobId);
-    media_status_t handleResume(ClientIdType clientId, JobIdType jobId,
+    media_status_t handlePause(ClientIdType clientId, SessionIdType sessionId);
+    media_status_t handleResume(ClientIdType clientId, SessionIdType sessionId,
                                 const TranscodingRequestParcel& request,
                                 const std::shared_ptr<ITranscodingClientCallback>& callback);
-    media_status_t setupTranscoder(ClientIdType clientId, JobIdType jobId,
-                                   const TranscodingRequestParcel& request,
-                                   const std::shared_ptr<ITranscodingClientCallback>& callback,
-                                   const std::shared_ptr<const Parcel>& pausedState = nullptr);
+    media_status_t setupTranscoder(
+            ClientIdType clientId, SessionIdType sessionId, const TranscodingRequestParcel& request,
+            const std::shared_ptr<ITranscodingClientCallback>& callback,
+            const std::shared_ptr<ndk::ScopedAParcel>& pausedState = nullptr);
 
     void cleanup();
-    void reportError(ClientIdType clientId, JobIdType jobId, media_status_t err);
-    void queueEvent(Event::Type type, ClientIdType clientId, JobIdType jobId,
+    void reportError(ClientIdType clientId, SessionIdType sessionId, media_status_t err);
+    void queueEvent(Event::Type type, ClientIdType clientId, SessionIdType sessionId,
                     const std::function<void()> runnable, int32_t arg = 0);
     void threadLoop();
 };
diff --git a/media/libmediatranscoding/include/media/TranscodingClientManager.h b/media/libmediatranscoding/include/media/TranscodingClientManager.h
index be03bc4..451f993 100644
--- a/media/libmediatranscoding/include/media/TranscodingClientManager.h
+++ b/media/libmediatranscoding/include/media/TranscodingClientManager.h
@@ -29,7 +29,7 @@
 #include <unordered_map>
 #include <unordered_set>
 
-#include "SchedulerClientInterface.h"
+#include "ControllerClientInterface.h"
 
 namespace android {
 
@@ -84,9 +84,9 @@
     struct ClientImpl;
 
     // Only allow MediaTranscodingService and unit tests to instantiate.
-    TranscodingClientManager(const std::shared_ptr<SchedulerClientInterface>& scheduler);
+    TranscodingClientManager(const std::shared_ptr<ControllerClientInterface>& controller);
 
-    // Checks if a user is trusted (and allowed to submit jobs on behalf of other uids)
+    // Checks if a user is trusted (and allowed to submit sessions on behalf of other uids)
     bool isTrustedCallingUid(uid_t uid);
 
     /**
@@ -108,8 +108,8 @@
 
     ::ndk::ScopedAIBinder_DeathRecipient mDeathRecipient;
 
-    std::shared_ptr<SchedulerClientInterface> mJobScheduler;
-    uid_t mMediaProviderUid;
+    std::shared_ptr<ControllerClientInterface> mSessionController;
+    std::unordered_set<uid_t> mMediaProviderUid;
 
     static std::atomic<ClientIdType> sCookieCounter;
     static std::mutex sCookie2ClientLock;
diff --git a/media/libmediatranscoding/include/media/TranscodingDefs.h b/media/libmediatranscoding/include/media/TranscodingDefs.h
index 31d83ac..8e02dd2 100644
--- a/media/libmediatranscoding/include/media/TranscodingDefs.h
+++ b/media/libmediatranscoding/include/media/TranscodingDefs.h
@@ -23,7 +23,7 @@
 namespace android {
 
 using ClientIdType = uintptr_t;
-using JobIdType = int32_t;
+using SessionIdType = int32_t;
 
 }  // namespace android
 #endif  // ANDROID_MEDIA_TRANSCODING_DEFS_H
diff --git a/media/libmediatranscoding/include/media/TranscodingJobScheduler.h b/media/libmediatranscoding/include/media/TranscodingJobScheduler.h
deleted file mode 100644
index 7a656d5..0000000
--- a/media/libmediatranscoding/include/media/TranscodingJobScheduler.h
+++ /dev/null
@@ -1,135 +0,0 @@
-/*
- * 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_JOB_SCHEDULER_H
-#define ANDROID_MEDIA_TRANSCODING_JOB_SCHEDULER_H
-
-#include <aidl/android/media/TranscodingJobPriority.h>
-#include <media/ResourcePolicyInterface.h>
-#include <media/SchedulerClientInterface.h>
-#include <media/TranscoderInterface.h>
-#include <media/TranscodingRequest.h>
-#include <media/UidPolicyInterface.h>
-#include <utils/String8.h>
-#include <utils/Vector.h>
-
-#include <list>
-#include <map>
-#include <mutex>
-
-namespace android {
-using ::aidl::android::media::TranscodingJobPriority;
-using ::aidl::android::media::TranscodingResultParcel;
-
-class TranscodingJobScheduler : public UidPolicyCallbackInterface,
-                                public SchedulerClientInterface,
-                                public TranscoderCallbackInterface,
-                                public ResourcePolicyCallbackInterface {
-public:
-    virtual ~TranscodingJobScheduler();
-
-    // SchedulerClientInterface
-    bool submit(ClientIdType clientId, JobIdType jobId, uid_t uid,
-                const TranscodingRequestParcel& request,
-                const std::weak_ptr<ITranscodingClientCallback>& clientCallback) override;
-    bool cancel(ClientIdType clientId, JobIdType jobId) override;
-    bool getJob(ClientIdType clientId, JobIdType jobId, TranscodingRequestParcel* request) override;
-    // ~SchedulerClientInterface
-
-    // TranscoderCallbackInterface
-    void onStarted(ClientIdType clientId, JobIdType jobId) override;
-    void onPaused(ClientIdType clientId, JobIdType jobId) override;
-    void onResumed(ClientIdType clientId, JobIdType jobId) override;
-    void onFinish(ClientIdType clientId, JobIdType jobId) override;
-    void onError(ClientIdType clientId, JobIdType jobId, TranscodingErrorCode err) override;
-    void onProgressUpdate(ClientIdType clientId, JobIdType jobId, int32_t progress) override;
-    void onResourceLost() override;
-    // ~TranscoderCallbackInterface
-
-    // UidPolicyCallbackInterface
-    void onTopUidsChanged(const std::unordered_set<uid_t>& uids) override;
-    // ~UidPolicyCallbackInterface
-
-    // ResourcePolicyCallbackInterface
-    void onResourceAvailable() override;
-    // ~ResourcePolicyCallbackInterface
-
-    /**
-     * Dump all the job information to the fd.
-     */
-    void dumpAllJobs(int fd, const Vector<String16>& args);
-
-private:
-    friend class MediaTranscodingService;
-    friend class TranscodingJobSchedulerTest;
-
-    using JobKeyType = std::pair<ClientIdType, JobIdType>;
-    using JobQueueType = std::list<JobKeyType>;
-
-    struct Job {
-        JobKeyType key;
-        uid_t uid;
-        enum State {
-            NOT_STARTED,
-            RUNNING,
-            PAUSED,
-        } state;
-        int32_t lastProgress;
-        TranscodingRequest request;
-        std::weak_ptr<ITranscodingClientCallback> callback;
-    };
-
-    // TODO(chz): call transcoder without global lock.
-    // Use mLock for all entrypoints for now.
-    mutable std::mutex mLock;
-
-    std::map<JobKeyType, Job> mJobMap;
-
-    // uid->JobQueue map (uid == -1: offline queue)
-    std::map<uid_t, JobQueueType> mJobQueues;
-
-    // uids, with the head being the most-recently-top app, 2nd item is the
-    // previous top app, etc.
-    std::list<uid_t> mUidSortedList;
-    std::list<uid_t>::iterator mOfflineUidIterator;
-
-    std::shared_ptr<TranscoderInterface> mTranscoder;
-    std::shared_ptr<UidPolicyInterface> mUidPolicy;
-    std::shared_ptr<ResourcePolicyInterface> mResourcePolicy;
-
-    Job* mCurrentJob;
-    bool mResourceLost;
-
-    // Only allow MediaTranscodingService and unit tests to instantiate.
-    TranscodingJobScheduler(const std::shared_ptr<TranscoderInterface>& transcoder,
-                            const std::shared_ptr<UidPolicyInterface>& uidPolicy,
-                            const std::shared_ptr<ResourcePolicyInterface>& resourcePolicy);
-
-    Job* getTopJob_l();
-    void updateCurrentJob_l();
-    void removeJob_l(const JobKeyType& jobKey);
-    void moveUidsToTop_l(const std::unordered_set<uid_t>& uids, bool preserveTopUid);
-    void notifyClient(ClientIdType clientId, JobIdType jobId, const char* reason,
-                      std::function<void(const JobKeyType&)> func);
-    // Internal state verifier (debug only)
-    void validateState_l();
-
-    static String8 jobToString(const JobKeyType& jobKey);
-    static const char* jobStateToString(const Job::State jobState);
-};
-
-}  // namespace android
-#endif  // ANDROID_MEDIA_TRANSCODING_JOB_SCHEDULER_H
diff --git a/media/libmediatranscoding/include/media/TranscodingRequest.h b/media/libmediatranscoding/include/media/TranscodingRequest.h
index a6cfed2..485403f 100644
--- a/media/libmediatranscoding/include/media/TranscodingRequest.h
+++ b/media/libmediatranscoding/include/media/TranscodingRequest.h
@@ -39,11 +39,12 @@
         destinationFilePath = parcel.destinationFilePath;
         clientUid = parcel.clientUid;
         clientPid = parcel.clientPid;
+        clientPackageName = parcel.clientPackageName;
         transcodingType = parcel.transcodingType;
         requestedVideoTrackFormat = parcel.requestedVideoTrackFormat;
         priority = parcel.priority;
         requestProgressUpdate = parcel.requestProgressUpdate;
-        requestJobEventUpdate = parcel.requestJobEventUpdate;
+        requestSessionEventUpdate = parcel.requestSessionEventUpdate;
         isForTesting = parcel.isForTesting;
         testConfig = parcel.testConfig;
     }
diff --git a/media/libmediatranscoding/include/media/TranscodingSessionController.h b/media/libmediatranscoding/include/media/TranscodingSessionController.h
new file mode 100644
index 0000000..c082074
--- /dev/null
+++ b/media/libmediatranscoding/include/media/TranscodingSessionController.h
@@ -0,0 +1,138 @@
+/*
+ * 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_SESSION_CONTROLLER_H
+#define ANDROID_MEDIA_TRANSCODING_SESSION_CONTROLLER_H
+
+#include <aidl/android/media/TranscodingSessionPriority.h>
+#include <media/ControllerClientInterface.h>
+#include <media/ResourcePolicyInterface.h>
+#include <media/TranscoderInterface.h>
+#include <media/TranscodingRequest.h>
+#include <media/UidPolicyInterface.h>
+#include <utils/String8.h>
+#include <utils/Vector.h>
+
+#include <list>
+#include <map>
+#include <mutex>
+
+namespace android {
+using ::aidl::android::media::TranscodingResultParcel;
+using ::aidl::android::media::TranscodingSessionPriority;
+
+class TranscodingSessionController : public UidPolicyCallbackInterface,
+                                     public ControllerClientInterface,
+                                     public TranscoderCallbackInterface,
+                                     public ResourcePolicyCallbackInterface {
+public:
+    virtual ~TranscodingSessionController();
+
+    // ControllerClientInterface
+    bool submit(ClientIdType clientId, SessionIdType sessionId, uid_t uid,
+                const TranscodingRequestParcel& request,
+                const std::weak_ptr<ITranscodingClientCallback>& clientCallback) override;
+    bool cancel(ClientIdType clientId, SessionIdType sessionId) override;
+    bool getSession(ClientIdType clientId, SessionIdType sessionId,
+                    TranscodingRequestParcel* request) override;
+    // ~ControllerClientInterface
+
+    // TranscoderCallbackInterface
+    void onStarted(ClientIdType clientId, SessionIdType sessionId) override;
+    void onPaused(ClientIdType clientId, SessionIdType sessionId) override;
+    void onResumed(ClientIdType clientId, SessionIdType sessionId) override;
+    void onFinish(ClientIdType clientId, SessionIdType sessionId) override;
+    void onError(ClientIdType clientId, SessionIdType sessionId, TranscodingErrorCode err) override;
+    void onProgressUpdate(ClientIdType clientId, SessionIdType sessionId,
+                          int32_t progress) override;
+    void onResourceLost() override;
+    // ~TranscoderCallbackInterface
+
+    // UidPolicyCallbackInterface
+    void onTopUidsChanged(const std::unordered_set<uid_t>& uids) override;
+    // ~UidPolicyCallbackInterface
+
+    // ResourcePolicyCallbackInterface
+    void onResourceAvailable() override;
+    // ~ResourcePolicyCallbackInterface
+
+    /**
+     * Dump all the session information to the fd.
+     */
+    void dumpAllSessions(int fd, const Vector<String16>& args);
+
+private:
+    friend class MediaTranscodingService;
+    friend class TranscodingSessionControllerTest;
+
+    using SessionKeyType = std::pair<ClientIdType, SessionIdType>;
+    using SessionQueueType = std::list<SessionKeyType>;
+
+    struct Session {
+        SessionKeyType key;
+        uid_t uid;
+        enum State {
+            NOT_STARTED,
+            RUNNING,
+            PAUSED,
+        } state;
+        int32_t lastProgress;
+        TranscodingRequest request;
+        std::weak_ptr<ITranscodingClientCallback> callback;
+    };
+
+    // TODO(chz): call transcoder without global lock.
+    // Use mLock for all entrypoints for now.
+    mutable std::mutex mLock;
+
+    std::map<SessionKeyType, Session> mSessionMap;
+
+    // uid->SessionQueue map (uid == -1: offline queue)
+    std::map<uid_t, SessionQueueType> mSessionQueues;
+
+    // uids, with the head being the most-recently-top app, 2nd item is the
+    // previous top app, etc.
+    std::list<uid_t> mUidSortedList;
+    std::list<uid_t>::iterator mOfflineUidIterator;
+    std::map<uid_t, std::string> mUidPackageNames;
+
+    std::shared_ptr<TranscoderInterface> mTranscoder;
+    std::shared_ptr<UidPolicyInterface> mUidPolicy;
+    std::shared_ptr<ResourcePolicyInterface> mResourcePolicy;
+
+    Session* mCurrentSession;
+    bool mResourceLost;
+
+    // Only allow MediaTranscodingService and unit tests to instantiate.
+    TranscodingSessionController(const std::shared_ptr<TranscoderInterface>& transcoder,
+                                 const std::shared_ptr<UidPolicyInterface>& uidPolicy,
+                                 const std::shared_ptr<ResourcePolicyInterface>& resourcePolicy);
+
+    Session* getTopSession_l();
+    void updateCurrentSession_l();
+    void removeSession_l(const SessionKeyType& sessionKey);
+    void moveUidsToTop_l(const std::unordered_set<uid_t>& uids, bool preserveTopUid);
+    void notifyClient(ClientIdType clientId, SessionIdType sessionId, const char* reason,
+                      std::function<void(const SessionKeyType&)> func);
+    // Internal state verifier (debug only)
+    void validateState_l();
+
+    static String8 sessionToString(const SessionKeyType& sessionKey);
+    static const char* sessionStateToString(const Session::State sessionState);
+};
+
+}  // namespace android
+#endif  // ANDROID_MEDIA_TRANSCODING_SESSION_CONTROLLER_H
diff --git a/media/libmediatranscoding/include/media/TranscodingUidPolicy.h b/media/libmediatranscoding/include/media/TranscodingUidPolicy.h
index f02b591..4c642de 100644
--- a/media/libmediatranscoding/include/media/TranscodingUidPolicy.h
+++ b/media/libmediatranscoding/include/media/TranscodingUidPolicy.h
@@ -35,7 +35,7 @@
 
 class ActivityManager;
 // Observer for UID lifecycle and provide information about the uid's app
-// priority used by the job scheduler.
+// priority used by the session controller.
 class TranscodingUidPolicy : public UidPolicyInterface {
 public:
     explicit TranscodingUidPolicy();
@@ -49,7 +49,6 @@
     void setCallback(const std::shared_ptr<UidPolicyCallbackInterface>& cb) override;
     // ~UidPolicyInterface
 
-    static bool getNamesForUids(const std::vector<int32_t>& uids, std::vector<std::string>* names);
     static status_t getUidForPackage(String16 packageName, /*inout*/ uid_t& uid);
 
 private:
diff --git a/media/libmediatranscoding/include/media/UidPolicyInterface.h b/media/libmediatranscoding/include/media/UidPolicyInterface.h
index f88c1ed..05d8db0 100644
--- a/media/libmediatranscoding/include/media/UidPolicyInterface.h
+++ b/media/libmediatranscoding/include/media/UidPolicyInterface.h
@@ -23,7 +23,7 @@
 
 class UidPolicyCallbackInterface;
 
-// Interface for the scheduler to query a uid's info.
+// Interface for the controller to query a uid's info.
 class UidPolicyInterface {
 public:
     // Instruct the uid policy to start monitoring a uid.
@@ -41,7 +41,7 @@
     virtual ~UidPolicyInterface() = default;
 };
 
-// Interface for notifying the scheduler of a change in uid states.
+// Interface for notifying the controller of a change in uid states.
 class UidPolicyCallbackInterface {
 public:
     // Called when the set of uids that's top priority among the uids of interest
diff --git a/media/libmediatranscoding/tests/AdjustableMaxPriorityQueue_tests.cpp b/media/libmediatranscoding/tests/AdjustableMaxPriorityQueue_tests.cpp
index 2e49f32..a35ca53 100644
--- a/media/libmediatranscoding/tests/AdjustableMaxPriorityQueue_tests.cpp
+++ b/media/libmediatranscoding/tests/AdjustableMaxPriorityQueue_tests.cpp
@@ -223,19 +223,19 @@
 }
 
 // Test the heap property and make sure it is the same as std::priority_queue.
-TEST(AdjustableMaxPriorityQueueTest, TranscodingJobTest) {
-    // Test data structure that mimics the Transcoding job.
-    struct TranscodingJob {
+TEST(AdjustableMaxPriorityQueueTest, TranscodingSessionTest) {
+    // Test data structure that mimics the Transcoding session.
+    struct TranscodingSession {
         int32_t priority;
         int64_t createTimeUs;
     };
 
-    // The job is arranging according to priority with highest priority comes first.
-    // For the job with the same priority, the job with early createTime will come first.
-    class TranscodingJobComp {
+    // The session is arranging according to priority with highest priority comes first.
+    // For the session with the same priority, the session with early createTime will come first.
+    class TranscodingSessionComp {
     public:
-        bool operator()(const std::unique_ptr<TranscodingJob>& lhs,
-                        const std::unique_ptr<TranscodingJob>& rhs) const {
+        bool operator()(const std::unique_ptr<TranscodingSession>& lhs,
+                        const std::unique_ptr<TranscodingSession>& rhs) const {
             if (lhs->priority != rhs->priority) {
                 return lhs->priority < rhs->priority;
             }
@@ -244,46 +244,47 @@
     };
 
     // Map to save each value's position in the heap.
-    std::unordered_map<int, TranscodingJob*> jobIdToJobMap;
+    std::unordered_map<int, TranscodingSession*> sessionIdToSessionMap;
 
-    TranscodingJob testJobs[] = {
-            {1 /*priority*/, 66 /*createTimeUs*/},  // First job,
-            {2 /*priority*/, 67 /*createTimeUs*/},  // Second job,
-            {2 /*priority*/, 66 /*createTimeUs*/},  // Third job,
-            {3 /*priority*/, 68 /*createTimeUs*/},  // Fourth job.
+    TranscodingSession testSessions[] = {
+            {1 /*priority*/, 66 /*createTimeUs*/},  // First session,
+            {2 /*priority*/, 67 /*createTimeUs*/},  // Second session,
+            {2 /*priority*/, 66 /*createTimeUs*/},  // Third session,
+            {3 /*priority*/, 68 /*createTimeUs*/},  // Fourth session.
     };
 
-    AdjustableMaxPriorityQueue<std::unique_ptr<TranscodingJob>, TranscodingJobComp> jobQueue;
+    AdjustableMaxPriorityQueue<std::unique_ptr<TranscodingSession>, TranscodingSessionComp>
+            sessionQueue;
 
-    // Pushes all the jobs into the heap.
-    for (int jobId = 0; jobId < 4; ++jobId) {
-        auto newJob = std::make_unique<TranscodingJob>(testJobs[jobId]);
-        jobIdToJobMap[jobId] = newJob.get();
-        EXPECT_TRUE(jobQueue.push(std::move(newJob)));
+    // Pushes all the sessions into the heap.
+    for (int sessionId = 0; sessionId < 4; ++sessionId) {
+        auto newSession = std::make_unique<TranscodingSession>(testSessions[sessionId]);
+        sessionIdToSessionMap[sessionId] = newSession.get();
+        EXPECT_TRUE(sessionQueue.push(std::move(newSession)));
     }
 
-    // Check the job queue size.
-    EXPECT_EQ(4, jobQueue.size());
+    // Check the session queue size.
+    EXPECT_EQ(4, sessionQueue.size());
 
-    // Check the top and it should be Forth job: (3, 68)
-    const std::unique_ptr<TranscodingJob>& topJob = jobQueue.top();
-    EXPECT_EQ(3, topJob->priority);
-    EXPECT_EQ(68, topJob->createTimeUs);
+    // Check the top and it should be Forth session: (3, 68)
+    const std::unique_ptr<TranscodingSession>& topSession = sessionQueue.top();
+    EXPECT_EQ(3, topSession->priority);
+    EXPECT_EQ(68, topSession->createTimeUs);
 
     // Consume the top.
-    std::unique_ptr<TranscodingJob> consumeJob = jobQueue.consume_top();
+    std::unique_ptr<TranscodingSession> consumeSession = sessionQueue.consume_top();
 
-    // Check the top and it should be Third Job (2, 66)
-    const std::unique_ptr<TranscodingJob>& topJob2 = jobQueue.top();
-    EXPECT_EQ(2, topJob2->priority);
-    EXPECT_EQ(66, topJob2->createTimeUs);
+    // Check the top and it should be Third Session (2, 66)
+    const std::unique_ptr<TranscodingSession>& topSession2 = sessionQueue.top();
+    EXPECT_EQ(2, topSession2->priority);
+    EXPECT_EQ(66, topSession2->createTimeUs);
 
-    // Change the Second job's priority to 4 from (2, 67) -> (4, 67). It should becomes top of the
-    // queue.
-    jobIdToJobMap[1]->priority = 4;
-    jobQueue.rebuild();
-    const std::unique_ptr<TranscodingJob>& topJob3 = jobQueue.top();
-    EXPECT_EQ(4, topJob3->priority);
-    EXPECT_EQ(67, topJob3->createTimeUs);
+    // Change the Second session's priority to 4 from (2, 67) -> (4, 67). It should becomes
+    // top of the queue.
+    sessionIdToSessionMap[1]->priority = 4;
+    sessionQueue.rebuild();
+    const std::unique_ptr<TranscodingSession>& topSession3 = sessionQueue.top();
+    EXPECT_EQ(4, topSession3->priority);
+    EXPECT_EQ(67, topSession3->createTimeUs);
 }
 }  // namespace android
\ No newline at end of file
diff --git a/media/libmediatranscoding/tests/Android.bp b/media/libmediatranscoding/tests/Android.bp
index b54022a..7b15b1b 100644
--- a/media/libmediatranscoding/tests/Android.bp
+++ b/media/libmediatranscoding/tests/Android.bp
@@ -38,13 +38,13 @@
 }
 
 //
-// TranscodingJobScheduler unit test
+// TranscodingSessionController unit test
 //
 cc_test {
-    name: "TranscodingJobScheduler_tests",
+    name: "TranscodingSessionController_tests",
     defaults: ["libmediatranscoding_test_defaults"],
 
-    srcs: ["TranscodingJobScheduler_tests.cpp"],
+    srcs: ["TranscodingSessionController_tests.cpp"],
 }
 
 //
diff --git a/media/libmediatranscoding/tests/TranscodingClientManager_tests.cpp b/media/libmediatranscoding/tests/TranscodingClientManager_tests.cpp
index c2ca9b4..1a50923 100644
--- a/media/libmediatranscoding/tests/TranscodingClientManager_tests.cpp
+++ b/media/libmediatranscoding/tests/TranscodingClientManager_tests.cpp
@@ -25,7 +25,7 @@
 #include <android/binder_manager.h>
 #include <android/binder_process.h>
 #include <gtest/gtest.h>
-#include <media/SchedulerClientInterface.h>
+#include <media/ControllerClientInterface.h>
 #include <media/TranscodingClientManager.h>
 #include <media/TranscodingRequest.h>
 #include <utils/Log.h>
@@ -38,10 +38,10 @@
 using ::aidl::android::media::BnTranscodingClientCallback;
 using ::aidl::android::media::IMediaTranscodingService;
 using ::aidl::android::media::TranscodingErrorCode;
-using ::aidl::android::media::TranscodingJobParcel;
-using ::aidl::android::media::TranscodingJobPriority;
 using ::aidl::android::media::TranscodingRequestParcel;
 using ::aidl::android::media::TranscodingResultParcel;
+using ::aidl::android::media::TranscodingSessionParcel;
+using ::aidl::android::media::TranscodingSessionPriority;
 
 constexpr pid_t kInvalidClientPid = -5;
 constexpr pid_t kInvalidClientUid = -10;
@@ -51,7 +51,7 @@
 constexpr const char* kClientName = "TestClientName";
 constexpr const char* kClientPackage = "TestClientPackage";
 
-#define JOB(n) (n)
+#define SESSION(n) (n)
 
 struct TestClientCallback : public BnTranscodingClientCallback {
     TestClientCallback() { ALOGI("TestClientCallback Created"); }
@@ -63,30 +63,32 @@
         return Status::ok();
     }
 
-    Status onTranscodingStarted(int32_t /*in_jobId*/) override { return Status::ok(); }
+    Status onTranscodingStarted(int32_t /*in_sessionId*/) override { return Status::ok(); }
 
-    Status onTranscodingPaused(int32_t /*in_jobId*/) override { return Status::ok(); }
+    Status onTranscodingPaused(int32_t /*in_sessionId*/) override { return Status::ok(); }
 
-    Status onTranscodingResumed(int32_t /*in_jobId*/) override { return Status::ok(); }
+    Status onTranscodingResumed(int32_t /*in_sessionId*/) override { return Status::ok(); }
 
-    Status onTranscodingFinished(int32_t in_jobId,
+    Status onTranscodingFinished(int32_t in_sessionId,
                                  const TranscodingResultParcel& in_result) override {
-        EXPECT_EQ(in_jobId, in_result.jobId);
-        mEventQueue.push_back(Finished(in_jobId));
+        EXPECT_EQ(in_sessionId, in_result.sessionId);
+        mEventQueue.push_back(Finished(in_sessionId));
         return Status::ok();
     }
 
-    Status onTranscodingFailed(int32_t in_jobId, TranscodingErrorCode /*in_errorCode */) override {
-        mEventQueue.push_back(Failed(in_jobId));
+    Status onTranscodingFailed(int32_t in_sessionId,
+                               TranscodingErrorCode /*in_errorCode */) override {
+        mEventQueue.push_back(Failed(in_sessionId));
         return Status::ok();
     }
 
-    Status onAwaitNumberOfJobsChanged(int32_t /* in_jobId */, int32_t /* in_oldAwaitNumber */,
-                                      int32_t /* in_newAwaitNumber */) override {
+    Status onAwaitNumberOfSessionsChanged(int32_t /* in_sessionId */,
+                                          int32_t /* in_oldAwaitNumber */,
+                                          int32_t /* in_newAwaitNumber */) override {
         return Status::ok();
     }
 
-    Status onProgressUpdate(int32_t /* in_jobId */, int32_t /* in_progress */) override {
+    Status onProgressUpdate(int32_t /* in_sessionId */, int32_t /* in_progress */) override {
         return Status::ok();
     }
 
@@ -96,12 +98,12 @@
             Finished,
             Failed,
         } type;
-        JobIdType jobId;
+        SessionIdType sessionId;
     };
 
     static constexpr Event NoEvent = {Event::NoEvent, 0};
 #define DECLARE_EVENT(action) \
-    static Event action(JobIdType jobId) { return {Event::action, jobId}; }
+    static Event action(SessionIdType sessionId) { return {Event::action, sessionId}; }
 
     DECLARE_EVENT(Finished);
     DECLARE_EVENT(Failed);
@@ -125,102 +127,102 @@
 };
 
 bool operator==(const TestClientCallback::Event& lhs, const TestClientCallback::Event& rhs) {
-    return lhs.type == rhs.type && lhs.jobId == rhs.jobId;
+    return lhs.type == rhs.type && lhs.sessionId == rhs.sessionId;
 }
 
-struct TestScheduler : public SchedulerClientInterface {
-    TestScheduler() { ALOGI("TestScheduler Created"); }
+struct TestController : public ControllerClientInterface {
+    TestController() { ALOGI("TestController Created"); }
 
-    virtual ~TestScheduler() { ALOGI("TestScheduler Destroyed"); }
+    virtual ~TestController() { ALOGI("TestController Destroyed"); }
 
-    bool submit(ClientIdType clientId, JobIdType jobId, uid_t /*uid*/,
+    bool submit(ClientIdType clientId, SessionIdType sessionId, uid_t /*uid*/,
                 const TranscodingRequestParcel& request,
                 const std::weak_ptr<ITranscodingClientCallback>& clientCallback) override {
-        JobKeyType jobKey = std::make_pair(clientId, jobId);
-        if (mJobs.count(jobKey) > 0) {
+        SessionKeyType sessionKey = std::make_pair(clientId, sessionId);
+        if (mSessions.count(sessionKey) > 0) {
             return false;
         }
 
         // This is the secret name we'll check, to test error propagation from
-        // the scheduler back to client.
+        // the controller back to client.
         if (request.sourceFilePath == "bad_source_file") {
             return false;
         }
 
-        mJobs[jobKey].request = request;
-        mJobs[jobKey].callback = clientCallback;
+        mSessions[sessionKey].request = request;
+        mSessions[sessionKey].callback = clientCallback;
 
-        mLastJob = jobKey;
+        mLastSession = sessionKey;
         return true;
     }
 
-    bool cancel(ClientIdType clientId, JobIdType jobId) override {
-        JobKeyType jobKey = std::make_pair(clientId, jobId);
+    bool cancel(ClientIdType clientId, SessionIdType sessionId) override {
+        SessionKeyType sessionKey = std::make_pair(clientId, sessionId);
 
-        if (mJobs.count(jobKey) == 0) {
+        if (mSessions.count(sessionKey) == 0) {
             return false;
         }
-        mJobs.erase(jobKey);
+        mSessions.erase(sessionKey);
         return true;
     }
 
-    bool getJob(ClientIdType clientId, JobIdType jobId,
-                TranscodingRequestParcel* request) override {
-        JobKeyType jobKey = std::make_pair(clientId, jobId);
-        if (mJobs.count(jobKey) == 0) {
+    bool getSession(ClientIdType clientId, SessionIdType sessionId,
+                    TranscodingRequestParcel* request) override {
+        SessionKeyType sessionKey = std::make_pair(clientId, sessionId);
+        if (mSessions.count(sessionKey) == 0) {
             return false;
         }
 
-        *(TranscodingRequest*)request = mJobs[jobKey].request;
+        *(TranscodingRequest*)request = mSessions[sessionKey].request;
         return true;
     }
 
-    void finishLastJob() {
-        auto it = mJobs.find(mLastJob);
-        if (it == mJobs.end()) {
+    void finishLastSession() {
+        auto it = mSessions.find(mLastSession);
+        if (it == mSessions.end()) {
             return;
         }
         {
             auto clientCallback = it->second.callback.lock();
             if (clientCallback != nullptr) {
                 clientCallback->onTranscodingFinished(
-                        mLastJob.second,
-                        TranscodingResultParcel({mLastJob.second, 0, std::nullopt}));
+                        mLastSession.second,
+                        TranscodingResultParcel({mLastSession.second, 0, std::nullopt}));
             }
         }
-        mJobs.erase(it);
+        mSessions.erase(it);
     }
 
-    void abortLastJob() {
-        auto it = mJobs.find(mLastJob);
-        if (it == mJobs.end()) {
+    void abortLastSession() {
+        auto it = mSessions.find(mLastSession);
+        if (it == mSessions.end()) {
             return;
         }
         {
             auto clientCallback = it->second.callback.lock();
             if (clientCallback != nullptr) {
-                clientCallback->onTranscodingFailed(mLastJob.second,
+                clientCallback->onTranscodingFailed(mLastSession.second,
                                                     TranscodingErrorCode::kUnknown);
             }
         }
-        mJobs.erase(it);
+        mSessions.erase(it);
     }
 
-    struct Job {
+    struct Session {
         TranscodingRequest request;
         std::weak_ptr<ITranscodingClientCallback> callback;
     };
 
-    typedef std::pair<ClientIdType, JobIdType> JobKeyType;
-    std::map<JobKeyType, Job> mJobs;
-    JobKeyType mLastJob;
+    typedef std::pair<ClientIdType, SessionIdType> SessionKeyType;
+    std::map<SessionKeyType, Session> mSessions;
+    SessionKeyType mLastSession;
 };
 
 class TranscodingClientManagerTest : public ::testing::Test {
 public:
     TranscodingClientManagerTest()
-          : mScheduler(new TestScheduler()),
-            mClientManager(new TranscodingClientManager(mScheduler)) {
+          : mController(new TestController()),
+            mClientManager(new TranscodingClientManager(mController)) {
         ALOGD("TranscodingClientManagerTest created");
     }
 
@@ -260,7 +262,7 @@
         EXPECT_EQ(mClientManager->getNumOfClients(), 0);
     }
 
-    std::shared_ptr<TestScheduler> mScheduler;
+    std::shared_ptr<TestController> mController;
     std::shared_ptr<TranscodingClientManager> mClientManager;
     std::shared_ptr<ITranscodingClient> mClient1;
     std::shared_ptr<ITranscodingClient> mClient2;
@@ -349,36 +351,36 @@
     unregisterMultipleClients();
 }
 
-TEST_F(TranscodingClientManagerTest, TestSubmitCancelGetJobs) {
+TEST_F(TranscodingClientManagerTest, TestSubmitCancelGetSessions) {
     addMultipleClients();
 
-    // Test jobId assignment.
+    // Test sessionId assignment.
     TranscodingRequestParcel request;
     request.sourceFilePath = "test_source_file_0";
     request.destinationFilePath = "test_desintaion_file_0";
-    TranscodingJobParcel job;
+    TranscodingSessionParcel session;
     bool result;
-    EXPECT_TRUE(mClient1->submitRequest(request, &job, &result).isOk());
+    EXPECT_TRUE(mClient1->submitRequest(request, &session, &result).isOk());
     EXPECT_TRUE(result);
-    EXPECT_EQ(job.jobId, JOB(0));
+    EXPECT_EQ(session.sessionId, SESSION(0));
 
     request.sourceFilePath = "test_source_file_1";
     request.destinationFilePath = "test_desintaion_file_1";
-    EXPECT_TRUE(mClient1->submitRequest(request, &job, &result).isOk());
+    EXPECT_TRUE(mClient1->submitRequest(request, &session, &result).isOk());
     EXPECT_TRUE(result);
-    EXPECT_EQ(job.jobId, JOB(1));
+    EXPECT_EQ(session.sessionId, SESSION(1));
 
     request.sourceFilePath = "test_source_file_2";
     request.destinationFilePath = "test_desintaion_file_2";
-    EXPECT_TRUE(mClient1->submitRequest(request, &job, &result).isOk());
+    EXPECT_TRUE(mClient1->submitRequest(request, &session, &result).isOk());
     EXPECT_TRUE(result);
-    EXPECT_EQ(job.jobId, JOB(2));
+    EXPECT_EQ(session.sessionId, SESSION(2));
 
     // Test submit bad request (no valid sourceFilePath) fails.
     TranscodingRequestParcel badRequest;
     badRequest.sourceFilePath = "bad_source_file";
     badRequest.destinationFilePath = "bad_destination_file";
-    EXPECT_TRUE(mClient1->submitRequest(badRequest, &job, &result).isOk());
+    EXPECT_TRUE(mClient1->submitRequest(badRequest, &session, &result).isOk());
     EXPECT_FALSE(result);
 
     // Test submit with bad pid/uid.
@@ -386,49 +388,49 @@
     badRequest.destinationFilePath = "test_desintaion_file_3";
     badRequest.clientPid = kInvalidClientPid;
     badRequest.clientUid = kInvalidClientUid;
-    EXPECT_TRUE(mClient1->submitRequest(badRequest, &job, &result).isOk());
+    EXPECT_TRUE(mClient1->submitRequest(badRequest, &session, &result).isOk());
     EXPECT_FALSE(result);
 
-    // Test get jobs by id.
-    EXPECT_TRUE(mClient1->getJobWithId(JOB(2), &job, &result).isOk());
-    EXPECT_EQ(job.jobId, JOB(2));
-    EXPECT_EQ(job.request.sourceFilePath, "test_source_file_2");
+    // Test get sessions by id.
+    EXPECT_TRUE(mClient1->getSessionWithId(SESSION(2), &session, &result).isOk());
+    EXPECT_EQ(session.sessionId, SESSION(2));
+    EXPECT_EQ(session.request.sourceFilePath, "test_source_file_2");
     EXPECT_TRUE(result);
 
-    // Test get jobs by invalid id fails.
-    EXPECT_TRUE(mClient1->getJobWithId(JOB(100), &job, &result).isOk());
+    // Test get sessions by invalid id fails.
+    EXPECT_TRUE(mClient1->getSessionWithId(SESSION(100), &session, &result).isOk());
     EXPECT_FALSE(result);
 
-    // Test cancel non-existent job fail.
-    EXPECT_TRUE(mClient2->cancelJob(JOB(100), &result).isOk());
+    // Test cancel non-existent session fail.
+    EXPECT_TRUE(mClient2->cancelSession(SESSION(100), &result).isOk());
     EXPECT_FALSE(result);
 
-    // Test cancel valid jobId in arbitrary order.
-    EXPECT_TRUE(mClient1->cancelJob(JOB(2), &result).isOk());
+    // Test cancel valid sessionId in arbitrary order.
+    EXPECT_TRUE(mClient1->cancelSession(SESSION(2), &result).isOk());
     EXPECT_TRUE(result);
 
-    EXPECT_TRUE(mClient1->cancelJob(JOB(0), &result).isOk());
+    EXPECT_TRUE(mClient1->cancelSession(SESSION(0), &result).isOk());
     EXPECT_TRUE(result);
 
-    EXPECT_TRUE(mClient1->cancelJob(JOB(1), &result).isOk());
+    EXPECT_TRUE(mClient1->cancelSession(SESSION(1), &result).isOk());
     EXPECT_TRUE(result);
 
-    // Test cancel job again fails.
-    EXPECT_TRUE(mClient1->cancelJob(JOB(1), &result).isOk());
+    // Test cancel session again fails.
+    EXPECT_TRUE(mClient1->cancelSession(SESSION(1), &result).isOk());
     EXPECT_FALSE(result);
 
-    // Test get job after cancel fails.
-    EXPECT_TRUE(mClient1->getJobWithId(JOB(2), &job, &result).isOk());
+    // Test get session after cancel fails.
+    EXPECT_TRUE(mClient1->getSessionWithId(SESSION(2), &session, &result).isOk());
     EXPECT_FALSE(result);
 
-    // Test jobId independence for each client.
-    EXPECT_TRUE(mClient2->submitRequest(request, &job, &result).isOk());
+    // Test sessionId independence for each client.
+    EXPECT_TRUE(mClient2->submitRequest(request, &session, &result).isOk());
     EXPECT_TRUE(result);
-    EXPECT_EQ(job.jobId, JOB(0));
+    EXPECT_EQ(session.sessionId, SESSION(0));
 
-    EXPECT_TRUE(mClient2->submitRequest(request, &job, &result).isOk());
+    EXPECT_TRUE(mClient2->submitRequest(request, &session, &result).isOk());
     EXPECT_TRUE(result);
-    EXPECT_EQ(job.jobId, JOB(1));
+    EXPECT_EQ(session.sessionId, SESSION(1));
 
     unregisterMultipleClients();
 }
@@ -439,32 +441,32 @@
     TranscodingRequestParcel request;
     request.sourceFilePath = "test_source_file_name";
     request.destinationFilePath = "test_destination_file_name";
-    TranscodingJobParcel job;
+    TranscodingSessionParcel session;
     bool result;
-    EXPECT_TRUE(mClient1->submitRequest(request, &job, &result).isOk());
+    EXPECT_TRUE(mClient1->submitRequest(request, &session, &result).isOk());
     EXPECT_TRUE(result);
-    EXPECT_EQ(job.jobId, JOB(0));
+    EXPECT_EQ(session.sessionId, SESSION(0));
 
-    mScheduler->finishLastJob();
-    EXPECT_EQ(mClientCallback1->popEvent(), TestClientCallback::Finished(job.jobId));
+    mController->finishLastSession();
+    EXPECT_EQ(mClientCallback1->popEvent(), TestClientCallback::Finished(session.sessionId));
 
-    EXPECT_TRUE(mClient1->submitRequest(request, &job, &result).isOk());
+    EXPECT_TRUE(mClient1->submitRequest(request, &session, &result).isOk());
     EXPECT_TRUE(result);
-    EXPECT_EQ(job.jobId, JOB(1));
+    EXPECT_EQ(session.sessionId, SESSION(1));
 
-    mScheduler->abortLastJob();
-    EXPECT_EQ(mClientCallback1->popEvent(), TestClientCallback::Failed(job.jobId));
+    mController->abortLastSession();
+    EXPECT_EQ(mClientCallback1->popEvent(), TestClientCallback::Failed(session.sessionId));
 
-    EXPECT_TRUE(mClient1->submitRequest(request, &job, &result).isOk());
+    EXPECT_TRUE(mClient1->submitRequest(request, &session, &result).isOk());
     EXPECT_TRUE(result);
-    EXPECT_EQ(job.jobId, JOB(2));
+    EXPECT_EQ(session.sessionId, SESSION(2));
 
-    EXPECT_TRUE(mClient2->submitRequest(request, &job, &result).isOk());
+    EXPECT_TRUE(mClient2->submitRequest(request, &session, &result).isOk());
     EXPECT_TRUE(result);
-    EXPECT_EQ(job.jobId, JOB(0));
+    EXPECT_EQ(session.sessionId, SESSION(0));
 
-    mScheduler->finishLastJob();
-    EXPECT_EQ(mClientCallback2->popEvent(), TestClientCallback::Finished(job.jobId));
+    mController->finishLastSession();
+    EXPECT_EQ(mClientCallback2->popEvent(), TestClientCallback::Finished(session.sessionId));
 
     unregisterMultipleClients();
 }
@@ -479,20 +481,20 @@
 
     // Submit 2 requests, 1 offline and 1 realtime.
     TranscodingRequestParcel request;
-    TranscodingJobParcel job;
+    TranscodingSessionParcel session;
     bool result;
 
     request.sourceFilePath = "test_source_file_0";
     request.destinationFilePath = "test_destination_file_0";
-    request.priority = TranscodingJobPriority::kUnspecified;
-    EXPECT_TRUE(client->submitRequest(request, &job, &result).isOk() && result);
-    EXPECT_EQ(job.jobId, JOB(0));
+    request.priority = TranscodingSessionPriority::kUnspecified;
+    EXPECT_TRUE(client->submitRequest(request, &session, &result).isOk() && result);
+    EXPECT_EQ(session.sessionId, SESSION(0));
 
     request.sourceFilePath = "test_source_file_1";
     request.destinationFilePath = "test_destination_file_1";
-    request.priority = TranscodingJobPriority::kNormal;
-    EXPECT_TRUE(client->submitRequest(request, &job, &result).isOk() && result);
-    EXPECT_EQ(job.jobId, JOB(1));
+    request.priority = TranscodingSessionPriority::kNormal;
+    EXPECT_TRUE(client->submitRequest(request, &session, &result).isOk() && result);
+    EXPECT_EQ(session.sessionId, SESSION(1));
 
     // Unregister client, should succeed.
     Status status = client->unregister();
@@ -501,36 +503,36 @@
     // Test submit new request after unregister, should fail with ERROR_DISCONNECTED.
     request.sourceFilePath = "test_source_file_2";
     request.destinationFilePath = "test_destination_file_2";
-    request.priority = TranscodingJobPriority::kNormal;
-    status = client->submitRequest(request, &job, &result);
+    request.priority = TranscodingSessionPriority::kNormal;
+    status = client->submitRequest(request, &session, &result);
     EXPECT_FALSE(status.isOk());
     EXPECT_EQ(status.getServiceSpecificError(), IMediaTranscodingService::ERROR_DISCONNECTED);
 
-    // Test cancel jobs after unregister, should fail with ERROR_DISCONNECTED
-    // regardless of realtime or offline job, or whether the jobId is valid.
-    status = client->cancelJob(JOB(0), &result);
+    // Test cancel sessions after unregister, should fail with ERROR_DISCONNECTED
+    // regardless of realtime or offline session, or whether the sessionId is valid.
+    status = client->cancelSession(SESSION(0), &result);
     EXPECT_FALSE(status.isOk());
     EXPECT_EQ(status.getServiceSpecificError(), IMediaTranscodingService::ERROR_DISCONNECTED);
 
-    status = client->cancelJob(JOB(1), &result);
+    status = client->cancelSession(SESSION(1), &result);
     EXPECT_FALSE(status.isOk());
     EXPECT_EQ(status.getServiceSpecificError(), IMediaTranscodingService::ERROR_DISCONNECTED);
 
-    status = client->cancelJob(JOB(2), &result);
+    status = client->cancelSession(SESSION(2), &result);
     EXPECT_FALSE(status.isOk());
     EXPECT_EQ(status.getServiceSpecificError(), IMediaTranscodingService::ERROR_DISCONNECTED);
 
-    // Test get jobs, should fail with ERROR_DISCONNECTED regardless of realtime
-    // or offline job, or whether the jobId is valid.
-    status = client->getJobWithId(JOB(0), &job, &result);
+    // Test get sessions, should fail with ERROR_DISCONNECTED regardless of realtime
+    // or offline session, or whether the sessionId is valid.
+    status = client->getSessionWithId(SESSION(0), &session, &result);
     EXPECT_FALSE(status.isOk());
     EXPECT_EQ(status.getServiceSpecificError(), IMediaTranscodingService::ERROR_DISCONNECTED);
 
-    status = client->getJobWithId(JOB(1), &job, &result);
+    status = client->getSessionWithId(SESSION(1), &session, &result);
     EXPECT_FALSE(status.isOk());
     EXPECT_EQ(status.getServiceSpecificError(), IMediaTranscodingService::ERROR_DISCONNECTED);
 
-    status = client->getJobWithId(JOB(2), &job, &result);
+    status = client->getSessionWithId(SESSION(2), &session, &result);
     EXPECT_FALSE(status.isOk());
     EXPECT_EQ(status.getServiceSpecificError(), IMediaTranscodingService::ERROR_DISCONNECTED);
 }
diff --git a/media/libmediatranscoding/tests/TranscodingJobScheduler_tests.cpp b/media/libmediatranscoding/tests/TranscodingJobScheduler_tests.cpp
deleted file mode 100644
index 9b9df87..0000000
--- a/media/libmediatranscoding/tests/TranscodingJobScheduler_tests.cpp
+++ /dev/null
@@ -1,603 +0,0 @@
-/*
- * 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.
- */
-
-// Unit Test for TranscodingJobScheduler
-
-// #define LOG_NDEBUG 0
-#define LOG_TAG "TranscodingJobSchedulerTest"
-
-#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 <android-base/logging.h>
-#include <android/binder_manager.h>
-#include <android/binder_process.h>
-#include <gtest/gtest.h>
-#include <media/TranscodingClientManager.h>
-#include <media/TranscodingJobScheduler.h>
-#include <utils/Log.h>
-
-#include <unordered_set>
-
-namespace android {
-
-using Status = ::ndk::ScopedAStatus;
-using aidl::android::media::BnTranscodingClientCallback;
-using aidl::android::media::IMediaTranscodingService;
-using aidl::android::media::ITranscodingClient;
-using aidl::android::media::TranscodingRequestParcel;
-
-constexpr ClientIdType kClientId = 1000;
-constexpr JobIdType kClientJobId = 0;
-constexpr uid_t kClientUid = 5000;
-constexpr uid_t kInvalidUid = (uid_t)-1;
-
-#define CLIENT(n) (kClientId + (n))
-#define JOB(n) (kClientJobId + (n))
-#define UID(n) (kClientUid + (n))
-
-class TestUidPolicy : public UidPolicyInterface {
-public:
-    TestUidPolicy() = default;
-    virtual ~TestUidPolicy() = default;
-
-    // UidPolicyInterface
-    void registerMonitorUid(uid_t /*uid*/) override {}
-    void unregisterMonitorUid(uid_t /*uid*/) override {}
-    bool isUidOnTop(uid_t uid) override { return mTopUids.count(uid) > 0; }
-    std::unordered_set<uid_t> getTopUids() const override { return mTopUids; }
-    void setCallback(const std::shared_ptr<UidPolicyCallbackInterface>& cb) override {
-        mUidPolicyCallback = cb;
-    }
-    void setTop(uid_t uid) {
-        std::unordered_set<uid_t> uids = {uid};
-        setTop(uids);
-    }
-    void setTop(const std::unordered_set<uid_t>& uids) {
-        mTopUids = uids;
-        auto uidPolicyCb = mUidPolicyCallback.lock();
-        if (uidPolicyCb != nullptr) {
-            uidPolicyCb->onTopUidsChanged(mTopUids);
-        }
-    }
-
-    std::unordered_set<uid_t> mTopUids;
-    std::weak_ptr<UidPolicyCallbackInterface> mUidPolicyCallback;
-};
-
-class TestTranscoder : public TranscoderInterface {
-public:
-    TestTranscoder() : mLastError(TranscodingErrorCode::kUnknown) {}
-    virtual ~TestTranscoder() {}
-
-    // TranscoderInterface
-    void setCallback(const std::shared_ptr<TranscoderCallbackInterface>& /*cb*/) 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 {
-        mEventQueue.push_back(Pause(clientId, jobId));
-    }
-    void resume(ClientIdType clientId, JobIdType jobId, const TranscodingRequestParcel& /*request*/,
-                const std::shared_ptr<ITranscodingClientCallback>& /*clientCallback*/) override {
-        mEventQueue.push_back(Resume(clientId, jobId));
-    }
-    void stop(ClientIdType clientId, JobIdType jobId) override {
-        mEventQueue.push_back(Stop(clientId, jobId));
-    }
-
-    void onFinished(ClientIdType clientId, JobIdType jobId) {
-        mEventQueue.push_back(Finished(clientId, jobId));
-    }
-
-    void onFailed(ClientIdType clientId, JobIdType jobId, TranscodingErrorCode err) {
-        mLastError = err;
-        mEventQueue.push_back(Failed(clientId, jobId));
-    }
-
-    TranscodingErrorCode getLastError() {
-        TranscodingErrorCode result = mLastError;
-        mLastError = TranscodingErrorCode::kUnknown;
-        return result;
-    }
-
-    struct Event {
-        enum { NoEvent, Start, Pause, Resume, Stop, Finished, Failed } type;
-        ClientIdType clientId;
-        JobIdType jobId;
-    };
-
-    static constexpr Event NoEvent = {Event::NoEvent, 0, 0};
-
-#define DECLARE_EVENT(action)                                     \
-    static Event action(ClientIdType clientId, JobIdType jobId) { \
-        return {Event::action, clientId, jobId};                  \
-    }
-
-    DECLARE_EVENT(Start);
-    DECLARE_EVENT(Pause);
-    DECLARE_EVENT(Resume);
-    DECLARE_EVENT(Stop);
-    DECLARE_EVENT(Finished);
-    DECLARE_EVENT(Failed);
-
-    const Event& popEvent() {
-        if (mEventQueue.empty()) {
-            mPoppedEvent = NoEvent;
-        } else {
-            mPoppedEvent = *mEventQueue.begin();
-            mEventQueue.pop_front();
-        }
-        return mPoppedEvent;
-    }
-
-private:
-    Event mPoppedEvent;
-    std::list<Event> mEventQueue;
-    TranscodingErrorCode mLastError;
-};
-
-bool operator==(const TestTranscoder::Event& lhs, const TestTranscoder::Event& rhs) {
-    return lhs.type == rhs.type && lhs.clientId == rhs.clientId && lhs.jobId == rhs.jobId;
-}
-
-struct TestClientCallback : public BnTranscodingClientCallback {
-    TestClientCallback(TestTranscoder* owner, int64_t clientId)
-          : mOwner(owner), mClientId(clientId) {
-        ALOGD("TestClient Created");
-    }
-
-    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 { return Status::ok(); }
-
-    Status onTranscodingPaused(int32_t /*in_jobId*/) override { return Status::ok(); }
-
-    Status onTranscodingResumed(int32_t /*in_jobId*/) override { return Status::ok(); }
-
-    Status onTranscodingFinished(int32_t in_jobId,
-                                 const TranscodingResultParcel& in_result) override {
-        EXPECT_EQ(in_jobId, in_result.jobId);
-        ALOGD("TestClientCallback: received onTranscodingFinished");
-        mOwner->onFinished(mClientId, in_jobId);
-        return Status::ok();
-    }
-
-    Status onTranscodingFailed(int32_t in_jobId, TranscodingErrorCode in_errorCode) override {
-        mOwner->onFailed(mClientId, in_jobId, in_errorCode);
-        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();
-    }
-
-    virtual ~TestClientCallback() { ALOGI("TestClient destroyed"); };
-
-private:
-    TestTranscoder* mOwner;
-    int64_t mClientId;
-    TestClientCallback(const TestClientCallback&) = delete;
-    TestClientCallback& operator=(const TestClientCallback&) = delete;
-};
-
-class TranscodingJobSchedulerTest : public ::testing::Test {
-public:
-    TranscodingJobSchedulerTest() { ALOGI("TranscodingJobSchedulerTest created"); }
-
-    void SetUp() override {
-        ALOGI("TranscodingJobSchedulerTest set up");
-        mTranscoder.reset(new TestTranscoder());
-        mUidPolicy.reset(new TestUidPolicy());
-        mScheduler.reset(
-                new TranscodingJobScheduler(mTranscoder, mUidPolicy, nullptr /*resourcePolicy*/));
-        mUidPolicy->setCallback(mScheduler);
-
-        // Set priority only, ignore other fields for now.
-        mOfflineRequest.priority = TranscodingJobPriority::kUnspecified;
-        mRealtimeRequest.priority = TranscodingJobPriority::kHigh;
-        mClientCallback0 =
-                ::ndk::SharedRefBase::make<TestClientCallback>(mTranscoder.get(), CLIENT(0));
-        mClientCallback1 =
-                ::ndk::SharedRefBase::make<TestClientCallback>(mTranscoder.get(), CLIENT(1));
-        mClientCallback2 =
-                ::ndk::SharedRefBase::make<TestClientCallback>(mTranscoder.get(), CLIENT(2));
-        mClientCallback3 =
-                ::ndk::SharedRefBase::make<TestClientCallback>(mTranscoder.get(), CLIENT(3));
-    }
-
-    void TearDown() override { ALOGI("TranscodingJobSchedulerTest tear down"); }
-
-    ~TranscodingJobSchedulerTest() { ALOGD("TranscodingJobSchedulerTest destroyed"); }
-
-    std::shared_ptr<TestTranscoder> mTranscoder;
-    std::shared_ptr<TestUidPolicy> mUidPolicy;
-    std::shared_ptr<TranscodingJobScheduler> mScheduler;
-    TranscodingRequestParcel mOfflineRequest;
-    TranscodingRequestParcel mRealtimeRequest;
-    std::shared_ptr<TestClientCallback> mClientCallback0;
-    std::shared_ptr<TestClientCallback> mClientCallback1;
-    std::shared_ptr<TestClientCallback> mClientCallback2;
-    std::shared_ptr<TestClientCallback> mClientCallback3;
-};
-
-TEST_F(TranscodingJobSchedulerTest, TestSubmitJob) {
-    ALOGD("TestSubmitJob");
-
-    // Start with UID(1) on top.
-    mUidPolicy->setTop(UID(1));
-
-    // Submit offline job to CLIENT(0) in UID(0).
-    // Should start immediately (because this is the only job).
-    mScheduler->submit(CLIENT(0), JOB(0), UID(0), mOfflineRequest, mClientCallback0);
-    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), 0));
-
-    // Submit real-time job to CLIENT(0).
-    // Should pause offline job and start new job,  even if UID(0) is not on top.
-    mScheduler->submit(CLIENT(0), JOB(1), UID(0), mRealtimeRequest, mClientCallback0);
-    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Pause(CLIENT(0), JOB(0)));
-    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), JOB(1)));
-
-    // Submit real-time job to CLIENT(0), should be queued after the previous job.
-    mScheduler->submit(CLIENT(0), JOB(2), UID(0), mRealtimeRequest, mClientCallback0);
-    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
-
-    // Submit real-time job to CLIENT(1) in same uid, should be queued after the previous job.
-    mScheduler->submit(CLIENT(1), JOB(0), UID(0), mRealtimeRequest, mClientCallback1);
-    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
-
-    // Submit real-time job to CLIENT(2) in UID(1).
-    // Should pause previous job and start new job, because UID(1) is (has been) top.
-    mScheduler->submit(CLIENT(2), JOB(0), UID(1), mRealtimeRequest, mClientCallback2);
-    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Pause(CLIENT(0), JOB(1)));
-    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(2), JOB(0)));
-
-    // Submit offline job, shouldn't generate any event.
-    mScheduler->submit(CLIENT(2), JOB(1), UID(1), mOfflineRequest, mClientCallback2);
-    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
-
-    // Bring UID(0) to top.
-    mUidPolicy->setTop(UID(0));
-    // Should pause current job, and resume last job in UID(0).
-    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Pause(CLIENT(2), JOB(0)));
-    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Resume(CLIENT(0), JOB(1)));
-}
-
-TEST_F(TranscodingJobSchedulerTest, TestCancelJob) {
-    ALOGD("TestCancelJob");
-
-    // Submit real-time job JOB(0), should start immediately.
-    mScheduler->submit(CLIENT(0), JOB(0), UID(0), mRealtimeRequest, mClientCallback0);
-    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), JOB(0)));
-
-    // Submit real-time job JOB(1), should not start.
-    mScheduler->submit(CLIENT(0), JOB(1), UID(0), mRealtimeRequest, mClientCallback0);
-    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
-
-    // Submit offline job JOB(2), should not start.
-    mScheduler->submit(CLIENT(0), JOB(2), UID(0), mOfflineRequest, mClientCallback0);
-    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
-
-    // Cancel queued real-time job.
-    // Cancel real-time job JOB(1), should be cancelled.
-    EXPECT_TRUE(mScheduler->cancel(CLIENT(0), JOB(1)));
-
-    // Cancel queued offline job.
-    // Cancel offline job JOB(2), should be cancelled.
-    EXPECT_TRUE(mScheduler->cancel(CLIENT(0), JOB(2)));
-
-    // Submit offline job JOB(3), shouldn't cause any event.
-    mScheduler->submit(CLIENT(0), JOB(3), UID(0), mOfflineRequest, mClientCallback0);
-    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
-
-    // Cancel running real-time job JOB(0).
-    // - Should be stopped first then cancelled.
-    // - Should also start offline job JOB(2) because real-time queue is empty.
-    EXPECT_TRUE(mScheduler->cancel(CLIENT(0), JOB(0)));
-    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Stop(CLIENT(0), JOB(0)));
-    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), JOB(3)));
-
-    // Submit real-time job JOB(4), offline JOB(3) should pause and JOB(4) should start.
-    mScheduler->submit(CLIENT(0), JOB(4), UID(0), mRealtimeRequest, mClientCallback0);
-    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Pause(CLIENT(0), JOB(3)));
-    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), JOB(4)));
-
-    // Cancel paused JOB(3). JOB(3) should be stopped.
-    EXPECT_TRUE(mScheduler->cancel(CLIENT(0), JOB(3)));
-    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Stop(CLIENT(0), JOB(3)));
-}
-
-TEST_F(TranscodingJobSchedulerTest, TestFinishJob) {
-    ALOGD("TestFinishJob");
-
-    // Start with unspecified top UID.
-    // Finish without any jobs submitted, should be ignored.
-    mScheduler->onFinish(CLIENT(0), JOB(0));
-    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
-
-    // Submit offline job JOB(0), should start immediately.
-    mScheduler->submit(CLIENT(0), JOB(0), UID(0), mOfflineRequest, mClientCallback0);
-    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), JOB(0)));
-
-    // Submit real-time job JOB(1), should pause offline job and start immediately.
-    mScheduler->submit(CLIENT(0), JOB(1), UID(0), mRealtimeRequest, mClientCallback0);
-    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Pause(CLIENT(0), JOB(0)));
-    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), JOB(1)));
-
-    // Submit real-time job JOB(2), should not start.
-    mScheduler->submit(CLIENT(0), JOB(2), UID(0), mRealtimeRequest, mClientCallback0);
-    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
-
-    // Finish when the job never started, should be ignored.
-    mScheduler->onFinish(CLIENT(0), JOB(2));
-    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
-
-    // UID(1) moves to top.
-    mUidPolicy->setTop(UID(1));
-    // Submit real-time job to CLIENT(1) in UID(1), should pause previous job and start new job.
-    mScheduler->submit(CLIENT(1), JOB(0), UID(1), mRealtimeRequest, mClientCallback1);
-    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Pause(CLIENT(0), JOB(1)));
-    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(1), JOB(0)));
-
-    // Simulate Finish that arrived late, after pause issued by scheduler.
-    // Should still be propagated to client, but shouldn't trigger any new start.
-    mScheduler->onFinish(CLIENT(0), JOB(1));
-    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Finished(CLIENT(0), JOB(1)));
-
-    // Finish running real-time job, should start next real-time job in queue.
-    mScheduler->onFinish(CLIENT(1), JOB(0));
-    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Finished(CLIENT(1), JOB(0)));
-    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), JOB(2)));
-
-    // Finish running real-time job, should resume next job (offline job) in queue.
-    mScheduler->onFinish(CLIENT(0), JOB(2));
-    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Finished(CLIENT(0), JOB(2)));
-    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Resume(CLIENT(0), JOB(0)));
-
-    // Finish running offline job.
-    mScheduler->onFinish(CLIENT(0), JOB(0));
-    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Finished(CLIENT(0), JOB(0)));
-
-    // Duplicate finish for last job, should be ignored.
-    mScheduler->onFinish(CLIENT(0), JOB(0));
-    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
-}
-
-TEST_F(TranscodingJobSchedulerTest, TestFailJob) {
-    ALOGD("TestFailJob");
-
-    // Start with unspecified top UID.
-    // Fail without any jobs submitted, should be ignored.
-    mScheduler->onError(CLIENT(0), JOB(0), TranscodingErrorCode::kUnknown);
-    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
-
-    // Submit offline job JOB(0), should start immediately.
-    mScheduler->submit(CLIENT(0), JOB(0), UID(0), mOfflineRequest, mClientCallback0);
-    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), JOB(0)));
-
-    // Submit real-time job JOB(1), should pause offline job and start immediately.
-    mScheduler->submit(CLIENT(0), JOB(1), UID(0), mRealtimeRequest, mClientCallback0);
-    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Pause(CLIENT(0), JOB(0)));
-    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), JOB(1)));
-
-    // Submit real-time job JOB(2), should not start.
-    mScheduler->submit(CLIENT(0), JOB(2), UID(0), mRealtimeRequest, mClientCallback0);
-    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
-
-    // Fail when the job never started, should be ignored.
-    mScheduler->onError(CLIENT(0), JOB(2), TranscodingErrorCode::kUnknown);
-    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
-
-    // UID(1) moves to top.
-    mUidPolicy->setTop(UID(1));
-    // Submit real-time job to CLIENT(1) in UID(1), should pause previous job and start new job.
-    mScheduler->submit(CLIENT(1), JOB(0), UID(1), mRealtimeRequest, mClientCallback1);
-    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Pause(CLIENT(0), JOB(1)));
-    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(1), JOB(0)));
-
-    // Simulate Fail that arrived late, after pause issued by scheduler.
-    // Should still be propagated to client, but shouldn't trigger any new start.
-    mScheduler->onError(CLIENT(0), JOB(1), TranscodingErrorCode::kUnknown);
-    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Failed(CLIENT(0), JOB(1)));
-
-    // Fail running real-time job, should start next real-time job in queue.
-    mScheduler->onError(CLIENT(1), JOB(0), TranscodingErrorCode::kUnknown);
-    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Failed(CLIENT(1), JOB(0)));
-    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), JOB(2)));
-
-    // Fail running real-time job, should resume next job (offline job) in queue.
-    mScheduler->onError(CLIENT(0), JOB(2), TranscodingErrorCode::kUnknown);
-    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Failed(CLIENT(0), JOB(2)));
-    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Resume(CLIENT(0), JOB(0)));
-
-    // Fail running offline job, and test error code propagation.
-    mScheduler->onError(CLIENT(0), JOB(0), TranscodingErrorCode::kInvalidOperation);
-    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Failed(CLIENT(0), JOB(0)));
-    EXPECT_EQ(mTranscoder->getLastError(), TranscodingErrorCode::kInvalidOperation);
-
-    // Duplicate fail for last job, should be ignored.
-    mScheduler->onError(CLIENT(0), JOB(0), TranscodingErrorCode::kUnknown);
-    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
-}
-
-TEST_F(TranscodingJobSchedulerTest, TestTopUidChanged) {
-    ALOGD("TestTopUidChanged");
-
-    // Start with unspecified top UID.
-    // Submit real-time job to CLIENT(0), job should start immediately.
-    mScheduler->submit(CLIENT(0), JOB(0), UID(0), mRealtimeRequest, mClientCallback0);
-    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), JOB(0)));
-
-    // Submit offline job to CLIENT(0), should not start.
-    mScheduler->submit(CLIENT(1), JOB(0), UID(0), mOfflineRequest, mClientCallback1);
-    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
-
-    // Move UID(1) to top.
-    mUidPolicy->setTop(UID(1));
-    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
-
-    // Submit real-time job to CLIENT(2) in different uid UID(1).
-    // Should pause previous job and start new job.
-    mScheduler->submit(CLIENT(2), JOB(0), UID(1), mRealtimeRequest, mClientCallback2);
-    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Pause(CLIENT(0), JOB(0)));
-    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(2), JOB(0)));
-
-    // Bring UID(0) back to top.
-    mUidPolicy->setTop(UID(0));
-    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Pause(CLIENT(2), JOB(0)));
-    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Resume(CLIENT(0), JOB(0)));
-
-    // Bring invalid uid to top.
-    mUidPolicy->setTop(kInvalidUid);
-    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
-
-    // Finish job, next real-time job should resume.
-    mScheduler->onFinish(CLIENT(0), JOB(0));
-    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Finished(CLIENT(0), JOB(0)));
-    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Resume(CLIENT(2), JOB(0)));
-
-    // Finish job, offline job should start.
-    mScheduler->onFinish(CLIENT(2), JOB(0));
-    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Finished(CLIENT(2), JOB(0)));
-    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(1), JOB(0)));
-}
-
-TEST_F(TranscodingJobSchedulerTest, TestTopUidSetChanged) {
-    ALOGD("TestTopUidChanged_MultipleUids");
-
-    // Start with unspecified top UID.
-    // Submit real-time job to CLIENT(0), job should start immediately.
-    mScheduler->submit(CLIENT(0), JOB(0), UID(0), mRealtimeRequest, mClientCallback0);
-    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), JOB(0)));
-
-    // Submit offline job to CLIENT(0), should not start.
-    mScheduler->submit(CLIENT(1), JOB(0), UID(0), mOfflineRequest, mClientCallback1);
-    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
-
-    // Set UID(0), UID(1) to top set.
-    // UID(0) should continue to run.
-    mUidPolicy->setTop({UID(0), UID(1)});
-    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
-
-    // Submit real-time job to CLIENT(2) in different uid UID(1).
-    // UID(0) should pause and UID(1) should start.
-    mScheduler->submit(CLIENT(2), JOB(0), UID(1), mRealtimeRequest, mClientCallback2);
-    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Pause(CLIENT(0), JOB(0)));
-    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(2), JOB(0)));
-
-    // Remove UID(0) from top set, and only leave UID(1) in the set.
-    // UID(1) should continue to run.
-    mUidPolicy->setTop(UID(1));
-    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
-
-    // Set UID(0), UID(2) to top set.
-    // UID(1) should continue to run.
-    mUidPolicy->setTop({UID(1), UID(2)});
-    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
-
-    // Bring UID(0) back to top.
-    mUidPolicy->setTop(UID(0));
-    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Pause(CLIENT(2), JOB(0)));
-    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Resume(CLIENT(0), JOB(0)));
-
-    // Bring invalid uid to top.
-    mUidPolicy->setTop(kInvalidUid);
-    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
-
-    // Finish job, next real-time job from UID(1) should resume, even if UID(1) no longer top.
-    mScheduler->onFinish(CLIENT(0), JOB(0));
-    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Finished(CLIENT(0), JOB(0)));
-    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Resume(CLIENT(2), JOB(0)));
-
-    // Finish job, offline job should start.
-    mScheduler->onFinish(CLIENT(2), JOB(0));
-    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Finished(CLIENT(2), JOB(0)));
-    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(1), JOB(0)));
-}
-
-TEST_F(TranscodingJobSchedulerTest, TestResourceLost) {
-    ALOGD("TestResourceLost");
-
-    // Start with unspecified top UID.
-    // Submit real-time job to CLIENT(0), job should start immediately.
-    mScheduler->submit(CLIENT(0), JOB(0), UID(0), mRealtimeRequest, mClientCallback0);
-    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), JOB(0)));
-
-    // Submit offline job to CLIENT(0), should not start.
-    mScheduler->submit(CLIENT(1), JOB(0), UID(0), mOfflineRequest, mClientCallback1);
-    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
-
-    // Move UID(1) to top.
-    mUidPolicy->setTop(UID(1));
-    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
-
-    // Submit real-time job to CLIENT(2) in different uid UID(1).
-    // Should pause previous job and start new job.
-    mScheduler->submit(CLIENT(2), JOB(0), UID(1), mRealtimeRequest, mClientCallback2);
-    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Pause(CLIENT(0), JOB(0)));
-    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(2), JOB(0)));
-
-    // Test 1: No queue change during resource loss.
-    // Signal resource lost.
-    mScheduler->onResourceLost();
-    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
-
-    // Signal resource available, CLIENT(2) should resume.
-    mScheduler->onResourceAvailable();
-    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Resume(CLIENT(2), JOB(0)));
-
-    // Test 2: Change of queue order during resource loss.
-    // Signal resource lost.
-    mScheduler->onResourceLost();
-    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
-
-    // Move UID(0) back to top, should have no resume due to no resource.
-    mUidPolicy->setTop(UID(0));
-    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
-
-    // Signal resource available, CLIENT(0) should resume.
-    mScheduler->onResourceAvailable();
-    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Resume(CLIENT(0), JOB(0)));
-
-    // Test 3: Adding new queue during resource loss.
-    // Signal resource lost.
-    mScheduler->onResourceLost();
-    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
-
-    // Move UID(2) to top.
-    mUidPolicy->setTop(UID(2));
-
-    // Submit real-time job to CLIENT(3) in UID(2), job shouldn't start due to no resource.
-    mScheduler->submit(CLIENT(3), JOB(0), UID(2), mRealtimeRequest, mClientCallback3);
-    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
-
-    // Signal resource available, CLIENT(3)'s job should start.
-    mScheduler->onResourceAvailable();
-    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(3), JOB(0)));
-}
-
-}  // namespace android
diff --git a/media/libmediatranscoding/tests/TranscodingSessionController_tests.cpp b/media/libmediatranscoding/tests/TranscodingSessionController_tests.cpp
new file mode 100644
index 0000000..4809d7a
--- /dev/null
+++ b/media/libmediatranscoding/tests/TranscodingSessionController_tests.cpp
@@ -0,0 +1,611 @@
+/*
+ * 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.
+ */
+
+// Unit Test for TranscodingSessionController
+
+// #define LOG_NDEBUG 0
+#define LOG_TAG "TranscodingSessionControllerTest"
+
+#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 <android-base/logging.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
+#include <gtest/gtest.h>
+#include <media/TranscodingClientManager.h>
+#include <media/TranscodingSessionController.h>
+#include <utils/Log.h>
+
+#include <unordered_set>
+
+namespace android {
+
+using Status = ::ndk::ScopedAStatus;
+using aidl::android::media::BnTranscodingClientCallback;
+using aidl::android::media::IMediaTranscodingService;
+using aidl::android::media::ITranscodingClient;
+using aidl::android::media::TranscodingRequestParcel;
+
+constexpr ClientIdType kClientId = 1000;
+constexpr SessionIdType kClientSessionId = 0;
+constexpr uid_t kClientUid = 5000;
+constexpr uid_t kInvalidUid = (uid_t)-1;
+
+#define CLIENT(n) (kClientId + (n))
+#define SESSION(n) (kClientSessionId + (n))
+#define UID(n) (kClientUid + (n))
+
+class TestUidPolicy : public UidPolicyInterface {
+public:
+    TestUidPolicy() = default;
+    virtual ~TestUidPolicy() = default;
+
+    // UidPolicyInterface
+    void registerMonitorUid(uid_t /*uid*/) override {}
+    void unregisterMonitorUid(uid_t /*uid*/) override {}
+    bool isUidOnTop(uid_t uid) override { return mTopUids.count(uid) > 0; }
+    std::unordered_set<uid_t> getTopUids() const override { return mTopUids; }
+    void setCallback(const std::shared_ptr<UidPolicyCallbackInterface>& cb) override {
+        mUidPolicyCallback = cb;
+    }
+    void setTop(uid_t uid) {
+        std::unordered_set<uid_t> uids = {uid};
+        setTop(uids);
+    }
+    void setTop(const std::unordered_set<uid_t>& uids) {
+        mTopUids = uids;
+        auto uidPolicyCb = mUidPolicyCallback.lock();
+        if (uidPolicyCb != nullptr) {
+            uidPolicyCb->onTopUidsChanged(mTopUids);
+        }
+    }
+
+    std::unordered_set<uid_t> mTopUids;
+    std::weak_ptr<UidPolicyCallbackInterface> mUidPolicyCallback;
+};
+
+class TestTranscoder : public TranscoderInterface {
+public:
+    TestTranscoder() : mLastError(TranscodingErrorCode::kUnknown) {}
+    virtual ~TestTranscoder() {}
+
+    // TranscoderInterface
+    void setCallback(const std::shared_ptr<TranscoderCallbackInterface>& /*cb*/) override {}
+
+    void start(ClientIdType clientId, SessionIdType sessionId,
+               const TranscodingRequestParcel& /*request*/,
+               const std::shared_ptr<ITranscodingClientCallback>& /*clientCallback*/) override {
+        mEventQueue.push_back(Start(clientId, sessionId));
+    }
+    void pause(ClientIdType clientId, SessionIdType sessionId) override {
+        mEventQueue.push_back(Pause(clientId, sessionId));
+    }
+    void resume(ClientIdType clientId, SessionIdType sessionId,
+                const TranscodingRequestParcel& /*request*/,
+                const std::shared_ptr<ITranscodingClientCallback>& /*clientCallback*/) override {
+        mEventQueue.push_back(Resume(clientId, sessionId));
+    }
+    void stop(ClientIdType clientId, SessionIdType sessionId) override {
+        mEventQueue.push_back(Stop(clientId, sessionId));
+    }
+
+    void onFinished(ClientIdType clientId, SessionIdType sessionId) {
+        mEventQueue.push_back(Finished(clientId, sessionId));
+    }
+
+    void onFailed(ClientIdType clientId, SessionIdType sessionId, TranscodingErrorCode err) {
+        mLastError = err;
+        mEventQueue.push_back(Failed(clientId, sessionId));
+    }
+
+    TranscodingErrorCode getLastError() {
+        TranscodingErrorCode result = mLastError;
+        mLastError = TranscodingErrorCode::kUnknown;
+        return result;
+    }
+
+    struct Event {
+        enum { NoEvent, Start, Pause, Resume, Stop, Finished, Failed } type;
+        ClientIdType clientId;
+        SessionIdType sessionId;
+    };
+
+    static constexpr Event NoEvent = {Event::NoEvent, 0, 0};
+
+#define DECLARE_EVENT(action)                                             \
+    static Event action(ClientIdType clientId, SessionIdType sessionId) { \
+        return {Event::action, clientId, sessionId};                      \
+    }
+
+    DECLARE_EVENT(Start);
+    DECLARE_EVENT(Pause);
+    DECLARE_EVENT(Resume);
+    DECLARE_EVENT(Stop);
+    DECLARE_EVENT(Finished);
+    DECLARE_EVENT(Failed);
+
+    const Event& popEvent() {
+        if (mEventQueue.empty()) {
+            mPoppedEvent = NoEvent;
+        } else {
+            mPoppedEvent = *mEventQueue.begin();
+            mEventQueue.pop_front();
+        }
+        return mPoppedEvent;
+    }
+
+private:
+    Event mPoppedEvent;
+    std::list<Event> mEventQueue;
+    TranscodingErrorCode mLastError;
+};
+
+bool operator==(const TestTranscoder::Event& lhs, const TestTranscoder::Event& rhs) {
+    return lhs.type == rhs.type && lhs.clientId == rhs.clientId && lhs.sessionId == rhs.sessionId;
+}
+
+struct TestClientCallback : public BnTranscodingClientCallback {
+    TestClientCallback(TestTranscoder* owner, int64_t clientId)
+          : mOwner(owner), mClientId(clientId) {
+        ALOGD("TestClient Created");
+    }
+
+    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_sessionId*/) override { return Status::ok(); }
+
+    Status onTranscodingPaused(int32_t /*in_sessionId*/) override { return Status::ok(); }
+
+    Status onTranscodingResumed(int32_t /*in_sessionId*/) override { return Status::ok(); }
+
+    Status onTranscodingFinished(int32_t in_sessionId,
+                                 const TranscodingResultParcel& in_result) override {
+        EXPECT_EQ(in_sessionId, in_result.sessionId);
+        ALOGD("TestClientCallback: received onTranscodingFinished");
+        mOwner->onFinished(mClientId, in_sessionId);
+        return Status::ok();
+    }
+
+    Status onTranscodingFailed(int32_t in_sessionId, TranscodingErrorCode in_errorCode) override {
+        mOwner->onFailed(mClientId, in_sessionId, in_errorCode);
+        return Status::ok();
+    }
+
+    Status onAwaitNumberOfSessionsChanged(int32_t /* in_sessionId */,
+                                          int32_t /* in_oldAwaitNumber */,
+                                          int32_t /* in_newAwaitNumber */) override {
+        return Status::ok();
+    }
+
+    Status onProgressUpdate(int32_t /* in_sessionId */, int32_t /* in_progress */) override {
+        return Status::ok();
+    }
+
+    virtual ~TestClientCallback() { ALOGI("TestClient destroyed"); };
+
+private:
+    TestTranscoder* mOwner;
+    int64_t mClientId;
+    TestClientCallback(const TestClientCallback&) = delete;
+    TestClientCallback& operator=(const TestClientCallback&) = delete;
+};
+
+class TranscodingSessionControllerTest : public ::testing::Test {
+public:
+    TranscodingSessionControllerTest() { ALOGI("TranscodingSessionControllerTest created"); }
+
+    void SetUp() override {
+        ALOGI("TranscodingSessionControllerTest set up");
+        mTranscoder.reset(new TestTranscoder());
+        mUidPolicy.reset(new TestUidPolicy());
+        mController.reset(new TranscodingSessionController(mTranscoder, mUidPolicy,
+                                                           nullptr /*resourcePolicy*/));
+        mUidPolicy->setCallback(mController);
+
+        // Set priority only, ignore other fields for now.
+        mOfflineRequest.priority = TranscodingSessionPriority::kUnspecified;
+        mRealtimeRequest.priority = TranscodingSessionPriority::kHigh;
+        mClientCallback0 =
+                ::ndk::SharedRefBase::make<TestClientCallback>(mTranscoder.get(), CLIENT(0));
+        mClientCallback1 =
+                ::ndk::SharedRefBase::make<TestClientCallback>(mTranscoder.get(), CLIENT(1));
+        mClientCallback2 =
+                ::ndk::SharedRefBase::make<TestClientCallback>(mTranscoder.get(), CLIENT(2));
+        mClientCallback3 =
+                ::ndk::SharedRefBase::make<TestClientCallback>(mTranscoder.get(), CLIENT(3));
+    }
+
+    void TearDown() override { ALOGI("TranscodingSessionControllerTest tear down"); }
+
+    ~TranscodingSessionControllerTest() { ALOGD("TranscodingSessionControllerTest destroyed"); }
+
+    std::shared_ptr<TestTranscoder> mTranscoder;
+    std::shared_ptr<TestUidPolicy> mUidPolicy;
+    std::shared_ptr<TranscodingSessionController> mController;
+    TranscodingRequestParcel mOfflineRequest;
+    TranscodingRequestParcel mRealtimeRequest;
+    std::shared_ptr<TestClientCallback> mClientCallback0;
+    std::shared_ptr<TestClientCallback> mClientCallback1;
+    std::shared_ptr<TestClientCallback> mClientCallback2;
+    std::shared_ptr<TestClientCallback> mClientCallback3;
+};
+
+TEST_F(TranscodingSessionControllerTest, TestSubmitSession) {
+    ALOGD("TestSubmitSession");
+
+    // Start with UID(1) on top.
+    mUidPolicy->setTop(UID(1));
+
+    // Submit offline session to CLIENT(0) in UID(0).
+    // Should start immediately (because this is the only session).
+    mController->submit(CLIENT(0), SESSION(0), UID(0), mOfflineRequest, mClientCallback0);
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), 0));
+
+    // Submit real-time session to CLIENT(0).
+    // Should pause offline session and start new session,  even if UID(0) is not on top.
+    mController->submit(CLIENT(0), SESSION(1), UID(0), mRealtimeRequest, mClientCallback0);
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Pause(CLIENT(0), SESSION(0)));
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), SESSION(1)));
+
+    // Submit real-time session to CLIENT(0), should be queued after the previous session.
+    mController->submit(CLIENT(0), SESSION(2), UID(0), mRealtimeRequest, mClientCallback0);
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
+
+    // Submit real-time session to CLIENT(1) in same uid, should be queued after the previous
+    // session.
+    mController->submit(CLIENT(1), SESSION(0), UID(0), mRealtimeRequest, mClientCallback1);
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
+
+    // Submit real-time session to CLIENT(2) in UID(1).
+    // Should pause previous session and start new session, because UID(1) is (has been) top.
+    mController->submit(CLIENT(2), SESSION(0), UID(1), mRealtimeRequest, mClientCallback2);
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Pause(CLIENT(0), SESSION(1)));
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(2), SESSION(0)));
+
+    // Submit offline session, shouldn't generate any event.
+    mController->submit(CLIENT(2), SESSION(1), UID(1), mOfflineRequest, mClientCallback2);
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
+
+    // Bring UID(0) to top.
+    mUidPolicy->setTop(UID(0));
+    // Should pause current session, and resume last session in UID(0).
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Pause(CLIENT(2), SESSION(0)));
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Resume(CLIENT(0), SESSION(1)));
+}
+
+TEST_F(TranscodingSessionControllerTest, TestCancelSession) {
+    ALOGD("TestCancelSession");
+
+    // Submit real-time session SESSION(0), should start immediately.
+    mController->submit(CLIENT(0), SESSION(0), UID(0), mRealtimeRequest, mClientCallback0);
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), SESSION(0)));
+
+    // Submit real-time session SESSION(1), should not start.
+    mController->submit(CLIENT(0), SESSION(1), UID(0), mRealtimeRequest, mClientCallback0);
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
+
+    // Submit offline session SESSION(2), should not start.
+    mController->submit(CLIENT(0), SESSION(2), UID(0), mOfflineRequest, mClientCallback0);
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
+
+    // Cancel queued real-time session.
+    // Cancel real-time session SESSION(1), should be cancelled.
+    EXPECT_TRUE(mController->cancel(CLIENT(0), SESSION(1)));
+
+    // Cancel queued offline session.
+    // Cancel offline session SESSION(2), should be cancelled.
+    EXPECT_TRUE(mController->cancel(CLIENT(0), SESSION(2)));
+
+    // Submit offline session SESSION(3), shouldn't cause any event.
+    mController->submit(CLIENT(0), SESSION(3), UID(0), mOfflineRequest, mClientCallback0);
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
+
+    // Cancel running real-time session SESSION(0).
+    // - Should be stopped first then cancelled.
+    // - Should also start offline session SESSION(2) because real-time queue is empty.
+    EXPECT_TRUE(mController->cancel(CLIENT(0), SESSION(0)));
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Stop(CLIENT(0), SESSION(0)));
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), SESSION(3)));
+
+    // Submit real-time session SESSION(4), offline SESSION(3) should pause and SESSION(4)
+    // should start.
+    mController->submit(CLIENT(0), SESSION(4), UID(0), mRealtimeRequest, mClientCallback0);
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Pause(CLIENT(0), SESSION(3)));
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), SESSION(4)));
+
+    // Cancel paused SESSION(3). SESSION(3) should be stopped.
+    EXPECT_TRUE(mController->cancel(CLIENT(0), SESSION(3)));
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Stop(CLIENT(0), SESSION(3)));
+}
+
+TEST_F(TranscodingSessionControllerTest, TestFinishSession) {
+    ALOGD("TestFinishSession");
+
+    // Start with unspecified top UID.
+    // Finish without any sessions submitted, should be ignored.
+    mController->onFinish(CLIENT(0), SESSION(0));
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
+
+    // Submit offline session SESSION(0), should start immediately.
+    mController->submit(CLIENT(0), SESSION(0), UID(0), mOfflineRequest, mClientCallback0);
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), SESSION(0)));
+
+    // Submit real-time session SESSION(1), should pause offline session and start immediately.
+    mController->submit(CLIENT(0), SESSION(1), UID(0), mRealtimeRequest, mClientCallback0);
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Pause(CLIENT(0), SESSION(0)));
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), SESSION(1)));
+
+    // Submit real-time session SESSION(2), should not start.
+    mController->submit(CLIENT(0), SESSION(2), UID(0), mRealtimeRequest, mClientCallback0);
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
+
+    // Finish when the session never started, should be ignored.
+    mController->onFinish(CLIENT(0), SESSION(2));
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
+
+    // UID(1) moves to top.
+    mUidPolicy->setTop(UID(1));
+    // Submit real-time session to CLIENT(1) in UID(1), should pause previous session and start
+    // new session.
+    mController->submit(CLIENT(1), SESSION(0), UID(1), mRealtimeRequest, mClientCallback1);
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Pause(CLIENT(0), SESSION(1)));
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(1), SESSION(0)));
+
+    // Simulate Finish that arrived late, after pause issued by controller.
+    // Should still be propagated to client, but shouldn't trigger any new start.
+    mController->onFinish(CLIENT(0), SESSION(1));
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Finished(CLIENT(0), SESSION(1)));
+
+    // Finish running real-time session, should start next real-time session in queue.
+    mController->onFinish(CLIENT(1), SESSION(0));
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Finished(CLIENT(1), SESSION(0)));
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), SESSION(2)));
+
+    // Finish running real-time session, should resume next session (offline session) in queue.
+    mController->onFinish(CLIENT(0), SESSION(2));
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Finished(CLIENT(0), SESSION(2)));
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Resume(CLIENT(0), SESSION(0)));
+
+    // Finish running offline session.
+    mController->onFinish(CLIENT(0), SESSION(0));
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Finished(CLIENT(0), SESSION(0)));
+
+    // Duplicate finish for last session, should be ignored.
+    mController->onFinish(CLIENT(0), SESSION(0));
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
+}
+
+TEST_F(TranscodingSessionControllerTest, TestFailSession) {
+    ALOGD("TestFailSession");
+
+    // Start with unspecified top UID.
+    // Fail without any sessions submitted, should be ignored.
+    mController->onError(CLIENT(0), SESSION(0), TranscodingErrorCode::kUnknown);
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
+
+    // Submit offline session SESSION(0), should start immediately.
+    mController->submit(CLIENT(0), SESSION(0), UID(0), mOfflineRequest, mClientCallback0);
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), SESSION(0)));
+
+    // Submit real-time session SESSION(1), should pause offline session and start immediately.
+    mController->submit(CLIENT(0), SESSION(1), UID(0), mRealtimeRequest, mClientCallback0);
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Pause(CLIENT(0), SESSION(0)));
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), SESSION(1)));
+
+    // Submit real-time session SESSION(2), should not start.
+    mController->submit(CLIENT(0), SESSION(2), UID(0), mRealtimeRequest, mClientCallback0);
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
+
+    // Fail when the session never started, should be ignored.
+    mController->onError(CLIENT(0), SESSION(2), TranscodingErrorCode::kUnknown);
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
+
+    // UID(1) moves to top.
+    mUidPolicy->setTop(UID(1));
+    // Submit real-time session to CLIENT(1) in UID(1), should pause previous session and start
+    // new session.
+    mController->submit(CLIENT(1), SESSION(0), UID(1), mRealtimeRequest, mClientCallback1);
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Pause(CLIENT(0), SESSION(1)));
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(1), SESSION(0)));
+
+    // Simulate Fail that arrived late, after pause issued by controller.
+    // Should still be propagated to client, but shouldn't trigger any new start.
+    mController->onError(CLIENT(0), SESSION(1), TranscodingErrorCode::kUnknown);
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Failed(CLIENT(0), SESSION(1)));
+
+    // Fail running real-time session, should start next real-time session in queue.
+    mController->onError(CLIENT(1), SESSION(0), TranscodingErrorCode::kUnknown);
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Failed(CLIENT(1), SESSION(0)));
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), SESSION(2)));
+
+    // Fail running real-time session, should resume next session (offline session) in queue.
+    mController->onError(CLIENT(0), SESSION(2), TranscodingErrorCode::kUnknown);
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Failed(CLIENT(0), SESSION(2)));
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Resume(CLIENT(0), SESSION(0)));
+
+    // Fail running offline session, and test error code propagation.
+    mController->onError(CLIENT(0), SESSION(0), TranscodingErrorCode::kInvalidOperation);
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Failed(CLIENT(0), SESSION(0)));
+    EXPECT_EQ(mTranscoder->getLastError(), TranscodingErrorCode::kInvalidOperation);
+
+    // Duplicate fail for last session, should be ignored.
+    mController->onError(CLIENT(0), SESSION(0), TranscodingErrorCode::kUnknown);
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
+}
+
+TEST_F(TranscodingSessionControllerTest, TestTopUidChanged) {
+    ALOGD("TestTopUidChanged");
+
+    // Start with unspecified top UID.
+    // Submit real-time session to CLIENT(0), session should start immediately.
+    mController->submit(CLIENT(0), SESSION(0), UID(0), mRealtimeRequest, mClientCallback0);
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), SESSION(0)));
+
+    // Submit offline session to CLIENT(0), should not start.
+    mController->submit(CLIENT(1), SESSION(0), UID(0), mOfflineRequest, mClientCallback1);
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
+
+    // Move UID(1) to top.
+    mUidPolicy->setTop(UID(1));
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
+
+    // Submit real-time session to CLIENT(2) in different uid UID(1).
+    // Should pause previous session and start new session.
+    mController->submit(CLIENT(2), SESSION(0), UID(1), mRealtimeRequest, mClientCallback2);
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Pause(CLIENT(0), SESSION(0)));
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(2), SESSION(0)));
+
+    // Bring UID(0) back to top.
+    mUidPolicy->setTop(UID(0));
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Pause(CLIENT(2), SESSION(0)));
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Resume(CLIENT(0), SESSION(0)));
+
+    // Bring invalid uid to top.
+    mUidPolicy->setTop(kInvalidUid);
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
+
+    // Finish session, next real-time session should resume.
+    mController->onFinish(CLIENT(0), SESSION(0));
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Finished(CLIENT(0), SESSION(0)));
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Resume(CLIENT(2), SESSION(0)));
+
+    // Finish session, offline session should start.
+    mController->onFinish(CLIENT(2), SESSION(0));
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Finished(CLIENT(2), SESSION(0)));
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(1), SESSION(0)));
+}
+
+TEST_F(TranscodingSessionControllerTest, TestTopUidSetChanged) {
+    ALOGD("TestTopUidChanged_MultipleUids");
+
+    // Start with unspecified top UID.
+    // Submit real-time session to CLIENT(0), session should start immediately.
+    mController->submit(CLIENT(0), SESSION(0), UID(0), mRealtimeRequest, mClientCallback0);
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), SESSION(0)));
+
+    // Submit offline session to CLIENT(0), should not start.
+    mController->submit(CLIENT(1), SESSION(0), UID(0), mOfflineRequest, mClientCallback1);
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
+
+    // Set UID(0), UID(1) to top set.
+    // UID(0) should continue to run.
+    mUidPolicy->setTop({UID(0), UID(1)});
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
+
+    // Submit real-time session to CLIENT(2) in different uid UID(1).
+    // UID(0) should pause and UID(1) should start.
+    mController->submit(CLIENT(2), SESSION(0), UID(1), mRealtimeRequest, mClientCallback2);
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Pause(CLIENT(0), SESSION(0)));
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(2), SESSION(0)));
+
+    // Remove UID(0) from top set, and only leave UID(1) in the set.
+    // UID(1) should continue to run.
+    mUidPolicy->setTop(UID(1));
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
+
+    // Set UID(0), UID(2) to top set.
+    // UID(1) should continue to run.
+    mUidPolicy->setTop({UID(1), UID(2)});
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
+
+    // Bring UID(0) back to top.
+    mUidPolicy->setTop(UID(0));
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Pause(CLIENT(2), SESSION(0)));
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Resume(CLIENT(0), SESSION(0)));
+
+    // Bring invalid uid to top.
+    mUidPolicy->setTop(kInvalidUid);
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
+
+    // Finish session, next real-time session from UID(1) should resume, even if UID(1)
+    // no longer top.
+    mController->onFinish(CLIENT(0), SESSION(0));
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Finished(CLIENT(0), SESSION(0)));
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Resume(CLIENT(2), SESSION(0)));
+
+    // Finish session, offline session should start.
+    mController->onFinish(CLIENT(2), SESSION(0));
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Finished(CLIENT(2), SESSION(0)));
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(1), SESSION(0)));
+}
+
+TEST_F(TranscodingSessionControllerTest, TestResourceLost) {
+    ALOGD("TestResourceLost");
+
+    // Start with unspecified top UID.
+    // Submit real-time session to CLIENT(0), session should start immediately.
+    mController->submit(CLIENT(0), SESSION(0), UID(0), mRealtimeRequest, mClientCallback0);
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), SESSION(0)));
+
+    // Submit offline session to CLIENT(0), should not start.
+    mController->submit(CLIENT(1), SESSION(0), UID(0), mOfflineRequest, mClientCallback1);
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
+
+    // Move UID(1) to top.
+    mUidPolicy->setTop(UID(1));
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
+
+    // Submit real-time session to CLIENT(2) in different uid UID(1).
+    // Should pause previous session and start new session.
+    mController->submit(CLIENT(2), SESSION(0), UID(1), mRealtimeRequest, mClientCallback2);
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Pause(CLIENT(0), SESSION(0)));
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(2), SESSION(0)));
+
+    // Test 1: No queue change during resource loss.
+    // Signal resource lost.
+    mController->onResourceLost();
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
+
+    // Signal resource available, CLIENT(2) should resume.
+    mController->onResourceAvailable();
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Resume(CLIENT(2), SESSION(0)));
+
+    // Test 2: Change of queue order during resource loss.
+    // Signal resource lost.
+    mController->onResourceLost();
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
+
+    // Move UID(0) back to top, should have no resume due to no resource.
+    mUidPolicy->setTop(UID(0));
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
+
+    // Signal resource available, CLIENT(0) should resume.
+    mController->onResourceAvailable();
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Resume(CLIENT(0), SESSION(0)));
+
+    // Test 3: Adding new queue during resource loss.
+    // Signal resource lost.
+    mController->onResourceLost();
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
+
+    // Move UID(2) to top.
+    mUidPolicy->setTop(UID(2));
+
+    // Submit real-time session to CLIENT(3) in UID(2), session shouldn't start due to no resource.
+    mController->submit(CLIENT(3), SESSION(0), UID(2), mRealtimeRequest, mClientCallback3);
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
+
+    // Signal resource available, CLIENT(3)'s session should start.
+    mController->onResourceAvailable();
+    EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(3), SESSION(0)));
+}
+
+}  // namespace android
diff --git a/media/libmediatranscoding/tests/build_and_run_all_unit_tests.sh b/media/libmediatranscoding/tests/build_and_run_all_unit_tests.sh
index ff6df2c..5db9258 100644
--- a/media/libmediatranscoding/tests/build_and_run_all_unit_tests.sh
+++ b/media/libmediatranscoding/tests/build_and_run_all_unit_tests.sh
@@ -29,6 +29,6 @@
 #adb shell /data/nativetest64/AdjustableMaxPriorityQueue_tests/AdjustableMaxPriorityQueue_tests
 adb shell /data/nativetest/AdjustableMaxPriorityQueue_tests/AdjustableMaxPriorityQueue_tests
 
-echo "testing TranscodingJobScheduler"
-#adb shell /data/nativetest64/TranscodingJobScheduler_tests/TranscodingJobScheduler_tests
-adb shell /data/nativetest/TranscodingJobScheduler_tests/TranscodingJobScheduler_tests
+echo "testing TranscodingSessionController"
+#adb shell /data/nativetest64/TranscodingSessionController_tests/TranscodingSessionController_tests
+adb shell /data/nativetest/TranscodingSessionController_tests/TranscodingSessionController_tests
diff --git a/media/libmediatranscoding/transcoder/Android.bp b/media/libmediatranscoding/transcoder/Android.bp
index 258ed9a..1896412 100644
--- a/media/libmediatranscoding/transcoder/Android.bp
+++ b/media/libmediatranscoding/transcoder/Android.bp
@@ -34,8 +34,7 @@
         "libmediandk",
         "libnativewindow",
         "libutils",
-        // TODO: Use libbinder_ndk
-        "libbinder",
+        "libbinder_ndk",
     ],
 
     export_include_dirs: [
diff --git a/media/libmediatranscoding/transcoder/MediaTranscoder.cpp b/media/libmediatranscoding/transcoder/MediaTranscoder.cpp
index cdb8368..d89b58f 100644
--- a/media/libmediatranscoding/transcoder/MediaTranscoder.cpp
+++ b/media/libmediatranscoding/transcoder/MediaTranscoder.cpp
@@ -18,7 +18,6 @@
 #define LOG_TAG "MediaTranscoder"
 
 #include <android-base/logging.h>
-#include <binder/Parcel.h>
 #include <fcntl.h>
 #include <media/MediaSampleReaderNDK.h>
 #include <media/MediaSampleWriter.h>
@@ -160,7 +159,7 @@
 
 std::shared_ptr<MediaTranscoder> MediaTranscoder::create(
         const std::shared_ptr<CallbackInterface>& callbacks,
-        const std::shared_ptr<const Parcel>& pausedState) {
+        const std::shared_ptr<ndk::ScopedAParcel>& pausedState) {
     if (pausedState != nullptr) {
         LOG(INFO) << "Initializing from paused state.";
     }
@@ -325,9 +324,9 @@
     return AMEDIA_OK;
 }
 
-media_status_t MediaTranscoder::pause(std::shared_ptr<const Parcel>* pausedState) {
+media_status_t MediaTranscoder::pause(std::shared_ptr<ndk::ScopedAParcel>* pausedState) {
     // TODO: write internal states to parcel.
-    *pausedState = std::make_shared<Parcel>();
+    *pausedState = std::shared_ptr<::ndk::ScopedAParcel>(new ::ndk::ScopedAParcel());
     return cancel();
 }
 
diff --git a/media/libmediatranscoding/transcoder/NdkCommon.cpp b/media/libmediatranscoding/transcoder/NdkCommon.cpp
index e58330f..a7b79dc 100644
--- a/media/libmediatranscoding/transcoder/NdkCommon.cpp
+++ b/media/libmediatranscoding/transcoder/NdkCommon.cpp
@@ -73,4 +73,18 @@
         }
     }
 }
+
+#define DEFINE_SET_DEFAULT_FORMAT_VALUE_FUNC(_type, _typeName)                                  \
+    bool SetDefaultFormatValue##_typeName(const char* key, AMediaFormat* format, _type value) { \
+        _type tmp;                                                                              \
+        if (!AMediaFormat_get##_typeName(format, key, &tmp)) {                                  \
+            AMediaFormat_set##_typeName(format, key, value);                                    \
+            return true;                                                                        \
+        }                                                                                       \
+        return false;                                                                           \
+    }
+
+DEFINE_SET_DEFAULT_FORMAT_VALUE_FUNC(float, Float);
+DEFINE_SET_DEFAULT_FORMAT_VALUE_FUNC(int32_t, Int32);
+
 }  // namespace AMediaFormatUtils
\ No newline at end of file
diff --git a/media/libmediatranscoding/transcoder/VideoTrackTranscoder.cpp b/media/libmediatranscoding/transcoder/VideoTrackTranscoder.cpp
index 0713eb8..4cf54f1 100644
--- a/media/libmediatranscoding/transcoder/VideoTrackTranscoder.cpp
+++ b/media/libmediatranscoding/transcoder/VideoTrackTranscoder.cpp
@@ -22,6 +22,8 @@
 #include <media/VideoTrackTranscoder.h>
 #include <utils/AndroidThreads.h>
 
+using namespace AMediaFormatUtils;
+
 namespace android {
 
 // Check that the codec sample flags have the expected NDK meaning.
@@ -36,6 +38,12 @@
 static constexpr int32_t kColorFormatSurface = 0x7f000789;
 // Default key frame interval in seconds.
 static constexpr float kDefaultKeyFrameIntervalSeconds = 1.0f;
+// Default codec operating rate.
+static constexpr int32_t kDefaultCodecOperatingRate = 240;
+// Default codec priority.
+static constexpr int32_t kDefaultCodecPriority = 1;
+// Default bitrate, in case source estimation fails.
+static constexpr int32_t kDefaultBitrateMbps = 10 * 1000 * 1000;
 
 template <typename T>
 void VideoTrackTranscoder::BlockingQueue<T>::push(T const& value, bool front) {
@@ -176,8 +184,6 @@
 // Creates and configures the codecs.
 media_status_t VideoTrackTranscoder::configureDestinationFormat(
         const std::shared_ptr<AMediaFormat>& destinationFormat) {
-    static constexpr int32_t kDefaultBitrateMbps = 10 * 1000 * 1000;
-
     media_status_t status = AMEDIA_OK;
 
     if (destinationFormat == nullptr) {
@@ -203,11 +209,12 @@
         AMediaFormat_setInt32(encoderFormat, AMEDIAFORMAT_KEY_BIT_RATE, bitrate);
     }
 
-    float tmp;
-    if (!AMediaFormat_getFloat(encoderFormat, AMEDIAFORMAT_KEY_I_FRAME_INTERVAL, &tmp)) {
-        AMediaFormat_setFloat(encoderFormat, AMEDIAFORMAT_KEY_I_FRAME_INTERVAL,
-                              kDefaultKeyFrameIntervalSeconds);
-    }
+    SetDefaultFormatValueFloat(AMEDIAFORMAT_KEY_I_FRAME_INTERVAL, encoderFormat,
+                               kDefaultKeyFrameIntervalSeconds);
+    SetDefaultFormatValueInt32(AMEDIAFORMAT_KEY_OPERATING_RATE, encoderFormat,
+                               kDefaultCodecOperatingRate);
+    SetDefaultFormatValueInt32(AMEDIAFORMAT_KEY_PRIORITY, encoderFormat, kDefaultCodecPriority);
+
     AMediaFormat_setInt32(encoderFormat, AMEDIAFORMAT_KEY_COLOR_FORMAT, kColorFormatSurface);
 
     // Always encode without rotation. The rotation degree will be transferred directly to
@@ -271,13 +278,13 @@
     AMediaFormat_setInt32(decoderFormat.get(), TBD_AMEDIACODEC_PARAMETER_KEY_ALLOW_FRAME_DROP, 0);
 
     // Copy over configurations that apply to both encoder and decoder.
-    static const AMediaFormatUtils::EntryCopier kEncoderEntriesToCopy[] = {
+    static const EntryCopier kEncoderEntriesToCopy[] = {
             ENTRY_COPIER2(AMEDIAFORMAT_KEY_OPERATING_RATE, Float, Int32),
             ENTRY_COPIER(AMEDIAFORMAT_KEY_PRIORITY, Int32),
     };
     const size_t entryCount = sizeof(kEncoderEntriesToCopy) / sizeof(kEncoderEntriesToCopy[0]);
-    AMediaFormatUtils::CopyFormatEntries(mDestinationFormat.get(), decoderFormat.get(),
-                                         kEncoderEntriesToCopy, entryCount);
+    CopyFormatEntries(mDestinationFormat.get(), decoderFormat.get(), kEncoderEntriesToCopy,
+                      entryCount);
 
     status = AMediaCodec_configure(mDecoder, decoderFormat.get(), mSurface, NULL /* crypto */,
                                    0 /* flags */);
@@ -480,7 +487,7 @@
     androidSetThreadPriority(0 /* tid (0 = current) */, ANDROID_PRIORITY_VIDEO);
 
     // Push start decoder and encoder as two messages, so that these are subject to the
-    // stop request as well. If the job is cancelled (or paused) immediately after start,
+    // stop request as well. If the session is cancelled (or paused) immediately after start,
     // we don't need to waste time start then stop the codecs.
     mCodecMessageQueue.push([this] {
         media_status_t status = AMediaCodec_start(mDecoder);
diff --git a/media/libmediatranscoding/transcoder/benchmark/MediaTrackTranscoderBenchmark.cpp b/media/libmediatranscoding/transcoder/benchmark/MediaTrackTranscoderBenchmark.cpp
index c695ed9..aee0ed6 100644
--- a/media/libmediatranscoding/transcoder/benchmark/MediaTrackTranscoderBenchmark.cpp
+++ b/media/libmediatranscoding/transcoder/benchmark/MediaTrackTranscoderBenchmark.cpp
@@ -192,7 +192,7 @@
         auto& p = mSamples[mCurrentSampleIndex % mSamples.size()];
 
         if (bufferSize < p.second.size) return AMEDIA_ERROR_INVALID_PARAMETER;
-        memcpy(buffer, p.first.get(), bufferSize);
+        memcpy(buffer, p.first.get(), p.second.size);
 
         advanceTrack(trackIndex);
         return AMEDIA_OK;
diff --git a/media/libmediatranscoding/transcoder/benchmark/MediaTranscoderBenchmark.cpp b/media/libmediatranscoding/transcoder/benchmark/MediaTranscoderBenchmark.cpp
index f985a28..465632f 100644
--- a/media/libmediatranscoding/transcoder/benchmark/MediaTranscoderBenchmark.cpp
+++ b/media/libmediatranscoding/transcoder/benchmark/MediaTranscoderBenchmark.cpp
@@ -55,7 +55,7 @@
                                   int32_t progress __unused) override {}
 
     virtual void onCodecResourceLost(const MediaTranscoder* transcoder __unused,
-                                     const std::shared_ptr<const Parcel>& pausedState
+                                     const std::shared_ptr<ndk::ScopedAParcel>& pausedState
                                              __unused) override {}
 
     bool waitForTranscodingFinished() {
@@ -247,6 +247,17 @@
                        false /* includeAudio */, true /* transcodeVideo */, SetMaxOperatingRate);
 }
 
+static void BM_TranscodeAvc2AvcAV2AV720P(benchmark::State& state) {
+    TranscodeMediaFile(state, "video_1280x720_3648frame_h264_16Mbps_30fps_aac.mp4",
+                       "video_1280x720_3648frame_h264_16Mbps_30fps_aac_transcoded_AV.mp4",
+                       true /* includeAudio */, true /* transcodeVideo */);
+}
+
+static void BM_TranscodeAvc2AvcAV2AV720PMaxOperatingRate(benchmark::State& state) {
+    TranscodeMediaFile(state, "video_1280x720_3648frame_h264_16Mbps_30fps_aac.mp4",
+                       "video_1280x720_3648frame_h264_16Mbps_30fps_aac_transcoded_AV.mp4",
+                       true /* includeAudio */, true /* transcodeVideo */, SetMaxOperatingRate);
+}
 //-------------------------------- HEVC to AVC Benchmarks ------------------------------------------
 
 static void BM_TranscodeHevc2AvcAudioVideo2AudioVideo(benchmark::State& state) {
@@ -273,6 +284,18 @@
                        false /* includeAudio */, true /* transcodeVideo */, SetMaxOperatingRate);
 }
 
+static void BM_TranscodeHevc2AvcAV2AV720P(benchmark::State& state) {
+    TranscodeMediaFile(state, "video_1280x720_3863frame_hevc_16Mbps_30fps_aac.mp4",
+                       "video_1280x720_3863frame_hevc_16Mbps_30fps_aac_transcoded_AV.mp4",
+                       true /* includeAudio */, true /* transcodeVideo */);
+}
+
+static void BM_TranscodeHevc2AvcAV2AV720PMaxOperatingRate(benchmark::State& state) {
+    TranscodeMediaFile(state, "video_1280x720_3863frame_hevc_16Mbps_30fps_aac.mp4",
+                       "video_1280x720_3863frame_hevc_16Mbps_30fps_aac_transcoded_AV.mp4",
+                       true /* includeAudio */, true /* transcodeVideo */, SetMaxOperatingRate);
+}
+
 //-------------------------------- Passthrough Benchmarks ------------------------------------------
 
 static void BM_TranscodeAudioVideoPassthrough(benchmark::State& state) {
@@ -296,11 +319,15 @@
 TRANSCODER_BENCHMARK(BM_TranscodeAvc2AvcVideo2Video);
 TRANSCODER_BENCHMARK(BM_TranscodeAvc2AvcAV2AVMaxOperatingRate);
 TRANSCODER_BENCHMARK(BM_TranscodeAvc2AvcV2VMaxOperatingRate);
+TRANSCODER_BENCHMARK(BM_TranscodeAvc2AvcAV2AV720P);
+TRANSCODER_BENCHMARK(BM_TranscodeAvc2AvcAV2AV720PMaxOperatingRate);
 
 TRANSCODER_BENCHMARK(BM_TranscodeHevc2AvcAudioVideo2AudioVideo);
 TRANSCODER_BENCHMARK(BM_TranscodeHevc2AvcVideo2Video);
 TRANSCODER_BENCHMARK(BM_TranscodeHevc2AvcAV2AVMaxOperatingRate);
 TRANSCODER_BENCHMARK(BM_TranscodeHevc2AvcV2VMaxOperatingRate);
+TRANSCODER_BENCHMARK(BM_TranscodeHevc2AvcAV2AV720P);
+TRANSCODER_BENCHMARK(BM_TranscodeHevc2AvcAV2AV720PMaxOperatingRate);
 
 TRANSCODER_BENCHMARK(BM_TranscodeAudioVideoPassthrough);
 TRANSCODER_BENCHMARK(BM_TranscodeVideoPassthrough);
diff --git a/media/libmediatranscoding/transcoder/include/media/MediaTranscoder.h b/media/libmediatranscoding/transcoder/include/media/MediaTranscoder.h
index 9a367ca..555cfce 100644
--- a/media/libmediatranscoding/transcoder/include/media/MediaTranscoder.h
+++ b/media/libmediatranscoding/transcoder/include/media/MediaTranscoder.h
@@ -17,6 +17,7 @@
 #ifndef ANDROID_MEDIA_TRANSCODER_H
 #define ANDROID_MEDIA_TRANSCODER_H
 
+#include <android/binder_auto_utils.h>
 #include <media/MediaSampleWriter.h>
 #include <media/MediaTrackTranscoderCallback.h>
 #include <media/NdkMediaError.h>
@@ -31,7 +32,6 @@
 namespace android {
 
 class MediaSampleReader;
-class Parcel;
 
 class MediaTranscoder : public std::enable_shared_from_this<MediaTranscoder>,
                         public MediaTrackTranscoderCallback,
@@ -56,8 +56,9 @@
          *   2) Creating a new MediaTranscoding instance with the paused state and then calling
          *      resume.
          */
-        virtual void onCodecResourceLost(const MediaTranscoder* transcoder,
-                                         const std::shared_ptr<const Parcel>& pausedState) = 0;
+        virtual void onCodecResourceLost(
+                const MediaTranscoder* transcoder,
+                const std::shared_ptr<ndk::ScopedAParcel>& pausedState) = 0;
 
         virtual ~CallbackInterface() = default;
     };
@@ -69,7 +70,7 @@
      */
     static std::shared_ptr<MediaTranscoder> create(
             const std::shared_ptr<CallbackInterface>& callbacks,
-            const std::shared_ptr<const Parcel>& pausedState = nullptr);
+            const std::shared_ptr<ndk::ScopedAParcel>& pausedState = nullptr);
 
     /** Configures source from path fd. */
     media_status_t configureSource(int fd);
@@ -102,12 +103,8 @@
      * release the transcoder instance, clear the paused state and delete the partial destination
      * file. The caller can optionally call cancel to let the transcoder clean up the partial
      * destination file.
-     *
-     * TODO: use NDK AParcel instead
-     * libbinder shouldn't be used by mainline modules. When transcoding goes mainline
-     * it needs to be replaced by stable AParcel.
      */
-    media_status_t pause(std::shared_ptr<const Parcel>* pausedState);
+    media_status_t pause(std::shared_ptr<ndk::ScopedAParcel>* pausedState);
 
     /** Resumes a paused transcoding. */
     media_status_t resume();
diff --git a/media/libmediatranscoding/transcoder/include/media/NdkCommon.h b/media/libmediatranscoding/transcoder/include/media/NdkCommon.h
index 5e24049..1a72be3 100644
--- a/media/libmediatranscoding/transcoder/include/media/NdkCommon.h
+++ b/media/libmediatranscoding/transcoder/include/media/NdkCommon.h
@@ -78,5 +78,8 @@
 void CopyFormatEntries(AMediaFormat* from, AMediaFormat* to, const EntryCopier* entries,
                        size_t entryCount);
 
+bool SetDefaultFormatValueFloat(const char* key, AMediaFormat* format, float value);
+bool SetDefaultFormatValueInt32(const char* key, AMediaFormat* format, int32_t value);
+
 }  // namespace AMediaFormatUtils
 #endif  // ANDROID_MEDIA_TRANSCODING_NDK_COMMON_H
diff --git a/media/libmediatranscoding/transcoder/tests/MediaTranscoderTests.cpp b/media/libmediatranscoding/transcoder/tests/MediaTranscoderTests.cpp
index 7a968eb..1bf2d8c 100644
--- a/media/libmediatranscoding/transcoder/tests/MediaTranscoderTests.cpp
+++ b/media/libmediatranscoding/transcoder/tests/MediaTranscoderTests.cpp
@@ -82,7 +82,7 @@
     }
 
     virtual void onCodecResourceLost(const MediaTranscoder* transcoder __unused,
-                                     const std::shared_ptr<const Parcel>& pausedState
+                                     const std::shared_ptr<ndk::ScopedAParcel>& pausedState
                                              __unused) override {}
 
     void waitForTranscodingFinished() {
diff --git a/media/libshmem/Android.bp b/media/libshmem/Android.bp
index fae98ed..c8d2284 100644
--- a/media/libshmem/Android.bp
+++ b/media/libshmem/Android.bp
@@ -41,6 +41,7 @@
     srcs: ["ShmemTest.cpp"],
     shared_libs: [
         "libbinder",
+        "libcutils",
         "libshmemcompat",
         "libshmemutil",
         "libutils",
diff --git a/media/libshmem/ShmemCompat.cpp b/media/libshmem/ShmemCompat.cpp
index 5dd83f4..246cb24 100644
--- a/media/libshmem/ShmemCompat.cpp
+++ b/media/libshmem/ShmemCompat.cpp
@@ -24,15 +24,12 @@
 
 bool convertSharedFileRegionToIMemory(const SharedFileRegion& shmem,
                                       sp<IMemory>* result) {
+    assert(result != nullptr);
+
     if (!validateSharedFileRegion(shmem)) {
         return false;
     }
 
-    if (shmem.fd.get() < 0) {
-        *result = nullptr;
-        return true;
-    }
-
     // Heap offset and size must be page aligned.
     const size_t pageSize = getpagesize();
     const size_t pageMask = ~(pageSize - 1);
@@ -52,8 +49,10 @@
         return false;
     }
 
+    uint32_t flags = !shmem.writeable ? IMemoryHeap::READ_ONLY : 0;
+
     const sp<MemoryHeapBase> heap =
-            new MemoryHeapBase(shmem.fd.get(), heapSize, 0, heapStartOffset);
+            new MemoryHeapBase(shmem.fd.get(), heapSize, flags, heapStartOffset);
     *result = sp<MemoryBase>::make(heap,
                                    shmem.offset - heapStartOffset,
                                    shmem.size);
@@ -62,16 +61,19 @@
 
 bool convertIMemoryToSharedFileRegion(const sp<IMemory>& mem,
                                       SharedFileRegion* result) {
+    assert(mem != nullptr);
+    assert(result != nullptr);
+
     *result = SharedFileRegion();
-    if (mem == nullptr) {
-        return true;
-    }
 
     ssize_t offset;
     size_t size;
 
     sp<IMemoryHeap> heap = mem->getMemory(&offset, &size);
-    if (heap != nullptr) {
+    if (size > 0) {
+        if (heap == nullptr) {
+            return false;
+        }
         // Make sure the offset and size do not overflow from int64 boundaries.
         if (size > std::numeric_limits<int64_t>::max() ||
                 offset > std::numeric_limits<int64_t>::max() ||
@@ -89,9 +91,34 @@
         result->fd.reset(base::unique_fd(fd));
         result->size = size;
         result->offset = heap->getOffset() + offset;
+        result->writeable = (heap->getFlags() & IMemoryHeap::READ_ONLY) == 0;
+    }
+    return true;
+}
+
+bool convertNullableSharedFileRegionToIMemory(const std::optional<SharedFileRegion>& shmem,
+                                              sp<IMemory>* result) {
+    assert(result != nullptr);
+
+    if (!shmem.has_value()) {
+        result->clear();
+        return true;
     }
 
-    return true;
+    return convertSharedFileRegionToIMemory(shmem.value(), result);
+}
+
+bool convertNullableIMemoryToSharedFileRegion(const sp<IMemory>& mem,
+                                              std::optional<SharedFileRegion>* result) {
+    assert(result != nullptr);
+
+    if (mem == nullptr) {
+        result->reset();
+        return true;
+    }
+
+    result->emplace();
+    return convertIMemoryToSharedFileRegion(mem, &result->value());
 }
 
 }  // namespace media
diff --git a/media/libshmem/ShmemTest.cpp b/media/libshmem/ShmemTest.cpp
index 4f11b51..874f34c 100644
--- a/media/libshmem/ShmemTest.cpp
+++ b/media/libshmem/ShmemTest.cpp
@@ -17,6 +17,7 @@
 
 #include "binder/MemoryBase.h"
 #include "binder/MemoryHeapBase.h"
+#include "cutils/ashmem.h"
 #include "media/ShmemCompat.h"
 #include "media/ShmemUtil.h"
 
@@ -24,18 +25,30 @@
 namespace media {
 namespace {
 
-// Creates a SharedFileRegion instance with a null FD.
+// Creates a SharedFileRegion instance.
 SharedFileRegion makeSharedFileRegion(int64_t offset, int64_t size) {
     SharedFileRegion shmem;
     shmem.offset = offset;
     shmem.size = size;
+    int fd = ashmem_create_region("", size + offset);
+    assert(fd >= 0);
+    shmem.fd = os::ParcelFileDescriptor(base::unique_fd(fd));
+    return shmem;
+}
+
+// Creates a SharedFileRegion instance with an invalid FD.
+SharedFileRegion makeInvalidSharedFileRegion(int64_t offset, int64_t size) {
+    SharedFileRegion shmem;
+    shmem.offset = offset;
+    shmem.size = size;
     return shmem;
 }
 
-sp<IMemory> makeIMemory(const std::vector<uint8_t>& content) {
+sp<IMemory> makeIMemory(const std::vector<uint8_t>& content, bool writeable = true) {
     constexpr size_t kOffset = 19;
 
-    sp<MemoryHeapBase> heap = new MemoryHeapBase(content.size());
+    sp<MemoryHeapBase> heap = new MemoryHeapBase(content.size(),
+                                                 !writeable ? IMemoryHeap::READ_ONLY : 0);
     sp<IMemory> result = sp<MemoryBase>::make(heap, kOffset, content.size());
     memcpy(result->unsecurePointer(), content.data(), content.size());
     return result;
@@ -46,9 +59,7 @@
     EXPECT_TRUE(validateSharedFileRegion(makeSharedFileRegion(1, 2)));
     EXPECT_FALSE(validateSharedFileRegion(makeSharedFileRegion(-1, 2)));
     EXPECT_FALSE(validateSharedFileRegion(makeSharedFileRegion(2, -1)));
-    EXPECT_TRUE(validateSharedFileRegion(makeSharedFileRegion(
-            std::numeric_limits<int64_t>::max(),
-            std::numeric_limits<int64_t>::max())));
+    EXPECT_FALSE(validateSharedFileRegion(makeInvalidSharedFileRegion(1, 2)));
 }
 
 TEST(ShmemTest, Conversion) {
@@ -59,9 +70,31 @@
         ASSERT_TRUE(convertIMemoryToSharedFileRegion(imem, &shmem));
         ASSERT_EQ(3, shmem.size);
         ASSERT_GE(shmem.fd.get(), 0);
+        ASSERT_TRUE(shmem.writeable);
         ASSERT_TRUE(convertSharedFileRegionToIMemory(shmem, &reconstructed));
     }
     ASSERT_EQ(3, reconstructed->size());
+    ASSERT_EQ(reconstructed->getMemory()->getFlags() & IMemoryHeap::READ_ONLY,  0);
+    const uint8_t* p =
+            reinterpret_cast<const uint8_t*>(reconstructed->unsecurePointer());
+    EXPECT_EQ(6, p[0]);
+    EXPECT_EQ(5, p[1]);
+    EXPECT_EQ(3, p[2]);
+}
+
+TEST(ShmemTest, ConversionReadOnly) {
+    sp<IMemory> reconstructed;
+    {
+        SharedFileRegion shmem;
+        sp<IMemory> imem = makeIMemory({6, 5, 3}, false);
+        ASSERT_TRUE(convertIMemoryToSharedFileRegion(imem, &shmem));
+        ASSERT_EQ(3, shmem.size);
+        ASSERT_GE(shmem.fd.get(), 0);
+        ASSERT_FALSE(shmem.writeable);
+        ASSERT_TRUE(convertSharedFileRegionToIMemory(shmem, &reconstructed));
+    }
+    ASSERT_EQ(3, reconstructed->size());
+    ASSERT_NE(reconstructed->getMemory()->getFlags() & IMemoryHeap::READ_ONLY,  0);
     const uint8_t* p =
             reinterpret_cast<const uint8_t*>(reconstructed->unsecurePointer());
     EXPECT_EQ(6, p[0]);
@@ -72,12 +105,11 @@
 TEST(ShmemTest, NullConversion) {
     sp<IMemory> reconstructed;
     {
-        SharedFileRegion shmem;
+        std::optional<SharedFileRegion> shmem;
         sp<IMemory> imem;
-        ASSERT_TRUE(convertIMemoryToSharedFileRegion(imem, &shmem));
-        ASSERT_EQ(0, shmem.size);
-        ASSERT_LT(shmem.fd.get(), 0);
-        ASSERT_TRUE(convertSharedFileRegionToIMemory(shmem, &reconstructed));
+        ASSERT_TRUE(convertNullableIMemoryToSharedFileRegion(imem, &shmem));
+        ASSERT_FALSE(shmem.has_value());
+        ASSERT_TRUE(convertNullableSharedFileRegionToIMemory(shmem, &reconstructed));
     }
     ASSERT_EQ(nullptr, reconstructed);
 }
diff --git a/media/libshmem/ShmemUtil.cpp b/media/libshmem/ShmemUtil.cpp
index a6d047f..e075346 100644
--- a/media/libshmem/ShmemUtil.cpp
+++ b/media/libshmem/ShmemUtil.cpp
@@ -19,6 +19,11 @@
 namespace media {
 
 bool validateSharedFileRegion(const SharedFileRegion& shmem) {
+    // FD must be valid.
+    if (shmem.fd.get() < 0) {
+        return false;
+    }
+
     // Size and offset must be non-negative.
     if (shmem.size < 0 || shmem.offset < 0) {
         return false;
diff --git a/media/libshmem/aidl/android/media/SharedFileRegion.aidl b/media/libshmem/aidl/android/media/SharedFileRegion.aidl
index c99ad95..199b647 100644
--- a/media/libshmem/aidl/android/media/SharedFileRegion.aidl
+++ b/media/libshmem/aidl/android/media/SharedFileRegion.aidl
@@ -20,16 +20,20 @@
  * A shared file region.
  *
  * This type contains the required information to share a region of a file between processes over
- * AIDL. An invalid (null) region may be represented using a negative file descriptor.
+ * AIDL.
+ * An instance of this type represents a valid FD. For representing a null SharedFileRegion, use a
+ * @nullable SharedFileRegion.
  * Primarily, this is intended for shared memory blocks.
  *
  * @hide
  */
 parcelable SharedFileRegion {
-    /** File descriptor of the region. */
+    /** File descriptor of the region. Must be valid. */
     ParcelFileDescriptor fd;
     /** Offset, in bytes within the file of the start of the region. Must be non-negative. */
     long offset;
     /** Size, in bytes of the memory region. Must be non-negative. */
     long size;
+    /** Whether the region is writeable. */
+    boolean writeable;
 }
diff --git a/media/libshmem/include/media/ShmemCompat.h b/media/libshmem/include/media/ShmemCompat.h
index 3bf7f67..ba59f25 100644
--- a/media/libshmem/include/media/ShmemCompat.h
+++ b/media/libshmem/include/media/ShmemCompat.h
@@ -19,6 +19,8 @@
 // This module contains utilities for interfacing between legacy code that is using IMemory and new
 // code that is using android.os.SharedFileRegion.
 
+#include <optional>
+
 #include "android/media/SharedFileRegion.h"
 #include "binder/IMemory.h"
 #include "utils/StrongPointer.h"
@@ -29,8 +31,7 @@
 /**
  * Converts a SharedFileRegion parcelable to an IMemory instance.
  * @param shmem The SharedFileRegion instance.
- * @param result The resulting IMemory instance, or null of the SharedFileRegion is null (has a
- *               negative FD).
+ * @param result The resulting IMemory instance. May not be null.
  * @return true if the conversion is successful (should always succeed under normal circumstances,
  *         failure usually means corrupt data).
  */
@@ -38,8 +39,19 @@
                                       sp<IMemory>* result);
 
 /**
+ * Converts a nullable SharedFileRegion parcelable to an IMemory instance.
+ * @param shmem The SharedFileRegion instance.
+ * @param result The resulting IMemory instance. May not be null. Pointee assigned to null,
+ *               if the input is null.
+ * @return true if the conversion is successful (should always succeed under normal circumstances,
+ *         failure usually means corrupt data).
+ */
+bool convertNullableSharedFileRegionToIMemory(const std::optional<SharedFileRegion>& shmem,
+                                              sp<IMemory>* result);
+
+/**
  * Converts an IMemory instance to SharedFileRegion.
- * @param mem The IMemory instance. May be null.
+ * @param mem The IMemory instance. May not be null.
  * @param result The resulting SharedFileRegion instance.
  * @return true if the conversion is successful (should always succeed under normal circumstances,
  *         failure usually means corrupt data).
@@ -47,5 +59,16 @@
 bool convertIMemoryToSharedFileRegion(const sp<IMemory>& mem,
                                       SharedFileRegion* result);
 
+/**
+ * Converts a nullable IMemory instance to a nullable SharedFileRegion.
+ * @param mem The IMemory instance. May be null.
+ * @param result The resulting SharedFileRegion instance. May not be null. Assigned to empty,
+ *               if the input is null.
+ * @return true if the conversion is successful (should always succeed under normal circumstances,
+ *         failure usually means corrupt data).
+ */
+bool convertNullableIMemoryToSharedFileRegion(const sp<IMemory>& mem,
+                                              std::optional<SharedFileRegion>* result);
+
 }  // namespace media
 }  // namespace android
diff --git a/media/libshmem/include/media/ShmemUtil.h b/media/libshmem/include/media/ShmemUtil.h
index 563cb71..3a7a5a5 100644
--- a/media/libshmem/include/media/ShmemUtil.h
+++ b/media/libshmem/include/media/ShmemUtil.h
@@ -25,7 +25,6 @@
 
 /**
  * Checks whether a SharedFileRegion instance is valid (all the fields have sane values).
- * A null SharedFileRegion (having a negative FD) is considered valid.
  */
 bool validateSharedFileRegion(const SharedFileRegion& shmem);
 
diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp
index 1a4f3d3..da8f024 100644
--- a/media/libstagefright/MediaCodec.cpp
+++ b/media/libstagefright/MediaCodec.cpp
@@ -639,6 +639,9 @@
       mDequeueInputReplyID(0),
       mDequeueOutputTimeoutGeneration(0),
       mDequeueOutputReplyID(0),
+      mTunneledInputWidth(0),
+      mTunneledInputHeight(0),
+      mTunneled(false),
       mHaveInputSurface(false),
       mHavePendingInputBuffers(false),
       mCpuBoostRequested(false),
@@ -2867,6 +2870,14 @@
 
             extractCSD(format);
 
+            int32_t tunneled;
+            if (format->findInt32("feature-tunneled-playback", &tunneled) && tunneled != 0) {
+                ALOGI("Configuring TUNNELED video playback.");
+                mTunneled = true;
+            } else {
+                mTunneled = false;
+            }
+
             mCodec->initiateConfigureComponent(format);
             break;
         }
@@ -3952,7 +3963,18 @@
     if (hasCryptoOrDescrambler() && !c2Buffer && !memory) {
         AString *errorDetailMsg;
         CHECK(msg->findPointer("errorDetailMsg", (void **)&errorDetailMsg));
-
+        // Notify mCrypto of video resolution changes
+        if (mTunneled && mCrypto != NULL) {
+            int32_t width, height;
+            if (mInputFormat->findInt32("width", &width) &&
+                mInputFormat->findInt32("height", &height) && width > 0 && height > 0) {
+                if (width != mTunneledInputWidth || height != mTunneledInputHeight) {
+                    mTunneledInputWidth = width;
+                    mTunneledInputHeight = height;
+                    mCrypto->notifyResolution(width, height);
+                }
+            }
+        }
         err = mBufferChannel->queueSecureInputBuffer(
                 buffer,
                 (mFlags & kFlagIsSecure),
diff --git a/media/libstagefright/NuMediaExtractor.cpp b/media/libstagefright/NuMediaExtractor.cpp
index 050d7c2..6245014 100644
--- a/media/libstagefright/NuMediaExtractor.cpp
+++ b/media/libstagefright/NuMediaExtractor.cpp
@@ -312,6 +312,27 @@
         (*format)->setBuffer("pssh", buf);
     }
 
+    // Copy over the slow-motion related metadata
+    const void *slomoMarkers;
+    size_t slomoMarkersSize;
+    if (meta->findData(kKeySlowMotionMarkers, &type, &slomoMarkers, &slomoMarkersSize)
+            && slomoMarkersSize > 0) {
+        sp<ABuffer> buf = new ABuffer(slomoMarkersSize);
+        memcpy(buf->data(), slomoMarkers, slomoMarkersSize);
+        (*format)->setBuffer("slow-motion-markers", buf);
+    }
+
+    int32_t temporalLayerCount;
+    if (meta->findInt32(kKeyTemporalLayerCount, &temporalLayerCount)
+            && temporalLayerCount > 0) {
+        (*format)->setInt32("temporal-layer-count", temporalLayerCount);
+    }
+
+    float captureFps;
+    if (meta->findFloat(kKeyCaptureFramerate, &captureFps) && captureFps > 0.0f) {
+        (*format)->setFloat("capture-rate", captureFps);
+    }
+
     return OK;
 }
 
diff --git a/media/libstagefright/Utils.cpp b/media/libstagefright/Utils.cpp
index 3d152bc..67493f0 100644
--- a/media/libstagefright/Utils.cpp
+++ b/media/libstagefright/Utils.cpp
@@ -1905,7 +1905,8 @@
             std::vector<uint8_t> hvcc(csd0size + 1024);
             size_t outsize = reassembleHVCC(csd0, hvcc.data(), hvcc.size(), 4);
             meta->setData(kKeyHVCC, kTypeHVCC, hvcc.data(), outsize);
-        } else if (mime == MEDIA_MIMETYPE_VIDEO_AV1) {
+        } else if (mime == MEDIA_MIMETYPE_VIDEO_AV1 ||
+                   mime == MEDIA_MIMETYPE_IMAGE_AVIF) {
             meta->setData(kKeyAV1C, 0, csd0->data(), csd0->size());
         } else if (mime == MEDIA_MIMETYPE_VIDEO_DOLBY_VISION) {
             if (msg->findBuffer("csd-2", &csd2)) {
diff --git a/media/libstagefright/codecs/amrnb/enc/Android.bp b/media/libstagefright/codecs/amrnb/enc/Android.bp
index 73a1d4b..ff9a720 100644
--- a/media/libstagefright/codecs/amrnb/enc/Android.bp
+++ b/media/libstagefright/codecs/amrnb/enc/Android.bp
@@ -81,6 +81,13 @@
     //},
 
     shared_libs: ["libstagefright_amrnb_common"],
+
+    host_supported: true,
+    target: {
+        darwin: {
+            enabled: false,
+        },
+    },
 }
 
 //###############################################################################
diff --git a/media/libstagefright/codecs/amrnb/enc/fuzzer/Android.bp b/media/libstagefright/codecs/amrnb/enc/fuzzer/Android.bp
new file mode 100644
index 0000000..e88e5eb
--- /dev/null
+++ b/media/libstagefright/codecs/amrnb/enc/fuzzer/Android.bp
@@ -0,0 +1,41 @@
+/******************************************************************************
+ *
+ * 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.
+ *
+ *****************************************************************************
+ * Originally developed and contributed by Ittiam Systems Pvt. Ltd, Bangalore
+ */
+
+cc_fuzz {
+    name: "amrnb_enc_fuzzer",
+    host_supported: true,
+
+    srcs: [
+        "amrnb_enc_fuzzer.cpp",
+    ],
+
+    static_libs: [
+        "liblog",
+        "libstagefright_amrnbenc",
+        "libstagefright_amrnb_common",
+    ],
+
+    fuzz_config: {
+        cc: [
+            "android-media-fuzzing-reports@google.com",
+        ],
+        componentid: 155276,
+    },
+}
diff --git a/media/libstagefright/codecs/amrnb/enc/fuzzer/README.md b/media/libstagefright/codecs/amrnb/enc/fuzzer/README.md
new file mode 100644
index 0000000..239b4a8
--- /dev/null
+++ b/media/libstagefright/codecs/amrnb/enc/fuzzer/README.md
@@ -0,0 +1,60 @@
+# Fuzzer for libstagefright_amrnbenc encoder
+
+## Plugin Design Considerations
+The fuzzer plugin for AMR-NB is designed based on the understanding of the
+codec and tries to achieve the following:
+
+##### Maximize code coverage
+The configuration parameters are not hardcoded, but instead selected based on
+incoming data. This ensures more code paths are reached by the fuzzer.
+
+AMR-WB supports the following parameters:
+1. Output Format (parameter name: `outputFormat`)
+2. Mode (parameter name: `mode`)
+
+| Parameter| Valid Values| Configured Value|
+|------------- |-------------| ----- |
+| `outputFormat` | 0. `AMR_TX_WMF` 1. `AMR_TX_IF2` 2. `AMR_TX_ETS` | Bits 0, 1 and 2 of 1st byte of data. |
+| `mode`   | 0. `MR475` 1. `MR515` 2. `MR59` 3. `MR67`  4. `MR74 ` 5. `MR795` 6. `MR102` 7. `MR122` 8. `MRDTX` | Bits 3, 4, 5 and 6 of 1st byte of data. |
+
+This also ensures that the plugin is always deterministic for any given input.
+
+##### Maximize utilization of input data
+The plugin feeds the entire input data to the codec using a loop.
+If the encode operation was successful, the input is advanced by the frame size.
+If the encode operation was un-successful, the input is still advanced by frame size so
+that the fuzzer can proceed to feed the next frame.
+
+This ensures that the plugin tolerates any kind of input (empty, huge,
+malformed, etc) and doesnt `exit()` on any input and thereby increasing the
+chance of identifying vulnerabilities.
+
+## Build
+
+This describes steps to build amrnb_enc_fuzzer binary.
+
+### Android
+
+#### Steps to build
+Build the fuzzer
+```
+  $ mm -j$(nproc) amrnb_enc_fuzzer
+```
+
+#### Steps to run
+Create a directory CORPUS_DIR and copy some pcm files to that folder
+Push this directory to device.
+
+To run on device
+```
+  $ adb sync data
+  $ adb shell /data/fuzz/arm64/amrnb_enc_fuzzer/amrnb_enc_fuzzer CORPUS_DIR
+```
+To run on host
+```
+  $ $ANDROID_HOST_OUT/fuzz/x86_64/amrnb_enc_fuzzer/amrnb_enc_fuzzer CORPUS_DIR
+```
+
+## References:
+ * http://llvm.org/docs/LibFuzzer.html
+ * https://github.com/google/oss-fuzz
diff --git a/media/libstagefright/codecs/amrnb/enc/fuzzer/amrnb_enc_fuzzer.cpp b/media/libstagefright/codecs/amrnb/enc/fuzzer/amrnb_enc_fuzzer.cpp
new file mode 100644
index 0000000..2fcbf24
--- /dev/null
+++ b/media/libstagefright/codecs/amrnb/enc/fuzzer/amrnb_enc_fuzzer.cpp
@@ -0,0 +1,105 @@
+/******************************************************************************
+ *
+ * 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.
+ *
+ *****************************************************************************
+ * Originally developed and contributed by Ittiam Systems Pvt. Ltd, Bangalore
+ */
+#include <string.h>
+#include <utils/Log.h>
+#include <algorithm>
+#include "gsmamr_enc.h"
+
+// Constants for AMR-NB
+const int32_t kNumInputSamples = L_FRAME;  // 160 samples
+const int32_t kOutputBufferSize = 2 * kNumInputSamples * sizeof(Word16);
+const Mode kModes[9] = {MR475, /* 4.75 kbps */
+                        MR515, /* 5.15 kbps */
+                        MR59,  /* 5.90 kbps */
+                        MR67,  /* 6.70 kbps */
+                        MR74,  /* 7.40 kbps */
+                        MR795, /* 7.95 kbps */
+                        MR102, /* 10.2 kbps */
+                        MR122, /* 12.2 kbps */
+                        MRDTX, /* DTX       */};
+const Word16 kOutputFormat[3] = {AMR_TX_WMF, AMR_TX_IF2, AMR_TX_ETS};
+
+class Codec {
+   public:
+    Codec() = default;
+    ~Codec() { deInitEncoder(); }
+    Word16 initEncoder(const uint8_t *data);
+    void deInitEncoder();
+    void encodeFrames(const uint8_t *data, size_t size);
+
+   private:
+    void *mEncState = nullptr;
+    void *mSidState = nullptr;
+};
+
+Word16 Codec::initEncoder(const uint8_t *data) {
+    return AMREncodeInit(&mEncState, &mSidState, (*data >> 1) & 0x01 /* dtx_enable flag */);
+}
+
+void Codec::deInitEncoder() {
+    if (mEncState) {
+        AMREncodeExit(&mEncState, &mSidState);
+        mEncState = nullptr;
+        mSidState = nullptr;
+    }
+}
+
+void Codec::encodeFrames(const uint8_t *data, size_t size) {
+    AMREncodeReset(mEncState, mSidState);
+    uint8_t startByte = *data;
+    int modeIndex = ((startByte >> 3) % 9);
+    int outputFormatIndex = (startByte % 3);
+    Mode mode = kModes[modeIndex];
+    Word16 outputFormat = kOutputFormat[outputFormatIndex];
+
+    // Consume startByte
+    data++;
+    size--;
+
+    while (size > 0) {
+        Frame_Type_3GPP frameType = (Frame_Type_3GPP)mode;
+
+        Word16 inputBuf[kNumInputSamples] = {};
+        int32_t minSize = std::min(size, sizeof(inputBuf));
+
+        uint8_t outputBuf[kOutputBufferSize] = {};
+        memcpy(inputBuf, data, minSize);
+
+        AMREncode(mEncState, mSidState, mode, inputBuf, outputBuf, &frameType, outputFormat);
+
+        data += minSize;
+        size -= minSize;
+    }
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+    if (size < 1) {
+        return 0;
+    }
+    Codec *codec = new Codec();
+    if (!codec) {
+        return 0;
+    }
+    if (codec->initEncoder(data) == 0) {
+        codec->encodeFrames(data, size);
+    }
+    delete codec;
+    return 0;
+}
diff --git a/media/libstagefright/codecs/amrwbenc/Android.bp b/media/libstagefright/codecs/amrwbenc/Android.bp
index 64f302c..70c672d 100644
--- a/media/libstagefright/codecs/amrwbenc/Android.bp
+++ b/media/libstagefright/codecs/amrwbenc/Android.bp
@@ -138,6 +138,12 @@
         cfi: true,
     },
 
+    host_supported: true,
+    target: {
+        darwin: {
+            enabled: false,
+        },
+    },
 }
 
 //###############################################################################
diff --git a/media/libstagefright/codecs/amrwbenc/fuzzer/Android.bp b/media/libstagefright/codecs/amrwbenc/fuzzer/Android.bp
new file mode 100644
index 0000000..e3473d6
--- /dev/null
+++ b/media/libstagefright/codecs/amrwbenc/fuzzer/Android.bp
@@ -0,0 +1,41 @@
+/******************************************************************************
+ *
+ * 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.
+ *
+ *****************************************************************************
+ * Originally developed and contributed by Ittiam Systems Pvt. Ltd, Bangalore
+ */
+
+cc_fuzz {
+    name: "amrwb_enc_fuzzer",
+    host_supported: true,
+
+    srcs: [
+        "amrwb_enc_fuzzer.cpp",
+    ],
+
+    static_libs: [
+        "liblog",
+        "libstagefright_amrwbenc",
+        "libstagefright_enc_common",
+    ],
+
+    fuzz_config: {
+        cc: [
+            "android-media-fuzzing-reports@google.com",
+        ],
+        componentid: 155276,
+    },
+}
diff --git a/media/libstagefright/codecs/amrwbenc/fuzzer/README.md b/media/libstagefright/codecs/amrwbenc/fuzzer/README.md
new file mode 100644
index 0000000..447fbfa
--- /dev/null
+++ b/media/libstagefright/codecs/amrwbenc/fuzzer/README.md
@@ -0,0 +1,60 @@
+# Fuzzer for libstagefright_amrwbenc encoder
+
+## Plugin Design Considerations
+The fuzzer plugin for AMR-WB is designed based on the understanding of the
+codec and tries to achieve the following:
+
+##### Maximize code coverage
+The configuration parameters are not hardcoded, but instead selected based on
+incoming data. This ensures more code paths are reached by the fuzzer.
+
+AMR-WB supports the following parameters:
+1. Frame Type (parameter name: `frameType`)
+2. Mode (parameter name: `mode`)
+
+| Parameter| Valid Values| Configured Value|
+|------------- |-------------| ----- |
+| `frameType` | 0. `VOAMRWB_DEFAULT` 1. `VOAMRWB_ITU` 2. `VOAMRWB_RFC3267` | Bits 0, 1 and 2 of 1st byte of data. |
+| `mode`   | 0. `VOAMRWB_MD66` 1. `VOAMRWB_MD885` 2. `VOAMRWB_MD1265` 3. `VOAMRWB_MD1425`  4. `VOAMRWB_MD1585 ` 5. `VOAMRWB_MD1825` 6. `VOAMRWB_MD1985` 7. `VOAMRWB_MD2305` 8. `VOAMRWB_MD2385` 9. `VOAMRWB_N_MODES` | Bits 4, 5, 6 and 7 of 1st byte of data. |
+
+This also ensures that the plugin is always deterministic for any given input.
+
+##### Maximize utilization of input data
+The plugin feeds the entire input data to the codec using a loop.
+If the encode operation was successful, the input is advanced by the frame size.
+If the encode operation was un-successful, the input is still advanced by frame size so
+that the fuzzer can proceed to feed the next frame.
+
+This ensures that the plugin tolerates any kind of input (empty, huge,
+malformed, etc) and doesnt `exit()` on any input and thereby increasing the
+chance of identifying vulnerabilities.
+
+## Build
+
+This describes steps to build amrwb_enc_fuzzer binary.
+
+### Android
+
+#### Steps to build
+Build the fuzzer
+```
+  $ mm -j$(nproc) amrwb_enc_fuzzer
+```
+
+#### Steps to run
+Create a directory CORPUS_DIR and copy some pcm files to that folder
+Push this directory to device.
+
+To run on device
+```
+  $ adb sync data
+  $ adb shell /data/fuzz/arm64/amrwb_enc_fuzzer/amrwb_enc_fuzzer CORPUS_DIR
+```
+To run on host
+```
+  $ $ANDROID_HOST_OUT/fuzz/x86_64/amrwb_enc_fuzzer/amrwb_enc_fuzzer CORPUS_DIR
+```
+
+## References:
+ * http://llvm.org/docs/LibFuzzer.html
+ * https://github.com/google/oss-fuzz
diff --git a/media/libstagefright/codecs/amrwbenc/fuzzer/amrwb_enc_fuzzer.cpp b/media/libstagefright/codecs/amrwbenc/fuzzer/amrwb_enc_fuzzer.cpp
new file mode 100644
index 0000000..4773a1f
--- /dev/null
+++ b/media/libstagefright/codecs/amrwbenc/fuzzer/amrwb_enc_fuzzer.cpp
@@ -0,0 +1,142 @@
+/******************************************************************************
+ *
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *****************************************************************************
+ * Originally developed and contributed by Ittiam Systems Pvt. Ltd, Bangalore
+ */
+#include <string.h>
+#include <utils/Log.h>
+#include <algorithm>
+#include "cmnMemory.h"
+#include "voAMRWB.h"
+#include "cnst.h"
+
+typedef int(VO_API *VOGETAUDIOENCAPI)(VO_AUDIO_CODECAPI *pEncHandle);
+const int32_t kInputBufferSize = L_FRAME16k * sizeof(int16_t) * 2;
+const int32_t kOutputBufferSize = 2 * kInputBufferSize;
+const int32_t kModes[] = {VOAMRWB_MD66 /* 6.60kbps */,    VOAMRWB_MD885 /* 8.85kbps */,
+                          VOAMRWB_MD1265 /* 12.65kbps */, VOAMRWB_MD1425 /* 14.25kbps */,
+                          VOAMRWB_MD1585 /* 15.85kbps */, VOAMRWB_MD1825 /* 18.25kbps */,
+                          VOAMRWB_MD1985 /* 19.85kbps */, VOAMRWB_MD2305 /* 23.05kbps */,
+                          VOAMRWB_MD2385 /* 23.85kbps */, VOAMRWB_N_MODES /* Invalid Mode */};
+const VOAMRWBFRAMETYPE kFrameTypes[] = {VOAMRWB_DEFAULT, VOAMRWB_ITU, VOAMRWB_RFC3267};
+
+class Codec {
+   public:
+    Codec() = default;
+    ~Codec() { deInitEncoder(); }
+    bool initEncoder(const uint8_t *data);
+    void deInitEncoder();
+    void encodeFrames(const uint8_t *data, size_t size);
+
+   private:
+    VO_AUDIO_CODECAPI *mApiHandle = nullptr;
+    VO_MEM_OPERATOR *mMemOperator = nullptr;
+    VO_HANDLE mEncoderHandle = nullptr;
+};
+
+bool Codec::initEncoder(const uint8_t *data) {
+    uint8_t startByte = *data;
+    int32_t mode = kModes[(startByte >> 4) % 10];
+    VOAMRWBFRAMETYPE frameType = kFrameTypes[startByte % 3];
+    mMemOperator = new VO_MEM_OPERATOR;
+    if (!mMemOperator) {
+        return false;
+    }
+
+    mMemOperator->Alloc = cmnMemAlloc;
+    mMemOperator->Copy = cmnMemCopy;
+    mMemOperator->Free = cmnMemFree;
+    mMemOperator->Set = cmnMemSet;
+    mMemOperator->Check = cmnMemCheck;
+
+    VO_CODEC_INIT_USERDATA userData;
+    memset(&userData, 0, sizeof(userData));
+    userData.memflag = VO_IMF_USERMEMOPERATOR;
+    userData.memData = (VO_PTR)mMemOperator;
+
+    mApiHandle = new VO_AUDIO_CODECAPI;
+    if (!mApiHandle) {
+        return false;
+    }
+    if (VO_ERR_NONE != voGetAMRWBEncAPI(mApiHandle)) {
+        // Failed to get api handle
+        return false;
+    }
+    if (VO_ERR_NONE != mApiHandle->Init(&mEncoderHandle, VO_AUDIO_CodingAMRWB, &userData)) {
+        // Failed to init AMRWB encoder
+        return false;
+    }
+    if (VO_ERR_NONE != mApiHandle->SetParam(mEncoderHandle, VO_PID_AMRWB_FRAMETYPE, &frameType)) {
+        // Failed to set AMRWB encoder frame type
+        return false;
+    }
+    if (VO_ERR_NONE != mApiHandle->SetParam(mEncoderHandle, VO_PID_AMRWB_MODE, &mode)) {
+        // Failed to set AMRWB encoder mode
+        return false;
+    }
+    return true;
+}
+
+void Codec::deInitEncoder() {
+    if (mEncoderHandle) {
+        mApiHandle->Uninit(mEncoderHandle);
+        mEncoderHandle = nullptr;
+    }
+    if (mApiHandle) {
+        delete mApiHandle;
+        mApiHandle = nullptr;
+    }
+    if (mMemOperator) {
+        delete mMemOperator;
+        mMemOperator = nullptr;
+    }
+}
+
+void Codec::encodeFrames(const uint8_t *data, size_t size) {
+    do {
+        int32_t minSize = std::min((int32_t)size, kInputBufferSize);
+        uint8_t outputBuf[kOutputBufferSize] = {};
+        VO_CODECBUFFER inData;
+        VO_CODECBUFFER outData;
+        VO_AUDIO_OUTPUTINFO outFormat;
+        inData.Buffer = (unsigned char *)data;
+        inData.Length = minSize;
+        outData.Buffer = outputBuf;
+        mApiHandle->SetInputData(mEncoderHandle, &inData);
+        mApiHandle->GetOutputData(mEncoderHandle, &outData, &outFormat);
+        data += minSize;
+        size -= minSize;
+    } while (size > 0);
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+    if (size < 1) {
+        return 0;
+    }
+    Codec *codec = new Codec();
+    if (!codec) {
+        return 0;
+    }
+    if (codec->initEncoder(data)) {
+        // Consume first byte
+        ++data;
+        --size;
+        codec->encodeFrames(data, size);
+    }
+    delete codec;
+    return 0;
+}
diff --git a/media/libstagefright/codecs/amrwbenc/src/preemph.c b/media/libstagefright/codecs/amrwbenc/src/preemph.c
index 70c8650..a43841a 100644
--- a/media/libstagefright/codecs/amrwbenc/src/preemph.c
+++ b/media/libstagefright/codecs/amrwbenc/src/preemph.c
@@ -24,6 +24,7 @@
 
 #include "typedef.h"
 #include "basic_op.h"
+#include <stdint.h>
 
 void Preemph(
         Word16 x[],                           /* (i/o)   : input signal overwritten by the output */
diff --git a/media/libstagefright/codecs/amrwbenc/src/q_pulse.c b/media/libstagefright/codecs/amrwbenc/src/q_pulse.c
index fe0bdda..657b6fe 100644
--- a/media/libstagefright/codecs/amrwbenc/src/q_pulse.c
+++ b/media/libstagefright/codecs/amrwbenc/src/q_pulse.c
@@ -27,6 +27,7 @@
 #include "q_pulse.h"
 
 #define NB_POS 16                          /* pos in track, mask for sign bit */
+#define UNUSED_VAR __attribute__((unused))
 
 Word32 quant_1p_N1(                        /* (o) return N+1 bits             */
         Word16 pos,                        /* (i) position of the pulse       */
@@ -188,7 +189,7 @@
         Word16 pos[],                         /* (i) position of the pulse 1..4  */
         Word16 N)                             /* (i) number of bits for position */
 {
-    Word16 nb_pos, mask __unused, n_1, tmp;
+    Word16 nb_pos, mask UNUSED_VAR, n_1, tmp;
     Word16 posA[4], posB[4];
     Word32 i, j, k, index;
 
diff --git a/media/libstagefright/codecs/common/Android.bp b/media/libstagefright/codecs/common/Android.bp
index 260a60a..2290722 100644
--- a/media/libstagefright/codecs/common/Android.bp
+++ b/media/libstagefright/codecs/common/Android.bp
@@ -14,4 +14,11 @@
     export_include_dirs: ["include"],
 
     cflags: ["-Werror"],
+
+    host_supported: true,
+    target: {
+        darwin: {
+            enabled: false,
+        },
+    },
 }
diff --git a/media/libstagefright/codecs/m4v_h263/dec/src/idct_vca.cpp b/media/libstagefright/codecs/m4v_h263/dec/src/idct_vca.cpp
index 0ba4944..dbaf5d1 100644
--- a/media/libstagefright/codecs/m4v_h263/dec/src/idct_vca.cpp
+++ b/media/libstagefright/codecs/m4v_h263/dec/src/idct_vca.cpp
@@ -37,6 +37,7 @@
     return ;
 }
 
+__attribute__((no_sanitize("signed-integer-overflow")))
 void idctrow1(int16 *blk, uint8 *pred, uint8 *dst, int width)
 {
     /* shortcut */
@@ -156,6 +157,7 @@
     return ;
 }
 
+__attribute__((no_sanitize("signed-integer-overflow")))
 void idctcol2(int16 *blk)
 {
     int32 x0, x1, x3, x5, x7;//, x8;
@@ -256,6 +258,7 @@
     return ;
 }
 
+__attribute__((no_sanitize("signed-integer-overflow")))
 void idctcol3(int16 *blk)
 {
     int32 x0, x1, x2, x3, x4, x5, x6, x7, x8;
diff --git a/media/libstagefright/codecs/m4v_h263/enc/Android.bp b/media/libstagefright/codecs/m4v_h263/enc/Android.bp
index b8bc24e..13d310d 100644
--- a/media/libstagefright/codecs/m4v_h263/enc/Android.bp
+++ b/media/libstagefright/codecs/m4v_h263/enc/Android.bp
@@ -6,6 +6,12 @@
         "com.android.media.swcodec",
     ],
     min_sdk_version: "29",
+    host_supported: true,
+    target: {
+        darwin: {
+            enabled: false,
+        },
+    },
 
     srcs: [
         "src/bitstream_io.cpp",
diff --git a/media/libstagefright/codecs/mp3dec/src/pvmp3_framedecoder.cpp b/media/libstagefright/codecs/mp3dec/src/pvmp3_framedecoder.cpp
index c64cf46..15d2feb 100644
--- a/media/libstagefright/codecs/mp3dec/src/pvmp3_framedecoder.cpp
+++ b/media/libstagefright/codecs/mp3dec/src/pvmp3_framedecoder.cpp
@@ -219,7 +219,7 @@
 
     if (info->error_protection)
     {
-        if (bitsAvailable(&pVars->inputStream, 16))
+        if (!bitsAvailable(&pVars->inputStream, 16))
         {
             return SIDE_INFO_ERROR;
         }
diff --git a/media/libstagefright/codecs/mp3dec/test/AndroidTest.xml b/media/libstagefright/codecs/mp3dec/test/AndroidTest.xml
index 233f9bb..29952eb 100644
--- a/media/libstagefright/codecs/mp3dec/test/AndroidTest.xml
+++ b/media/libstagefright/codecs/mp3dec/test/AndroidTest.xml
@@ -19,7 +19,7 @@
         <option name="cleanup" value="true" />
         <option name="push" value="Mp3DecoderTest->/data/local/tmp/Mp3DecoderTest" />
         <option name="push-file"
-            key="https://storage.googleapis.com/android_media/frameworks/av/media/libstagefright/mp3dec/test/Mp3DecoderTest-1.1.zip?unzip=true"
+            key="https://storage.googleapis.com/android_media/frameworks/av/media/libstagefright/mp3dec/test/Mp3DecoderTest-1.2.zip?unzip=true"
             value="/data/local/tmp/Mp3DecoderTestRes/" />
     </target_preparer>
 
diff --git a/media/libstagefright/codecs/mp3dec/test/Mp3DecoderTest.cpp b/media/libstagefright/codecs/mp3dec/test/Mp3DecoderTest.cpp
index 0784c0c..91326a8 100644
--- a/media/libstagefright/codecs/mp3dec/test/Mp3DecoderTest.cpp
+++ b/media/libstagefright/codecs/mp3dec/test/Mp3DecoderTest.cpp
@@ -186,6 +186,7 @@
                          ::testing::Values(("bbb_44100hz_2ch_128kbps_mp3_30sec.mp3"),
                                            ("bbb_44100hz_2ch_128kbps_mp3_5mins.mp3"),
                                            ("bug_136053885.mp3"),
+                                           ("bbb_2ch_44kHz_lame_crc.mp3"),
                                            ("bbb_mp3_stereo_192kbps_48000hz.mp3")));
 
 int main(int argc, char **argv) {
diff --git a/media/libstagefright/foundation/MediaDefs.cpp b/media/libstagefright/foundation/MediaDefs.cpp
index a08fed1..c216bc5 100644
--- a/media/libstagefright/foundation/MediaDefs.cpp
+++ b/media/libstagefright/foundation/MediaDefs.cpp
@@ -20,6 +20,7 @@
 
 const char *MEDIA_MIMETYPE_IMAGE_JPEG = "image/jpeg";
 const char *MEDIA_MIMETYPE_IMAGE_ANDROID_HEIC = "image/vnd.android.heic";
+const char *MEDIA_MIMETYPE_IMAGE_AVIF = "image/avif";
 
 const char *MEDIA_MIMETYPE_VIDEO_VP8 = "video/x-vnd.on2.vp8";
 const char *MEDIA_MIMETYPE_VIDEO_VP9 = "video/x-vnd.on2.vp9";
diff --git a/media/libstagefright/foundation/include/media/stagefright/foundation/MediaDefs.h b/media/libstagefright/foundation/include/media/stagefright/foundation/MediaDefs.h
index 1f9e636..e96243e 100644
--- a/media/libstagefright/foundation/include/media/stagefright/foundation/MediaDefs.h
+++ b/media/libstagefright/foundation/include/media/stagefright/foundation/MediaDefs.h
@@ -22,6 +22,7 @@
 
 extern const char *MEDIA_MIMETYPE_IMAGE_JPEG;
 extern const char *MEDIA_MIMETYPE_IMAGE_ANDROID_HEIC;
+extern const char *MEDIA_MIMETYPE_IMAGE_AVIF;
 
 extern const char *MEDIA_MIMETYPE_VIDEO_VP8;
 extern const char *MEDIA_MIMETYPE_VIDEO_VP9;
diff --git a/media/libstagefright/include/media/stagefright/MediaCodec.h b/media/libstagefright/include/media/stagefright/MediaCodec.h
index c246b36..a28d479 100644
--- a/media/libstagefright/include/media/stagefright/MediaCodec.h
+++ b/media/libstagefright/include/media/stagefright/MediaCodec.h
@@ -425,6 +425,10 @@
 
     sp<ICrypto> mCrypto;
 
+    int32_t mTunneledInputWidth;
+    int32_t mTunneledInputHeight;
+    bool mTunneled;
+
     sp<IDescrambler> mDescrambler;
 
     List<sp<ABuffer> > mCSD;
diff --git a/media/libstagefright/mpeg2ts/ESQueue.cpp b/media/libstagefright/mpeg2ts/ESQueue.cpp
index 801dba1..192ba77 100644
--- a/media/libstagefright/mpeg2ts/ESQueue.cpp
+++ b/media/libstagefright/mpeg2ts/ESQueue.cpp
@@ -1430,7 +1430,13 @@
                 if (mSampleDecryptor != NULL && (nalType == 1 || nalType == 5)) {
                     uint8_t *nalData = mBuffer->data() + pos.nalOffset;
                     size_t newSize = mSampleDecryptor->processNal(nalData, pos.nalSize);
-                    // Note: the data can shrink due to unescaping
+                    // Note: the data can shrink due to unescaping, but it can never grow
+                    if (newSize > pos.nalSize) {
+                        // don't log unless verbose, since this can get called a lot if
+                        // the caller is trying to resynchronize
+                        ALOGV("expected sample size < %u, got %zu", pos.nalSize, newSize);
+                        return NULL;
+                    }
                     memcpy(accessUnit->data() + dstOffset + 4,
                             nalData,
                             newSize);
diff --git a/media/libstagefright/omx/OMXUtils.cpp b/media/libstagefright/omx/OMXUtils.cpp
index d6d280f..49b2dec 100644
--- a/media/libstagefright/omx/OMXUtils.cpp
+++ b/media/libstagefright/omx/OMXUtils.cpp
@@ -172,6 +172,8 @@
             "audio_decoder.ac4", "audio_encoder.ac4" },
         { MEDIA_MIMETYPE_IMAGE_ANDROID_HEIC,
             "image_decoder.heic", "image_encoder.heic" },
+        { MEDIA_MIMETYPE_IMAGE_AVIF,
+            "image_decoder.avif", "image_encoder.avif" },
     };
 
     static const size_t kNumMimeToRole =
diff --git a/media/libstagefright/tests/fuzzers/Android.bp b/media/libstagefright/tests/fuzzers/Android.bp
new file mode 100644
index 0000000..49ff69a
--- /dev/null
+++ b/media/libstagefright/tests/fuzzers/Android.bp
@@ -0,0 +1,53 @@
+cc_defaults {
+    name: "libstagefright_fuzzer_defaults",
+    cflags: [
+        "-Wno-multichar",
+        "-Werror",
+        "-Wno-error=deprecated-declarations",
+        "-Wall",
+    ],
+    shared_libs: [
+        "libstagefright",
+	"libstagefright_codecbase",
+        "libutils",
+        "libstagefright_foundation",
+        "libmedia",
+        "libaudioclient",
+        "libmedia_omx",
+        "libgui",
+        "libbinder",
+        "libcutils",
+    ],
+}
+
+cc_fuzz {
+    name: "libstagefright_mediaclock_fuzzer",
+    srcs: [
+        "MediaClockFuzzer.cpp",
+    ],
+    defaults: ["libstagefright_fuzzer_defaults"],
+}
+
+cc_fuzz {
+    name: "libstagefright_mediascanner_fuzzer",
+    srcs: [
+        "StagefrightMediaScannerFuzzer.cpp",
+    ],
+    defaults: ["libstagefright_fuzzer_defaults"],
+}
+
+cc_fuzz {
+    name: "libstagefright_skipcutbuffer_fuzzer",
+    srcs: [
+        "SkipCutBufferFuzzer.cpp",
+    ],
+    defaults: ["libstagefright_fuzzer_defaults"],
+}
+
+cc_fuzz {
+    name: "libstagefright_mediamuxer_fuzzer",
+    srcs: [
+        "MediaMuxerFuzzer.cpp",
+    ],
+    defaults: ["libstagefright_fuzzer_defaults"],
+}
diff --git a/media/libstagefright/tests/fuzzers/MediaClockFuzzer.cpp b/media/libstagefright/tests/fuzzers/MediaClockFuzzer.cpp
new file mode 100644
index 0000000..e473541
--- /dev/null
+++ b/media/libstagefright/tests/fuzzers/MediaClockFuzzer.cpp
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+// Authors: corbin.souffrant@leviathansecurity.com
+//          dylan.katz@leviathansecurity.com
+
+#include <fuzzer/FuzzedDataProvider.h>
+#include <media/stagefright/MediaClock.h>
+#include <media/stagefright/foundation/AMessage.h>
+
+namespace android {
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+  FuzzedDataProvider fdp = FuzzedDataProvider(data, size);
+  sp<MediaClock> mClock(new MediaClock);
+
+  bool registered = false;
+  while (fdp.remaining_bytes() > 0) {
+    switch (fdp.ConsumeIntegralInRange<uint8_t>(0, 5)) {
+    case 0: {
+      if (registered == false) {
+        mClock->init();
+        registered = true;
+      }
+      break;
+    }
+    case 1: {
+      int64_t startingTimeMediaUs = fdp.ConsumeIntegral<int64_t>();
+      mClock->setStartingTimeMedia(startingTimeMediaUs);
+      break;
+    }
+    case 2: {
+      mClock->clearAnchor();
+      break;
+    }
+    case 3: {
+      int64_t anchorTimeRealUs = fdp.ConsumeIntegral<int64_t>();
+      int64_t anchorTimeMediaUs = fdp.ConsumeIntegral<int64_t>();
+      int64_t maxTimeMediaUs = fdp.ConsumeIntegral<int64_t>();
+      mClock->updateAnchor(anchorTimeMediaUs, anchorTimeRealUs, maxTimeMediaUs);
+      break;
+    }
+    case 4: {
+      int64_t maxTimeMediaUs = fdp.ConsumeIntegral<int64_t>();
+      mClock->updateMaxTimeMedia(maxTimeMediaUs);
+      break;
+    }
+    case 5: {
+      wp<AMessage> msg(new AMessage);
+      mClock->setNotificationMessage(msg.promote());
+    }
+    }
+  }
+
+  return 0;
+}
+} // namespace android
diff --git a/media/libstagefright/tests/fuzzers/MediaMuxerFuzzer.cpp b/media/libstagefright/tests/fuzzers/MediaMuxerFuzzer.cpp
new file mode 100644
index 0000000..5df3267
--- /dev/null
+++ b/media/libstagefright/tests/fuzzers/MediaMuxerFuzzer.cpp
@@ -0,0 +1,103 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+// Authors: corbin.souffrant@leviathansecurity.com
+//          dylan.katz@leviathansecurity.com
+
+#include <MediaMuxerFuzzer.h>
+#include <cutils/ashmem.h>
+#include <fuzzer/FuzzedDataProvider.h>
+#include <media/stagefright/MediaMuxer.h>
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/AMessage.h>
+
+namespace android {
+
+// Can't seem to get setBuffer or setString working. It always segfaults on a
+// null pointer read or memleaks. So that functionality is missing.
+void createMessage(AMessage *msg, FuzzedDataProvider *fdp) {
+  size_t count = fdp->ConsumeIntegralInRange<size_t>(0, 32);
+  while (fdp->remaining_bytes() > 0 && count > 0) {
+    uint8_t function_id =
+        fdp->ConsumeIntegralInRange<uint8_t>(0, amessage_setvals.size() - 1);
+    amessage_setvals[function_id](msg, fdp);
+    count--;
+  }
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+  FuzzedDataProvider fdp = FuzzedDataProvider(data, size);
+
+  size_t data_size = fdp.ConsumeIntegralInRange<size_t>(0, size);
+  int fd = ashmem_create_region("mediamuxer_fuzz_region", data_size);
+  if (fd < 0)
+    return 0;
+
+  uint8_t *sh_data = static_cast<uint8_t *>(
+      mmap(NULL, data_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0));
+  if (sh_data == MAP_FAILED)
+    return 0;
+
+  MediaMuxer::OutputFormat format =
+      (MediaMuxer::OutputFormat)fdp.ConsumeIntegralInRange<int32_t>(0, 4);
+  sp<MediaMuxer> mMuxer(new MediaMuxer(fd, format));
+
+  while (fdp.remaining_bytes() > 1) {
+    switch (fdp.ConsumeIntegralInRange<uint8_t>(0, 4)) {
+    case 0: {
+      // For some reason it only likes mp4s here...
+      if (format == 1 || format == 4)
+        break;
+
+      sp<AMessage> a_format(new AMessage);
+      createMessage(a_format.get(), &fdp);
+      mMuxer->addTrack(a_format);
+      break;
+    }
+    case 1: {
+      mMuxer->start();
+      break;
+    }
+    case 2: {
+      int degrees = fdp.ConsumeIntegral<int>();
+      mMuxer->setOrientationHint(degrees);
+      break;
+    }
+    case 3: {
+      int latitude = fdp.ConsumeIntegral<int>();
+      int longitude = fdp.ConsumeIntegral<int>();
+      mMuxer->setLocation(latitude, longitude);
+      break;
+    }
+    case 4: {
+      size_t buf_size = fdp.ConsumeIntegralInRange<size_t>(0, data_size);
+      sp<ABuffer> a_buffer(new ABuffer(buf_size));
+
+      size_t trackIndex = fdp.ConsumeIntegral<size_t>();
+      int64_t timeUs = fdp.ConsumeIntegral<int64_t>();
+      uint32_t flags = fdp.ConsumeIntegral<uint32_t>();
+      mMuxer->writeSampleData(a_buffer, trackIndex, timeUs, flags);
+    }
+    }
+  }
+
+  if (fdp.ConsumeBool())
+    mMuxer->stop();
+
+  munmap(sh_data, data_size);
+  close(fd);
+  return 0;
+}
+} // namespace android
diff --git a/media/libstagefright/tests/fuzzers/MediaMuxerFuzzer.h b/media/libstagefright/tests/fuzzers/MediaMuxerFuzzer.h
new file mode 100644
index 0000000..7d4421d
--- /dev/null
+++ b/media/libstagefright/tests/fuzzers/MediaMuxerFuzzer.h
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+// Authors: corbin.souffrant@leviathansecurity.com
+//          dylan.katz@leviathansecurity.com
+
+#pragma once
+
+#include <fuzzer/FuzzedDataProvider.h>
+#include <media/stagefright/foundation/AMessage.h>
+
+namespace android {
+
+// Mappings vectors are the list of attributes that the MediaMuxer
+// class looks for in the message.
+static std::vector<const char *> floatMappings{
+    "capture-rate",
+    "time-lapse-fps",
+    "frame-rate",
+};
+
+static std::vector<const char *> int64Mappings{
+    "exif-offset",    "exif-size", "target-time",
+    "thumbnail-time", "timeUs",    "durationUs",
+};
+
+static std::vector<const char *> int32Mappings{"loop",
+                                               "time-scale",
+                                               "crypto-mode",
+                                               "crypto-default-iv-size",
+                                               "crypto-encrypted-byte-block",
+                                               "crypto-skip-byte-block",
+                                               "frame-count",
+                                               "max-bitrate",
+                                               "pcm-big-endian",
+                                               "temporal-layer-count",
+                                               "temporal-layer-id",
+                                               "thumbnail-width",
+                                               "thumbnail-height",
+                                               "track-id",
+                                               "valid-samples",
+                                               "color-format",
+                                               "ca-system-id",
+                                               "is-sync-frame",
+                                               "bitrate",
+                                               "max-bitrate",
+                                               "width",
+                                               "height",
+                                               "sar-width",
+                                               "sar-height",
+                                               "display-width",
+                                               "display-height",
+                                               "is-default",
+                                               "tile-width",
+                                               "tile-height",
+                                               "grid-rows",
+                                               "grid-cols",
+                                               "rotation-degrees",
+                                               "channel-count",
+                                               "sample-rate",
+                                               "bits-per-sample",
+                                               "channel-mask",
+                                               "encoder-delay",
+                                               "encoder-padding",
+                                               "is-adts",
+                                               "frame-rate",
+                                               "max-height",
+                                               "max-width",
+                                               "max-input-size",
+                                               "haptic-channel-count",
+                                               "pcm-encoding",
+                                               "aac-profile"};
+
+static const std::vector<std::function<void(AMessage *, FuzzedDataProvider *)>>
+    amessage_setvals = {
+        [](AMessage *msg, FuzzedDataProvider *fdp) -> void {
+          msg->setRect("crop", fdp->ConsumeIntegral<int32_t>(),
+                       fdp->ConsumeIntegral<int32_t>(),
+                       fdp->ConsumeIntegral<int32_t>(),
+                       fdp->ConsumeIntegral<int32_t>());
+        },
+        [](AMessage *msg, FuzzedDataProvider *fdp) -> void {
+          msg->setFloat(floatMappings[fdp->ConsumeIntegralInRange<size_t>(
+                            0, floatMappings.size() - 1)],
+                        fdp->ConsumeFloatingPoint<float>());
+        },
+        [](AMessage *msg, FuzzedDataProvider *fdp) -> void {
+          msg->setInt64(int64Mappings[fdp->ConsumeIntegralInRange<size_t>(
+                            0, int64Mappings.size() - 1)],
+                        fdp->ConsumeIntegral<int64_t>());
+        },
+        [](AMessage *msg, FuzzedDataProvider *fdp) -> void {
+          msg->setInt32(int32Mappings[fdp->ConsumeIntegralInRange<size_t>(
+                            0, int32Mappings.size() - 1)],
+                        fdp->ConsumeIntegral<int32_t>());
+        }};
+} // namespace android
diff --git a/media/libstagefright/tests/fuzzers/SkipCutBufferFuzzer.cpp b/media/libstagefright/tests/fuzzers/SkipCutBufferFuzzer.cpp
new file mode 100644
index 0000000..1f78e6d
--- /dev/null
+++ b/media/libstagefright/tests/fuzzers/SkipCutBufferFuzzer.cpp
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+// Authors: corbin.souffrant@leviathansecurity.com
+//          dylan.katz@leviathansecurity.com
+
+#include <fuzzer/FuzzedDataProvider.h>
+#include <media/stagefright/MediaBuffer.h>
+#include <media/stagefright/SkipCutBuffer.h>
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/AMessage.h>
+
+namespace android {
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+  FuzzedDataProvider fdp = FuzzedDataProvider(data, size);
+  size_t skip = fdp.ConsumeIntegral<size_t>();
+  size_t cut = fdp.ConsumeIntegral<size_t>();
+  size_t num16Channels = fdp.ConsumeIntegral<size_t>();
+  sp<SkipCutBuffer> sBuffer(new SkipCutBuffer(skip, cut, num16Channels));
+
+  while (fdp.remaining_bytes() > 0) {
+    // Cap size to 1024 to limit max amount allocated.
+    size_t buf_size = fdp.ConsumeIntegralInRange<size_t>(0, 1024);
+    size_t range = fdp.ConsumeIntegralInRange<size_t>(0, buf_size);
+    size_t length = fdp.ConsumeIntegralInRange<size_t>(0, buf_size - range);
+
+    switch (fdp.ConsumeIntegralInRange<uint8_t>(0, 4)) {
+    case 0: {
+      sp<ABuffer> a_buffer(new ABuffer(buf_size));
+      sp<AMessage> format(new AMessage);
+      sp<MediaCodecBuffer> s_buffer(new MediaCodecBuffer(format, a_buffer));
+      s_buffer->setRange(range, length);
+      sBuffer->submit(s_buffer);
+      break;
+    }
+    case 1: {
+      std::unique_ptr<MediaBufferBase> m_buffer(new MediaBuffer(buf_size));
+      m_buffer->set_range(range, length);
+      sBuffer->submit(reinterpret_cast<MediaBuffer *>(m_buffer.get()));
+      break;
+    }
+    case 2: {
+      sp<ABuffer> a_buffer(new ABuffer(buf_size));
+      sp<AMessage> format(new AMessage);
+      sp<MediaCodecBuffer> s_buffer(new MediaCodecBuffer(format, a_buffer));
+      a_buffer->setRange(range, length);
+      sBuffer->submit(a_buffer);
+      break;
+    }
+    case 3: {
+      sBuffer->clear();
+      break;
+    }
+    case 4: {
+      sBuffer->size();
+    }
+    }
+  }
+  return 0;
+}
+} // namespace android
diff --git a/media/libstagefright/tests/fuzzers/StagefrightMediaScannerFuzzer.cpp b/media/libstagefright/tests/fuzzers/StagefrightMediaScannerFuzzer.cpp
new file mode 100644
index 0000000..a072b7c
--- /dev/null
+++ b/media/libstagefright/tests/fuzzers/StagefrightMediaScannerFuzzer.cpp
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+// Authors: corbin.souffrant@leviathansecurity.com
+//          dylan.katz@leviathansecurity.com
+
+#include <cutils/ashmem.h>
+#include <fuzzer/FuzzedDataProvider.h>
+#include <media/stagefright/StagefrightMediaScanner.h>
+
+#include <cstdio>
+
+namespace android {
+class FuzzMediaScannerClient : public MediaScannerClient {
+public:
+  virtual status_t scanFile(const char *, long long, long long, bool, bool) {
+    return 0;
+  }
+
+  virtual status_t handleStringTag(const char *, const char *) { return 0; }
+
+  virtual status_t setMimeType(const char *) { return 0; }
+};
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+  FuzzedDataProvider fdp = FuzzedDataProvider(data, size);
+  StagefrightMediaScanner mScanner = StagefrightMediaScanner();
+  // Without this, the fuzzer crashes for some reason.
+  mScanner.setLocale("");
+
+  size_t data_size = fdp.ConsumeIntegralInRange<size_t>(0, size);
+  int fd =
+      ashmem_create_region("stagefrightmediascanner_fuzz_region", data_size);
+  if (fd < 0)
+    return 0;
+
+  uint8_t *sh_data = static_cast<uint8_t *>(
+      mmap(NULL, data_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0));
+  if (sh_data == MAP_FAILED)
+    return 0;
+
+  while (fdp.remaining_bytes() > 8) {
+    switch (fdp.ConsumeIntegralInRange<uint8_t>(0, 1)) {
+    case 0: {
+      std::string path = fdp.ConsumeRandomLengthString(fdp.remaining_bytes());
+      std::string mimeType =
+          fdp.ConsumeRandomLengthString(fdp.remaining_bytes());
+      std::shared_ptr<MediaScannerClient> client(new FuzzMediaScannerClient());
+      mScanner.processFile(path.c_str(), mimeType.c_str(), *client);
+      break;
+    }
+    case 1: {
+      size_t to_copy = fdp.ConsumeIntegralInRange<size_t>(1, data_size);
+      std::vector<uint8_t> rand_buf = fdp.ConsumeBytes<uint8_t>(to_copy);
+
+      // If fdp doesn't have enough bytes left it will just make a shorter
+      // vector.
+      to_copy = std::min(rand_buf.size(), data_size);
+
+      std::copy(sh_data, sh_data + to_copy, rand_buf.begin());
+      mScanner.extractAlbumArt(fd);
+    }
+    }
+  }
+
+  munmap(sh_data, data_size);
+  close(fd);
+  return 0;
+}
+} // namespace android
diff --git a/media/mediaserver/main_mediaserver.cpp b/media/mediaserver/main_mediaserver.cpp
index 316732b..58e2d2a 100644
--- a/media/mediaserver/main_mediaserver.cpp
+++ b/media/mediaserver/main_mediaserver.cpp
@@ -18,7 +18,6 @@
 #define LOG_TAG "mediaserver"
 //#define LOG_NDEBUG 0
 
-#include <aicu/AIcu.h>
 #include <binder/IPCThreadState.h>
 #include <binder/ProcessState.h>
 #include <binder/IServiceManager.h>
@@ -39,7 +38,6 @@
     sp<ProcessState> proc(ProcessState::self());
     sp<IServiceManager> sm(defaultServiceManager());
     ALOGI("ServiceManager: %p", sm.get());
-    AIcu_initializeIcuOrDie();
     MediaPlayerService::instantiate();
     ResourceManagerService::instantiate();
     registerExtensions();
diff --git a/media/ndk/Android.bp b/media/ndk/Android.bp
index d0e0cc7..755d6e6 100644
--- a/media/ndk/Android.bp
+++ b/media/ndk/Android.bp
@@ -54,6 +54,7 @@
 
 cc_library_shared {
     name: "libmediandk",
+    llndk_stubs: "libmediandk.llndk",
 
     srcs: [
         "NdkJavaVMHelper.cpp",
@@ -134,7 +135,7 @@
 }
 
 llndk_library {
-    name: "libmediandk",
+    name: "libmediandk.llndk",
     symbol_file: "libmediandk.map.txt",
     export_include_dirs: [
         "include",
diff --git a/services/audioflinger/Android.bp b/services/audioflinger/Android.bp
index 12f6eba..261af5a 100644
--- a/services/audioflinger/Android.bp
+++ b/services/audioflinger/Android.bp
@@ -35,6 +35,9 @@
     ],
 
     shared_libs: [
+        "audioflinger-aidl-unstable-cpp",
+        "audioclient-types-aidl-unstable-cpp",
+        "libaudioclient_aidl_conversion",
         "libaudiofoundation",
         "libaudiohal",
         "libaudioprocessing",
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index eae9437..e589eb9 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -22,6 +22,13 @@
 // Define AUDIO_ARRAYS_STATIC_CHECK to check all audio arrays are correct
 #define AUDIO_ARRAYS_STATIC_CHECK 1
 
+#define VALUE_OR_FATAL(result) \
+    ({ auto _tmp = (result); \
+       LOG_ALWAYS_FATAL_IF(!_tmp.ok(), \
+                           "Failed result (%d)", \
+                           _tmp.error()); \
+       _tmp.value(); })
+
 #include "Configuration.h"
 #include <dirent.h>
 #include <math.h>
@@ -68,6 +75,7 @@
 #include <powermanager/PowerManager.h>
 
 #include <media/IMediaLogService.h>
+#include <media/AidlConversion.h>
 #include <media/nbaio/Pipe.h>
 #include <media/nbaio/PipeReader.h>
 #include <mediautils/BatteryNotifier.h>
@@ -1774,7 +1782,7 @@
     return BAD_VALUE;
 }
 
-void AudioFlinger::registerClient(const sp<IAudioFlingerClient>& client)
+void AudioFlinger::registerClient(const sp<media::IAudioFlingerClient>& client)
 {
     Mutex::Autolock _l(mLock);
     if (client == 0) {
@@ -1849,13 +1857,18 @@
 
 void AudioFlinger::ioConfigChanged(audio_io_config_event event,
                                    const sp<AudioIoDescriptor>& ioDesc,
-                                   pid_t pid)
-{
+                                   pid_t pid) {
+    media::AudioIoDescriptor descAidl = VALUE_OR_FATAL(
+            legacy2aidl_AudioIoDescriptor_AudioIoDescriptor(ioDesc));
+    media::AudioIoConfigEvent eventAidl = VALUE_OR_FATAL(
+            legacy2aidl_audio_io_config_event_AudioIoConfigEvent(event));
+
     Mutex::Autolock _l(mClientLock);
     size_t size = mNotificationClients.size();
     for (size_t i = 0; i < size; i++) {
         if ((pid == 0) || (mNotificationClients.keyAt(i) == pid)) {
-            mNotificationClients.valueAt(i)->audioFlingerClient()->ioConfigChanged(event, ioDesc);
+            mNotificationClients.valueAt(i)->audioFlingerClient()->ioConfigChanged(eventAidl,
+                                                                                   descAidl);
         }
     }
 }
@@ -1929,7 +1942,7 @@
 // ----------------------------------------------------------------------------
 
 AudioFlinger::NotificationClient::NotificationClient(const sp<AudioFlinger>& audioFlinger,
-                                                     const sp<IAudioFlingerClient>& client,
+                                                     const sp<media::IAudioFlingerClient>& client,
                                                      pid_t pid,
                                                      uid_t uid)
     : mAudioFlinger(audioFlinger), mPid(pid), mUid(uid), mAudioFlingerClient(client)
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index 14a4df7..65d672a 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -33,16 +33,16 @@
 #include <sys/types.h>
 #include <limits.h>
 
+#include <android/media/IAudioFlingerClient.h>
 #include <android/media/IAudioTrackCallback.h>
 #include <android/os/BnExternalVibrationController.h>
-#include <android-base/macros.h>
 
+#include <android-base/macros.h>
 #include <cutils/atomic.h>
 #include <cutils/compiler.h>
-#include <cutils/properties.h>
 
+#include <cutils/properties.h>
 #include <media/IAudioFlinger.h>
-#include <media/IAudioFlingerClient.h>
 #include <media/IAudioTrack.h>
 #include <media/AudioSystem.h>
 #include <media/AudioTrack.h>
@@ -177,7 +177,7 @@
     virtual     status_t    setParameters(audio_io_handle_t ioHandle, const String8& keyValuePairs);
     virtual     String8     getParameters(audio_io_handle_t ioHandle, const String8& keys) const;
 
-    virtual     void        registerClient(const sp<IAudioFlingerClient>& client);
+    virtual     void        registerClient(const sp<media::IAudioFlingerClient>& client);
 
     virtual     size_t      getInputBufferSize(uint32_t sampleRate, audio_format_t format,
                                                audio_channel_mask_t channelMask) const;
@@ -490,12 +490,12 @@
     class NotificationClient : public IBinder::DeathRecipient {
     public:
                             NotificationClient(const sp<AudioFlinger>& audioFlinger,
-                                                const sp<IAudioFlingerClient>& client,
+                                                const sp<media::IAudioFlingerClient>& client,
                                                 pid_t pid,
                                                 uid_t uid);
         virtual             ~NotificationClient();
 
-                sp<IAudioFlingerClient> audioFlingerClient() const { return mAudioFlingerClient; }
+                sp<media::IAudioFlingerClient> audioFlingerClient() const { return mAudioFlingerClient; }
                 pid_t getPid() const { return mPid; }
                 uid_t getUid() const { return mUid; }
 
@@ -508,7 +508,7 @@
         const sp<AudioFlinger>  mAudioFlinger;
         const pid_t             mPid;
         const uid_t             mUid;
-        const sp<IAudioFlingerClient> mAudioFlingerClient;
+        const sp<media::IAudioFlingerClient> mAudioFlingerClient;
     };
 
     // --- MediaLogNotifier ---
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index 46969ef..c1c3c44 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -1914,9 +1914,8 @@
                                        : AUDIO_DEVICE_NONE));
     }
 
-    // ++ operator does not compile
-    for (audio_stream_type_t stream = AUDIO_STREAM_MIN; stream < AUDIO_STREAM_FOR_POLICY_CNT;
-            stream = (audio_stream_type_t) (stream + 1)) {
+    for (int i = AUDIO_STREAM_MIN; i < AUDIO_STREAM_FOR_POLICY_CNT; ++i) {
+        const audio_stream_type_t stream{static_cast<audio_stream_type_t>(i)};
         mStreamTypes[stream].volume = 0.0f;
         mStreamTypes[stream].mute = mAudioFlinger->streamMute_l(stream);
     }
diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h
index 39d1140..1d9223e 100644
--- a/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h
+++ b/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h
@@ -498,11 +498,6 @@
      */
     bool isA2dpOffloadedOnPrimary() const;
 
-    /**
-     * returns true if A2DP is supported (either via hardware offload or software encoding)
-     */
-    bool isA2dpSupported() const;
-
     sp<SwAudioOutputDescriptor> getOutputFromId(audio_port_handle_t id) const;
 
     sp<SwAudioOutputDescriptor> getPrimaryOutput() const;
diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioPolicyConfig.h b/services/audiopolicy/common/managerdefinitions/include/AudioPolicyConfig.h
index 395bc70..cf1f64c 100644
--- a/services/audiopolicy/common/managerdefinitions/include/AudioPolicyConfig.h
+++ b/services/audiopolicy/common/managerdefinitions/include/AudioPolicyConfig.h
@@ -33,6 +33,15 @@
 
 namespace android {
 
+// This class gathers together various bits of AudioPolicyManager
+// configuration, which are usually filled out as a result of parsing
+// the audio_policy_configuration.xml file.
+//
+// Note that AudioPolicyConfig doesn't own some of the data,
+// it simply proxies access to the fields of AudioPolicyManager
+// class. Be careful about the fields that are references,
+// e.g. 'mOutputDevices'. This also means that it's impossible
+// to implement "deep copying" of this class without re-designing it.
 class AudioPolicyConfig
 {
 public:
@@ -40,14 +49,24 @@
                       DeviceVector &outputDevices,
                       DeviceVector &inputDevices,
                       sp<DeviceDescriptor> &defaultOutputDevice)
-        : mEngineLibraryNameSuffix(kDefaultEngineLibraryNameSuffix),
-          mHwModules(hwModules),
+        : mHwModules(hwModules),
           mOutputDevices(outputDevices),
           mInputDevices(inputDevices),
-          mDefaultOutputDevice(defaultOutputDevice),
-          mIsSpeakerDrcEnabled(false),
-          mIsCallScreenModeSupported(false)
-    {}
+          mDefaultOutputDevice(defaultOutputDevice) {
+        clear();
+    }
+
+    void clear() {
+        mSource = {};
+        mEngineLibraryNameSuffix = kDefaultEngineLibraryNameSuffix;
+        mHwModules.clear();
+        mOutputDevices.clear();
+        mInputDevices.clear();
+        mDefaultOutputDevice.clear();
+        mIsSpeakerDrcEnabled = false;
+        mIsCallScreenModeSupported = false;
+        mSurroundFormats.clear();
+    }
 
     const std::string& getSource() const {
         return mSource;
diff --git a/services/audiopolicy/common/managerdefinitions/include/DeviceDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/DeviceDescriptor.h
index c51d6a9..ca29591 100644
--- a/services/audiopolicy/common/managerdefinitions/include/DeviceDescriptor.h
+++ b/services/audiopolicy/common/managerdefinitions/include/DeviceDescriptor.h
@@ -65,6 +65,9 @@
 
     bool supportsFormat(audio_format_t format);
 
+    void setDynamic() { mIsDynamic = true; }
+    bool isDynamic() const { return mIsDynamic; }
+
     // PolicyAudioPortConfig
     virtual sp<PolicyAudioPort> getPolicyAudioPort() const {
         return static_cast<PolicyAudioPort*>(const_cast<DeviceDescriptor*>(this));
@@ -97,6 +100,8 @@
     std::string mTagName; // Unique human readable identifier for a device port found in conf file.
     FormatVector        mEncodedFormats;
     audio_format_t      mCurrentEncodedFormat;
+    bool                mIsDynamic = false;
+    const std::string   mDeclaredAddress; // Original device address
 };
 
 class DeviceVector : public SortedVector<sp<DeviceDescriptor> >
diff --git a/services/audiopolicy/common/managerdefinitions/include/HwModule.h b/services/audiopolicy/common/managerdefinitions/include/HwModule.h
index 23f0c9a..b5b10f3 100644
--- a/services/audiopolicy/common/managerdefinitions/include/HwModule.h
+++ b/services/audiopolicy/common/managerdefinitions/include/HwModule.h
@@ -131,8 +131,17 @@
 public:
     sp<HwModule> getModuleFromName(const char *name) const;
 
+    /**
+     * @brief getModuleForDeviceType try to get a device from type / format on all modules
+     * @param device type to consider
+     * @param encodedFormat to consider
+     * @param[out] tagName if not null, if a matching device is found, will return the tagName
+     * of original device from XML file so that audio routes matchin rules work.
+     * @return valid module if considered device found, nullptr otherwise.
+     */
     sp<HwModule> getModuleForDeviceType(audio_devices_t device,
-                                        audio_format_t encodedFormat) const;
+                                        audio_format_t encodedFormat,
+                                        std::string *tagName = nullptr) const;
 
     sp<HwModule> getModuleForDevice(const sp<DeviceDescriptor> &device,
                                     audio_format_t encodedFormat) const;
diff --git a/services/audiopolicy/common/managerdefinitions/include/IOProfile.h b/services/audiopolicy/common/managerdefinitions/include/IOProfile.h
index 5f551d5..11d3a99 100644
--- a/services/audiopolicy/common/managerdefinitions/include/IOProfile.h
+++ b/services/audiopolicy/common/managerdefinitions/include/IOProfile.h
@@ -112,6 +112,19 @@
     }
 
     /**
+     * @brief getTag
+     * @param deviceTypes to be considered
+     * @return tagName of first matching device for the considered types, empty string otherwise.
+     */
+    std::string getTag(const DeviceTypeSet& deviceTypes) const
+    {
+        if (supportsDeviceTypes(deviceTypes)) {
+            return mSupportedDevices.getDevicesFromTypes(deviceTypes).itemAt(0)->getTagName();
+        }
+        return {};
+    }
+
+    /**
      * @brief supportsDevice
      * @param device to be checked against
      *        forceCheckOnAddress if true, check on type and address whatever the type, otherwise
@@ -150,6 +163,12 @@
     }
     void removeSupportedDevice(const sp<DeviceDescriptor> &device)
     {
+        ssize_t ret = mSupportedDevices.indexOf(device);
+        if (ret >= 0 && !mSupportedDevices.itemAt(ret)->isDynamic()) {
+            // devices equality checks only type, address, name and format
+            // Prevents from removing non dynamically added devices
+            return;
+        }
         mSupportedDevices.remove(device);
     }
     void setSupportedDevices(const DeviceVector &devices)
diff --git a/services/audiopolicy/common/managerdefinitions/include/PolicyAudioPort.h b/services/audiopolicy/common/managerdefinitions/include/PolicyAudioPort.h
index d2f6297..e6eef24 100644
--- a/services/audiopolicy/common/managerdefinitions/include/PolicyAudioPort.h
+++ b/services/audiopolicy/common/managerdefinitions/include/PolicyAudioPort.h
@@ -42,6 +42,11 @@
 
     virtual const std::string getTagName() const = 0;
 
+    bool equals(const sp<PolicyAudioPort> &right) const
+    {
+        return getTagName() == right->getTagName();
+    }
+
     virtual sp<AudioPort> asAudioPort() const = 0;
 
     virtual void setFlags(uint32_t flags)
diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp
index a2e2eec..25f7c27 100644
--- a/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp
@@ -764,11 +764,6 @@
     return false;
 }
 
-bool SwAudioOutputCollection::isA2dpSupported() const
-{
-    return (isA2dpOffloadedOnPrimary() || (getA2dpOutput() != 0));
-}
-
 sp<SwAudioOutputDescriptor> SwAudioOutputCollection::getPrimaryOutput() const
 {
     for (size_t i = 0; i < size(); i++) {
diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioRoute.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioRoute.cpp
index 2a18f19..c8e4e76 100644
--- a/services/audiopolicy/common/managerdefinitions/src/AudioRoute.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/AudioRoute.cpp
@@ -39,12 +39,12 @@
 bool AudioRoute::supportsPatch(const sp<PolicyAudioPort> &srcPort,
                                const sp<PolicyAudioPort> &dstPort) const
 {
-    if (mSink == 0 || dstPort == 0 || dstPort != mSink) {
+    if (mSink == 0 || dstPort == 0 || !dstPort->equals(mSink)) {
         return false;
     }
     ALOGV("%s: sinks %s matching", __FUNCTION__, mSink->getTagName().c_str());
     for (const auto &sourcePort : mSources) {
-        if (sourcePort == srcPort) {
+        if (sourcePort->equals(srcPort)) {
             ALOGV("%s: sources %s matching", __FUNCTION__, sourcePort->getTagName().c_str());
             return true;
         }
diff --git a/services/audiopolicy/common/managerdefinitions/src/DeviceDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/DeviceDescriptor.cpp
index b450e82..6ff1a98 100644
--- a/services/audiopolicy/common/managerdefinitions/src/DeviceDescriptor.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/DeviceDescriptor.cpp
@@ -52,7 +52,8 @@
 DeviceDescriptor::DeviceDescriptor(const AudioDeviceTypeAddr &deviceTypeAddr,
                                    const std::string &tagName,
                                    const FormatVector &encodedFormats) :
-        DeviceDescriptorBase(deviceTypeAddr), mTagName(tagName), mEncodedFormats(encodedFormats)
+        DeviceDescriptorBase(deviceTypeAddr), mTagName(tagName), mEncodedFormats(encodedFormats),
+        mDeclaredAddress(deviceTypeAddr.getAddress())
 {
     mCurrentEncodedFormat = AUDIO_FORMAT_DEFAULT;
     /* If framework runs against a pre 5.0 Audio HAL, encoded formats are absent from the config.
@@ -75,6 +76,10 @@
 void DeviceDescriptor::detach() {
     mId = AUDIO_PORT_HANDLE_NONE;
     PolicyAudioPort::detach();
+    // The device address may have been overwritten on device connection
+    setAddress(mDeclaredAddress);
+    // Device Port does not have a name unless provided by setDeviceConnectionState
+    setName("");
 }
 
 template<typename T>
diff --git a/services/audiopolicy/common/managerdefinitions/src/HwModule.cpp b/services/audiopolicy/common/managerdefinitions/src/HwModule.cpp
index d31e443..2967014 100644
--- a/services/audiopolicy/common/managerdefinitions/src/HwModule.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/HwModule.cpp
@@ -271,8 +271,9 @@
     return nullptr;
 }
 
-sp <HwModule> HwModuleCollection::getModuleForDeviceType(audio_devices_t type,
-                                                         audio_format_t encodedFormat) const
+sp<HwModule> HwModuleCollection::getModuleForDeviceType(audio_devices_t type,
+                                                        audio_format_t encodedFormat,
+                                                        std::string *tagName) const
 {
     for (const auto& module : *this) {
         const auto& profiles = audio_is_output_device(type) ?
@@ -284,9 +285,15 @@
                     sp <DeviceDescriptor> deviceDesc =
                             declaredDevices.getDevice(type, String8(), encodedFormat);
                     if (deviceDesc) {
+                        if (tagName != nullptr) {
+                            *tagName = deviceDesc->getTagName();
+                        }
                         return module;
                     }
                 } else {
+                    if (tagName != nullptr) {
+                        *tagName = profile->getTag({type});
+                    }
                     return module;
                 }
             }
@@ -325,15 +332,32 @@
     }
 
     for (const auto& hwModule : *this) {
+        if (!allowToCreate) {
+            auto dynamicDevices = hwModule->getDynamicDevices();
+            auto dynamicDevice = dynamicDevices.getDevice(deviceType, devAddress, encodedFormat);
+            if (dynamicDevice) {
+                return dynamicDevice;
+            }
+        }
         DeviceVector moduleDevices = hwModule->getAllDevices();
         auto moduleDevice = moduleDevices.getDevice(deviceType, devAddress, encodedFormat);
+
+        // Prevent overwritting moduleDevice address if connected device does not have the same
+        // address (since getDevice with empty address ignores match on address), use dynamic device
+        if (moduleDevice && allowToCreate &&
+                (!moduleDevice->address().empty() &&
+                 (moduleDevice->address().compare(devAddress.c_str()) != 0))) {
+            break;
+        }
         if (moduleDevice) {
             if (encodedFormat != AUDIO_FORMAT_DEFAULT) {
                 moduleDevice->setEncodedFormat(encodedFormat);
             }
             if (allowToCreate) {
                 moduleDevice->attach(hwModule);
+                // Name may be overwritten, restored on detach.
                 moduleDevice->setAddress(devAddress.string());
+                // Name may be overwritten, restored on detach.
                 moduleDevice->setName(name);
             }
             return moduleDevice;
@@ -352,18 +376,19 @@
                                                       const char *name,
                                                       const audio_format_t encodedFormat) const
 {
-    sp<HwModule> hwModule = getModuleForDeviceType(type, encodedFormat);
+    std::string tagName = {};
+    sp<HwModule> hwModule = getModuleForDeviceType(type, encodedFormat, &tagName);
     if (hwModule == 0) {
         ALOGE("%s: could not find HW module for device %04x address %s", __FUNCTION__, type,
               address);
         return nullptr;
     }
 
-    sp<DeviceDescriptor> device = new DeviceDescriptor(type, name, address);
+    sp<DeviceDescriptor> device = new DeviceDescriptor(type, tagName, address);
     device->setName(name);
     device->setEncodedFormat(encodedFormat);
-
-  // Add the device to the list of dynamic devices
+    device->setDynamic();
+    // Add the device to the list of dynamic devices
     hwModule->addDynamicDevice(device);
     // Reciprocally attach the device to the module
     device->attach(hwModule);
@@ -375,7 +400,7 @@
     for (const auto &profile : profiles) {
         // Add the device as supported to all profile supporting "weakly" or not the device
         // according to its type
-        if (profile->supportsDevice(device, false /*matchAdress*/)) {
+        if (profile->supportsDevice(device, false /*matchAddress*/)) {
 
             // @todo quid of audio profile? import the profile from device of the same type?
             const auto &isoTypeDeviceForProfile =
@@ -406,10 +431,9 @@
 
         device->detach();
         // Only remove from dynamic list, not from declared list!!!
-        if (!hwModule->getDynamicDevices().contains(device)) {
+        if (!hwModule->removeDynamicDevice(device)) {
             return;
         }
-        hwModule->removeDynamicDevice(device);
         ALOGV("%s: removed dynamic device %s from module %s", __FUNCTION__,
               device->toString().c_str(), hwModule->getName());
 
diff --git a/services/audiopolicy/common/managerdefinitions/src/IOProfile.cpp b/services/audiopolicy/common/managerdefinitions/src/IOProfile.cpp
index bf1a0f7..ae92b40 100644
--- a/services/audiopolicy/common/managerdefinitions/src/IOProfile.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/IOProfile.cpp
@@ -17,7 +17,7 @@
 #define LOG_TAG "APM::IOProfile"
 //#define LOG_NDEBUG 0
 
-#include <system/audio-base.h>
+#include <system/audio.h>
 #include "IOProfile.h"
 #include "HwModule.h"
 #include "TypeConverter.h"
@@ -112,12 +112,11 @@
     dst->append(portStr.c_str());
 
     dst->appendFormat("    - flags: 0x%04x", getFlags());
-    std::string flagsLiteral;
-    if (getRole() == AUDIO_PORT_ROLE_SINK) {
-        InputFlagConverter::maskToString(getFlags(), flagsLiteral);
-    } else if (getRole() == AUDIO_PORT_ROLE_SOURCE) {
-        OutputFlagConverter::maskToString(getFlags(), flagsLiteral);
-    }
+    std::string flagsLiteral =
+            getRole() == AUDIO_PORT_ROLE_SINK ?
+            toString(static_cast<audio_input_flags_t>(getFlags())) :
+            getRole() == AUDIO_PORT_ROLE_SOURCE ?
+            toString(static_cast<audio_output_flags_t>(getFlags())) : "";
     if (!flagsLiteral.empty()) {
         dst->appendFormat(" (%s)", flagsLiteral.c_str());
     }
diff --git a/services/audiopolicy/common/managerdefinitions/src/Serializer.cpp b/services/audiopolicy/common/managerdefinitions/src/Serializer.cpp
index 889f031..0cc3a68 100644
--- a/services/audiopolicy/common/managerdefinitions/src/Serializer.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/Serializer.cpp
@@ -254,9 +254,8 @@
 constexpr void (*xmlDeleter)(T* t);
 template <>
 constexpr auto xmlDeleter<xmlDoc> = xmlFreeDoc;
-// http://b/111067277 - Add back constexpr when we switch to C++17.
 template <>
-auto xmlDeleter<xmlChar> = [](xmlChar *s) { xmlFree(s); };
+constexpr auto xmlDeleter<xmlChar> = [](xmlChar *s) { xmlFree(s); };
 
 /** @return a unique_ptr with the correct deleter for the libxml2 object. */
 template <class T>
@@ -337,7 +336,7 @@
 
     std::string mode = getXmlAttribute(cur, Attributes::mode);
     if (!mode.empty()) {
-        gain->setMode(static_cast<audio_gain_mode_t>(GainModeConverter::maskFromString(mode)));
+        gain->setMode(GainModeConverter::maskFromString(mode, " "));
     }
 
     std::string channelsLiteral = getXmlAttribute(cur, Attributes::channelMask);
@@ -501,7 +500,7 @@
                 AUDIO_PORT_ROLE_SOURCE : AUDIO_PORT_ROLE_SINK;
 
     audio_devices_t type = AUDIO_DEVICE_NONE;
-    if (!deviceFromString(typeName, type) ||
+    if (!DeviceConverter::fromString(typeName, type) ||
             (!audio_is_input_device(type) && portRole == AUDIO_PORT_ROLE_SOURCE) ||
             (!audio_is_output_devices(type) && portRole == AUDIO_PORT_ROLE_SINK)) {
         ALOGW("%s: bad type %08x", __func__, type);
@@ -804,7 +803,9 @@
 status_t deserializeAudioPolicyFile(const char *fileName, AudioPolicyConfig *config)
 {
     PolicySerializer serializer;
-    return serializer.deserialize(fileName, config);
+    status_t status = serializer.deserialize(fileName, config);
+    if (status != OK) config->clear();
+    return status;
 }
 
 } // namespace android
diff --git a/services/audiopolicy/engine/common/src/EngineBase.cpp b/services/audiopolicy/engine/common/src/EngineBase.cpp
index 1875c10..8c7fb97 100644
--- a/services/audiopolicy/engine/common/src/EngineBase.cpp
+++ b/services/audiopolicy/engine/common/src/EngineBase.cpp
@@ -19,7 +19,6 @@
 
 #include "EngineBase.h"
 #include "EngineDefaultConfig.h"
-#include "../include/EngineBase.h"
 #include <TypeConverter.h>
 
 namespace android {
diff --git a/services/audiopolicy/engine/common/src/LastRemovableMediaDevices.cpp b/services/audiopolicy/engine/common/src/LastRemovableMediaDevices.cpp
index 87b6aaf..96cc140 100644
--- a/services/audiopolicy/engine/common/src/LastRemovableMediaDevices.cpp
+++ b/services/audiopolicy/engine/common/src/LastRemovableMediaDevices.cpp
@@ -69,6 +69,11 @@
     case AUDIO_DEVICE_OUT_BLUETOOTH_A2DP:
     case AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES:
     case AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER:
+    // TODO (b/122931261): remove when preferred device for strategy media will be used instead of
+    //  AUDIO_POLICY_FORCE_NO_BT_A2DP.
+    case AUDIO_DEVICE_OUT_HEARING_AID:
+    case AUDIO_DEVICE_OUT_BLE_HEADSET:
+    case AUDIO_DEVICE_OUT_BLE_SPEAKER:
         return GROUP_BT_A2DP;
     default:
         return GROUP_NONE;
diff --git a/services/audiopolicy/engine/config/TEST_MAPPING b/services/audiopolicy/engine/config/TEST_MAPPING
new file mode 100644
index 0000000..06ce111
--- /dev/null
+++ b/services/audiopolicy/engine/config/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+  "presubmit": [
+    {
+       "name": "audiopolicy_engineconfig_tests"
+    }
+  ]
+}
diff --git a/services/audiopolicy/engine/config/include/EngineConfig.h b/services/audiopolicy/engine/config/include/EngineConfig.h
index 5d22c24..c565926 100644
--- a/services/audiopolicy/engine/config/include/EngineConfig.h
+++ b/services/audiopolicy/engine/config/include/EngineConfig.h
@@ -111,6 +111,8 @@
  */
 ParsingResult parse(const char* path = DEFAULT_PATH);
 android::status_t parseLegacyVolumes(VolumeGroups &volumeGroups);
+// Exposed for testing.
+android::status_t parseLegacyVolumeFile(const char* path, VolumeGroups &volumeGroups);
 
 } // namespace engineConfig
 } // namespace android
diff --git a/services/audiopolicy/engine/config/src/EngineConfig.cpp b/services/audiopolicy/engine/config/src/EngineConfig.cpp
index daf6418..7cfef5b 100644
--- a/services/audiopolicy/engine/config/src/EngineConfig.cpp
+++ b/services/audiopolicy/engine/config/src/EngineConfig.cpp
@@ -589,6 +589,7 @@
             }
         }
     }
+    VolumeGroups tempVolumeGroups = volumeGroups;
     for (const auto &volumeMapIter : legacyVolumeMap) {
         // In order to let AudioService setting the min and max (compatibility), set Min and Max
         // to -1 except for private streams
@@ -599,8 +600,10 @@
         }
         int indexMin = streamType >= AUDIO_STREAM_PUBLIC_CNT ? 0 : -1;
         int indexMax = streamType >= AUDIO_STREAM_PUBLIC_CNT ? 100 : -1;
-        volumeGroups.push_back({ volumeMapIter.first, indexMin, indexMax, volumeMapIter.second });
+        tempVolumeGroups.push_back(
+                { volumeMapIter.first, indexMin, indexMax, volumeMapIter.second });
     }
+    std::swap(tempVolumeGroups, volumeGroups);
     return NO_ERROR;
 }
 
@@ -695,35 +698,14 @@
     return deserializeLegacyVolumeCollection(doc, cur, volumeGroups, nbSkippedElements);
 }
 
-static const int gApmXmlConfigFilePathMaxLength = 128;
-
-static constexpr const char *apmXmlConfigFileName = "audio_policy_configuration.xml";
-static constexpr const char *apmA2dpOffloadDisabledXmlConfigFileName =
-        "audio_policy_configuration_a2dp_offload_disabled.xml";
-
 android::status_t parseLegacyVolumes(VolumeGroups &volumeGroups) {
-    char audioPolicyXmlConfigFile[gApmXmlConfigFilePathMaxLength];
-    std::vector<const char *> fileNames;
-    status_t ret;
-
-    if (property_get_bool("ro.bluetooth.a2dp_offload.supported", false) &&
-            property_get_bool("persist.bluetooth.a2dp_offload.disabled", false)) {
-        // A2DP offload supported but disabled: try to use special XML file
-        fileNames.push_back(apmA2dpOffloadDisabledXmlConfigFileName);
+    if (std::string audioPolicyXmlConfigFile = audio_get_audio_policy_config_file();
+            !audioPolicyXmlConfigFile.empty()) {
+        return parseLegacyVolumeFile(audioPolicyXmlConfigFile.c_str(), volumeGroups);
+    } else {
+        ALOGE("No readable audio policy config file found");
+        return BAD_VALUE;
     }
-    fileNames.push_back(apmXmlConfigFileName);
-
-    for (const char* fileName : fileNames) {
-        for (const auto& path : audio_get_configuration_paths()) {
-            snprintf(audioPolicyXmlConfigFile, sizeof(audioPolicyXmlConfigFile),
-                     "%s/%s", path.c_str(), fileName);
-            ret = parseLegacyVolumeFile(audioPolicyXmlConfigFile, volumeGroups);
-            if (ret == NO_ERROR) {
-                return ret;
-            }
-        }
-    }
-    return BAD_VALUE;
 }
 
 } // namespace engineConfig
diff --git a/services/audiopolicy/engine/config/tests/Android.bp b/services/audiopolicy/engine/config/tests/Android.bp
new file mode 100644
index 0000000..6b0774f
--- /dev/null
+++ b/services/audiopolicy/engine/config/tests/Android.bp
@@ -0,0 +1,25 @@
+cc_test {
+    name: "audiopolicy_engineconfig_tests",
+
+    shared_libs: [
+        "libbase",
+        "liblog",
+        "libmedia_helper",
+        "libutils",
+        "libxml2",
+    ],
+    static_libs: [
+        "libaudiopolicyengine_config",
+    ],
+
+    srcs: ["engineconfig_tests.cpp"],
+
+    data: [":audiopolicy_engineconfig_files"],
+
+    cflags: [
+        "-Werror",
+        "-Wall",
+    ],
+
+    test_suites: ["device-tests"],
+}
diff --git a/services/audiopolicy/engine/config/tests/engineconfig_tests.cpp b/services/audiopolicy/engine/config/tests/engineconfig_tests.cpp
new file mode 100644
index 0000000..f61e02f
--- /dev/null
+++ b/services/audiopolicy/engine/config/tests/engineconfig_tests.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.
+ */
+
+#include <gtest/gtest.h>
+
+#define LOG_TAG "APM_Test"
+#include <android-base/file.h>
+#include <log/log.h>
+
+#include "EngineConfig.h"
+
+using namespace android;
+
+TEST(EngineConfigTestInit, LegacyVolumeGroupsLoadingIsTransactional) {
+    engineConfig::VolumeGroups groups;
+    ASSERT_TRUE(groups.empty());
+    status_t status = engineConfig::parseLegacyVolumeFile(
+            (base::GetExecutableDirectory() + "/test_invalid_apm_volume_tables.xml").c_str(),
+            groups);
+    ASSERT_NE(NO_ERROR, status);
+    EXPECT_TRUE(groups.empty());
+    status = engineConfig::parseLegacyVolumeFile(
+            (base::GetExecutableDirectory() + "/test_apm_volume_tables.xml").c_str(),
+            groups);
+    ASSERT_EQ(NO_ERROR, status);
+    EXPECT_FALSE(groups.empty());
+}
diff --git a/services/audiopolicy/engine/config/tests/resources/Android.bp b/services/audiopolicy/engine/config/tests/resources/Android.bp
new file mode 100644
index 0000000..0aee0e9
--- /dev/null
+++ b/services/audiopolicy/engine/config/tests/resources/Android.bp
@@ -0,0 +1,7 @@
+filegroup {
+    name: "audiopolicy_engineconfig_files",
+    srcs: [
+        "test_apm_volume_tables.xml",
+        "test_invalid_apm_volume_tables.xml",
+    ],
+}
diff --git a/services/audiopolicy/engine/config/tests/resources/test_apm_volume_tables.xml b/services/audiopolicy/engine/config/tests/resources/test_apm_volume_tables.xml
new file mode 100644
index 0000000..16126b6
--- /dev/null
+++ b/services/audiopolicy/engine/config/tests/resources/test_apm_volume_tables.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<!-- 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.
+-->
+
+<audioPolicyConfiguration version="1.0" xmlns:xi="http://www.w3.org/2001/XInclude">
+    <globalConfiguration speaker_drc_enabled="true"/>
+    <volumes>
+        <volume stream="AUDIO_STREAM_VOICE_CALL" deviceCategory="DEVICE_CATEGORY_HEADSET">
+            <point>0,-4200</point>
+            <point>33,-2800</point>
+            <point>66,-1400</point>
+            <point>100,0</point>
+        </volume>
+        <volume stream="AUDIO_STREAM_VOICE_CALL" deviceCategory="DEVICE_CATEGORY_SPEAKER">
+            <point>0,-2400</point>
+            <point>33,-1600</point>
+            <point>66,-800</point>
+            <point>100,0</point>
+        </volume>
+        <volume stream="AUDIO_STREAM_VOICE_CALL" deviceCategory="DEVICE_CATEGORY_HEARING_AID"
+                ref="FULL_SCALE_VOLUME_CURVE"/>
+    </volumes>
+    <volumes>
+        <reference name="FULL_SCALE_VOLUME_CURVE">
+            <!-- Full Scale reference Volume Curve -->
+            <point>0,0</point>
+            <point>100,0</point>
+        </reference>
+    </volumes>
+</audioPolicyConfiguration>
diff --git a/services/audiopolicy/engine/config/tests/resources/test_invalid_apm_volume_tables.xml b/services/audiopolicy/engine/config/tests/resources/test_invalid_apm_volume_tables.xml
new file mode 100644
index 0000000..3ec5d10
--- /dev/null
+++ b/services/audiopolicy/engine/config/tests/resources/test_invalid_apm_volume_tables.xml
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<!-- 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.
+-->
+
+<!-- This file uses a non-existent audio stream name. -->
+<audioPolicyConfiguration version="1.0" xmlns:xi="http://www.w3.org/2001/XInclude">
+    <globalConfiguration speaker_drc_enabled="true"/>
+    <volumes>
+        <volume stream="AUDIO_STREAM_VOICE_CALL" deviceCategory="DEVICE_CATEGORY_HEADSET">
+            <point>0,-4200</point>
+            <point>33,-2800</point>
+            <point>66,-1400</point>
+            <point>100,0</point>
+        </volume>
+        <volume stream="AUDIO_STREAM_NON_EXISTING" deviceCategory="DEVICE_CATEGORY_SPEAKER">
+            <point>0,-2400</point>
+            <point>33,-1600</point>
+            <point>66,-800</point>
+            <point>100,0</point>
+        </volume>
+        <volume stream="AUDIO_STREAM_RING" deviceCategory="DEVICE_CATEGORY_HEADSET"
+                ref="FULL_SCALE_VOLUME_CURVE"/>
+        <volume stream="AUDIO_STREAM_MUSIC" deviceCategory="DEVICE_CATEGORY_HEADSET"
+                ref="FULL_SCALE_VOLUME_CURVE"/>
+        <volume stream="AUDIO_STREAM_ALARM" deviceCategory="DEVICE_CATEGORY_SPEAKER">
+            <point>0,-2970</point>
+            <point>33,-2010</point>
+            <point>66,-1020</point>
+            <point>100,0</point>
+        </volume>
+        <volume stream="AUDIO_STREAM_NOTIFICATION" deviceCategory="DEVICE_CATEGORY_HEADSET"
+                ref="FULL_SCALE_VOLUME_CURVE"/>
+        <volume stream="AUDIO_STREAM_BLUETOOTH_SCO" deviceCategory="DEVICE_CATEGORY_EXT_MEDIA"
+                ref="FULL_SCALE_VOLUME_CURVE"/>
+        <volume stream="AUDIO_STREAM_ENFORCED_AUDIBLE" deviceCategory="DEVICE_CATEGORY_HEARING_AID"
+                ref="FULL_SCALE_VOLUME_CURVE"/>
+        <volume stream="AUDIO_STREAM_DTMF" deviceCategory="DEVICE_CATEGORY_SPEAKER"
+                ref="FULL_SCALE_VOLUME_CURVE"/>
+    </volumes>
+    <volumes>
+        <reference name="FULL_SCALE_VOLUME_CURVE">
+            <!-- Full Scale reference Volume Curve -->
+            <point>0,0</point>
+            <point>100,0</point>
+        </reference>
+    </volumes>
+</audioPolicyConfiguration>
diff --git a/services/audiopolicy/enginedefault/src/Engine.cpp b/services/audiopolicy/enginedefault/src/Engine.cpp
index ec50b14..eccde7b 100644
--- a/services/audiopolicy/enginedefault/src/Engine.cpp
+++ b/services/audiopolicy/enginedefault/src/Engine.cpp
@@ -241,10 +241,15 @@
         default:    // FORCE_NONE
             devices = availableOutputDevices.getDevicesFromType(AUDIO_DEVICE_OUT_HEARING_AID);
             if (!devices.isEmpty()) break;
+
+            // TODO (b/161358428): remove when preferred device
+            //  for strategy phone will be used instead of AUDIO_POLICY_FORCE_FOR_COMMUNICATION
+            devices = availableOutputDevices.getDevicesFromType(AUDIO_DEVICE_OUT_BLE_HEADSET);
+            if (!devices.isEmpty()) break;
+
             // when not in a phone call, phone strategy should route STREAM_VOICE_CALL to A2DP
             if (!isInCall() &&
-                    (getForceUse(AUDIO_POLICY_FORCE_FOR_MEDIA) != AUDIO_POLICY_FORCE_NO_BT_A2DP) &&
-                     outputs.isA2dpSupported()) {
+                    (getForceUse(AUDIO_POLICY_FORCE_FOR_MEDIA) != AUDIO_POLICY_FORCE_NO_BT_A2DP)) {
                 devices = availableOutputDevices.getFirstDevicesFromTypes({
                         AUDIO_DEVICE_OUT_BLUETOOTH_A2DP,
                         AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES});
@@ -267,12 +272,16 @@
         case AUDIO_POLICY_FORCE_SPEAKER:
             // when not in a phone call, phone strategy should route STREAM_VOICE_CALL to
             // A2DP speaker when forcing to speaker output
-            if (!isInCall() &&
-                    (getForceUse(AUDIO_POLICY_FORCE_FOR_MEDIA) != AUDIO_POLICY_FORCE_NO_BT_A2DP) &&
-                     outputs.isA2dpSupported()) {
+            if (!isInCall()) {
                 devices = availableOutputDevices.getDevicesFromType(
-                        AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER);
+                        AUDIO_DEVICE_OUT_BLE_SPEAKER);
                 if (!devices.isEmpty()) break;
+
+                if ((getForceUse(AUDIO_POLICY_FORCE_FOR_MEDIA) != AUDIO_POLICY_FORCE_NO_BT_A2DP)) {
+                    devices = availableOutputDevices.getDevicesFromType(
+                            AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER);
+                    if (!devices.isEmpty()) break;
+                }
             }
             if (!isInCall()) {
                 devices = availableOutputDevices.getFirstDevicesFromTypes({
@@ -386,18 +395,13 @@
                     STRATEGY_PHONE, availableOutputDevices, availableInputDevices, outputs);
             break;
         }
-        // FIXME: Find a better solution to prevent routing to BT hearing aid(b/122931261).
-        if ((devices2.isEmpty()) &&
-                (getForceUse(AUDIO_POLICY_FORCE_FOR_MEDIA) != AUDIO_POLICY_FORCE_NO_BT_A2DP)) {
-            devices2 = availableOutputDevices.getDevicesFromType(AUDIO_DEVICE_OUT_HEARING_AID);
-        }
+
         if ((devices2.isEmpty()) &&
             (getForceUse(AUDIO_POLICY_FORCE_FOR_MEDIA) == AUDIO_POLICY_FORCE_SPEAKER)) {
             devices2 = availableOutputDevices.getDevicesFromType(AUDIO_DEVICE_OUT_SPEAKER);
         }
         if (devices2.isEmpty() && (getLastRemovableMediaDevices().size() > 0)) {
-            if ((getForceUse(AUDIO_POLICY_FORCE_FOR_MEDIA) != AUDIO_POLICY_FORCE_NO_BT_A2DP) &&
-                    outputs.isA2dpSupported()) {
+            if ((getForceUse(AUDIO_POLICY_FORCE_FOR_MEDIA) != AUDIO_POLICY_FORCE_NO_BT_A2DP)) {
                 // Get the last connected device of wired and bluetooth a2dp
                 devices2 = availableOutputDevices.getFirstDevicesFromTypes(
                         getLastRemovableMediaDevices());
@@ -518,8 +522,9 @@
             if (device != nullptr) break;
         }
         device = availableDevices.getFirstExistingDevice({
-                AUDIO_DEVICE_IN_WIRED_HEADSET, AUDIO_DEVICE_IN_USB_HEADSET,
-                AUDIO_DEVICE_IN_USB_DEVICE, AUDIO_DEVICE_IN_BUILTIN_MIC});
+                AUDIO_DEVICE_IN_BLE_HEADSET, AUDIO_DEVICE_IN_WIRED_HEADSET,
+                AUDIO_DEVICE_IN_USB_HEADSET, AUDIO_DEVICE_IN_USB_DEVICE,
+                AUDIO_DEVICE_IN_BUILTIN_MIC});
         break;
 
     case AUDIO_SOURCE_VOICE_COMMUNICATION:
@@ -543,9 +548,13 @@
             FALLTHROUGH_INTENDED;
 
         default:    // FORCE_NONE
+            // TODO (b/161358428): remove AUDIO_DEVICE_IN_BLE_HEADSET from the list
+            //  when preferred device for strategy phone will be used instead of
+            //  AUDIO_POLICY_FORCE_FOR_COMMUNICATION.
             device = availableDevices.getFirstExistingDevice({
-                    AUDIO_DEVICE_IN_WIRED_HEADSET, AUDIO_DEVICE_IN_USB_HEADSET,
-                    AUDIO_DEVICE_IN_USB_DEVICE, AUDIO_DEVICE_IN_BUILTIN_MIC});
+                    AUDIO_DEVICE_IN_BLE_HEADSET, AUDIO_DEVICE_IN_WIRED_HEADSET,
+                    AUDIO_DEVICE_IN_USB_HEADSET, AUDIO_DEVICE_IN_USB_DEVICE,
+                    AUDIO_DEVICE_IN_BUILTIN_MIC});
             break;
 
         case AUDIO_POLICY_FORCE_SPEAKER:
@@ -570,8 +579,9 @@
             if (device != nullptr) break;
         }
         device = availableDevices.getFirstExistingDevice({
-                AUDIO_DEVICE_IN_WIRED_HEADSET, AUDIO_DEVICE_IN_USB_HEADSET,
-                AUDIO_DEVICE_IN_USB_DEVICE, AUDIO_DEVICE_IN_BUILTIN_MIC});
+                AUDIO_DEVICE_IN_BLE_HEADSET, AUDIO_DEVICE_IN_WIRED_HEADSET,
+                AUDIO_DEVICE_IN_USB_HEADSET, AUDIO_DEVICE_IN_USB_DEVICE,
+                AUDIO_DEVICE_IN_BUILTIN_MIC});
         break;
     case AUDIO_SOURCE_CAMCORDER:
         // For a device without built-in mic, adding usb device
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index d9a5fa2..4a3e31f 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -29,31 +29,26 @@
 #define ALOGVV(a...) do { } while(0)
 #endif
 
-#define AUDIO_POLICY_XML_CONFIG_FILE_PATH_MAX_LENGTH 128
-#define AUDIO_POLICY_XML_CONFIG_FILE_NAME "audio_policy_configuration.xml"
-#define AUDIO_POLICY_A2DP_OFFLOAD_DISABLED_XML_CONFIG_FILE_NAME \
-        "audio_policy_configuration_a2dp_offload_disabled.xml"
-#define AUDIO_POLICY_BLUETOOTH_LEGACY_HAL_XML_CONFIG_FILE_NAME \
-        "audio_policy_configuration_bluetooth_legacy_hal.xml"
-
 #include <algorithm>
 #include <inttypes.h>
 #include <math.h>
 #include <set>
 #include <unordered_set>
 #include <vector>
+
+#include <Serializer.h>
 #include <cutils/bitops.h>
 #include <cutils/properties.h>
-#include <utils/Log.h>
 #include <media/AudioParameter.h>
+#include <policy.h>
 #include <private/android_filesystem_config.h>
 #include <system/audio.h>
 #include <system/audio_config.h>
 #include <system/audio_effects/effect_hapticgenerator.h>
+#include <utils/Log.h>
+
 #include "AudioPolicyManager.h"
-#include <Serializer.h>
 #include "TypeConverter.h"
-#include <policy.h>
 
 namespace android {
 
@@ -4539,37 +4534,15 @@
 }
 
 static status_t deserializeAudioPolicyXmlConfig(AudioPolicyConfig &config) {
-    char audioPolicyXmlConfigFile[AUDIO_POLICY_XML_CONFIG_FILE_PATH_MAX_LENGTH];
-    std::vector<const char*> fileNames;
-    status_t ret;
-
-    if (property_get_bool("ro.bluetooth.a2dp_offload.supported", false)) {
-        if (property_get_bool("persist.bluetooth.bluetooth_audio_hal.disabled", false) &&
-            property_get_bool("persist.bluetooth.a2dp_offload.disabled", false)) {
-            // Both BluetoothAudio@2.0 and BluetoothA2dp@1.0 (Offlaod) are disabled, and uses
-            // the legacy hardware module for A2DP and hearing aid.
-            fileNames.push_back(AUDIO_POLICY_BLUETOOTH_LEGACY_HAL_XML_CONFIG_FILE_NAME);
-        } else if (property_get_bool("persist.bluetooth.a2dp_offload.disabled", false)) {
-            // A2DP offload supported but disabled: try to use special XML file
-            fileNames.push_back(AUDIO_POLICY_A2DP_OFFLOAD_DISABLED_XML_CONFIG_FILE_NAME);
+    if (std::string audioPolicyXmlConfigFile = audio_get_audio_policy_config_file();
+            !audioPolicyXmlConfigFile.empty()) {
+        status_t ret = deserializeAudioPolicyFile(audioPolicyXmlConfigFile.c_str(), &config);
+        if (ret == NO_ERROR) {
+            config.setSource(audioPolicyXmlConfigFile);
         }
-    } else if (property_get_bool("persist.bluetooth.bluetooth_audio_hal.disabled", false)) {
-        fileNames.push_back(AUDIO_POLICY_BLUETOOTH_LEGACY_HAL_XML_CONFIG_FILE_NAME);
+        return ret;
     }
-    fileNames.push_back(AUDIO_POLICY_XML_CONFIG_FILE_NAME);
-
-    for (const char* fileName : fileNames) {
-        for (const auto& path : audio_get_configuration_paths()) {
-            snprintf(audioPolicyXmlConfigFile, sizeof(audioPolicyXmlConfigFile),
-                     "%s/%s", path.c_str(), fileName);
-            ret = deserializeAudioPolicyFile(audioPolicyXmlConfigFile, &config);
-            if (ret == NO_ERROR) {
-                config.setSource(audioPolicyXmlConfigFile);
-                return ret;
-            }
-        }
-    }
-    return ret;
+    return BAD_VALUE;
 }
 
 AudioPolicyManager::AudioPolicyManager(AudioPolicyClientInterface *clientInterface,
@@ -5638,8 +5611,8 @@
     }
     DeviceVector activeDevices;
     DeviceVector devices;
-    for (audio_stream_type_t curStream = AUDIO_STREAM_MIN; curStream < AUDIO_STREAM_PUBLIC_CNT;
-         curStream = (audio_stream_type_t) (curStream + 1)) {
+    for (int i = AUDIO_STREAM_MIN; i < AUDIO_STREAM_PUBLIC_CNT; ++i) {
+        const audio_stream_type_t curStream{static_cast<audio_stream_type_t>(i)};
         if (!streamsMatchForvolume(stream, curStream)) {
             continue;
         }
@@ -6251,9 +6224,8 @@
 
     float volumeDb = computeVolume(curves, volumeSource, index, deviceTypes);
     if (outputDesc->isFixedVolume(deviceTypes) ||
-            // Force VoIP volume to max for bluetooth SCO
-
-            ((isVoiceVolSrc || isBtScoVolSrc) &&
+            // Force VoIP volume to max for bluetooth SCO device except if muted
+            (index != 0 && (isVoiceVolSrc || isBtScoVolSrc) &&
                     isSingleDeviceType(deviceTypes, audio_is_bluetooth_out_sco_device))) {
         volumeDb = 0.0f;
     }
diff --git a/services/audiopolicy/tests/audiopolicymanager_tests.cpp b/services/audiopolicy/tests/audiopolicymanager_tests.cpp
index ca2164b..7972dbf 100644
--- a/services/audiopolicy/tests/audiopolicymanager_tests.cpp
+++ b/services/audiopolicy/tests/audiopolicymanager_tests.cpp
@@ -58,6 +58,34 @@
     ASSERT_EQ(NO_INIT, manager.initCheck());
 }
 
+// Verifies that a failure while loading a config doesn't leave
+// APM config in a "dirty" state. Since AudioPolicyConfig object
+// is a proxy for the data hosted by APM, it isn't possible
+// to "deep copy" it, and thus we have to test its elements
+// individually.
+TEST(AudioPolicyManagerTestInit, ConfigLoadingIsTransactional) {
+    AudioPolicyTestClient client;
+    AudioPolicyTestManager manager(&client);
+    ASSERT_TRUE(manager.getConfig().getHwModules().isEmpty());
+    ASSERT_TRUE(manager.getConfig().getInputDevices().isEmpty());
+    ASSERT_TRUE(manager.getConfig().getOutputDevices().isEmpty());
+    status_t status = deserializeAudioPolicyFile(
+            (base::GetExecutableDirectory() +
+                    "/test_invalid_audio_policy_configuration.xml").c_str(),
+            &manager.getConfig());
+    ASSERT_NE(NO_ERROR, status);
+    EXPECT_TRUE(manager.getConfig().getHwModules().isEmpty());
+    EXPECT_TRUE(manager.getConfig().getInputDevices().isEmpty());
+    EXPECT_TRUE(manager.getConfig().getOutputDevices().isEmpty());
+    status = deserializeAudioPolicyFile(
+            (base::GetExecutableDirectory() + "/test_audio_policy_configuration.xml").c_str(),
+            &manager.getConfig());
+    ASSERT_EQ(NO_ERROR, status);
+    EXPECT_FALSE(manager.getConfig().getHwModules().isEmpty());
+    EXPECT_FALSE(manager.getConfig().getInputDevices().isEmpty());
+    EXPECT_FALSE(manager.getConfig().getOutputDevices().isEmpty());
+}
+
 
 class PatchCountCheck {
   public:
diff --git a/services/audiopolicy/tests/resources/Android.bp b/services/audiopolicy/tests/resources/Android.bp
index d9476d9..4f50dad 100644
--- a/services/audiopolicy/tests/resources/Android.bp
+++ b/services/audiopolicy/tests/resources/Android.bp
@@ -3,6 +3,7 @@
     srcs: [
         "test_audio_policy_configuration.xml",
         "test_audio_policy_primary_only_configuration.xml",
+        "test_invalid_audio_policy_configuration.xml",
         "test_tv_apm_configuration.xml",
     ],
 }
diff --git a/services/audiopolicy/tests/resources/test_invalid_audio_policy_configuration.xml b/services/audiopolicy/tests/resources/test_invalid_audio_policy_configuration.xml
new file mode 100644
index 0000000..25641d5
--- /dev/null
+++ b/services/audiopolicy/tests/resources/test_invalid_audio_policy_configuration.xml
@@ -0,0 +1,113 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<!-- 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.
+-->
+
+<!-- This file contains an unnamed device port in the "r_submix" module section. -->
+<audioPolicyConfiguration version="1.0" xmlns:xi="http://www.w3.org/2001/XInclude">
+    <globalConfiguration speaker_drc_enabled="true"/>
+
+    <modules>
+        <!-- Primary module -->
+        <module name="primary" halVersion="2.0">
+            <attachedDevices>
+                <item>Speaker</item>
+                <item>Built-In Mic</item>
+            </attachedDevices>
+            <defaultOutputDevice>Speaker</defaultOutputDevice>
+            <mixPorts>
+                <mixPort name="primary output" role="source" flags="AUDIO_OUTPUT_FLAG_PRIMARY">
+                    <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+                             samplingRates="48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
+                </mixPort>
+                <mixPort name="primary input" role="sink">
+                    <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+                             samplingRates="48000"
+                             channelMasks="AUDIO_CHANNEL_IN_STEREO"/>
+                </mixPort>
+                <mixPort name="mixport_bt_hfp_output" role="source">
+                    <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+                             samplingRates="48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
+                </mixPort>
+                <mixPort name="mixport_bt_hfp_input" role="sink">
+                    <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+                             samplingRates="8000,11025,16000,44100,48000"
+                             channelMasks="AUDIO_CHANNEL_IN_STEREO,AUDIO_CHANNEL_IN_MONO"/>
+                </mixPort>
+            </mixPorts>
+            <devicePorts>
+                <devicePort tagName="Speaker" type="AUDIO_DEVICE_OUT_SPEAKER" role="sink">
+                </devicePort>
+                <devicePort tagName="Built-In Mic" type="AUDIO_DEVICE_IN_BUILTIN_MIC" role="source">
+                </devicePort>
+                <devicePort tagName="Hdmi" type="AUDIO_DEVICE_OUT_HDMI" role="sink">
+                </devicePort>
+                <devicePort tagName="Hdmi-In Mic" type="AUDIO_DEVICE_IN_HDMI" role="source">
+                </devicePort>
+                <devicePort tagName="BT SCO" type="AUDIO_DEVICE_OUT_BLUETOOTH_SCO"
+                            role="sink" address="hfp_client_out">
+                </devicePort>
+                <devicePort tagName="BT SCO Headset Mic" type="AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET"
+                            role="source" address="hfp_client_in">
+                </devicePort>
+            </devicePorts>
+            <routes>
+                <route type="mix" sink="Speaker"
+                       sources="primary output"/>
+                <route type="mix" sink="primary input"
+                       sources="Built-In Mic,Hdmi-In Mic"/>
+                <route type="mix" sink="Hdmi"
+                       sources="primary output"/>
+                <route type="mix" sink="BT SCO"
+                       sources="mixport_bt_hfp_output"/>
+                <route type="mix" sink="mixport_bt_hfp_input"
+                       sources="BT SCO Headset Mic"/>
+            </routes>
+        </module>
+
+        <!-- Remote Submix module -->
+        <module name="r_submix" halVersion="2.0">
+            <attachedDevices>
+                <item>Remote Submix In</item>
+            </attachedDevices>
+            <mixPorts>
+                <mixPort name="r_submix output" role="source">
+                    <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+                             samplingRates="48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
+                </mixPort>
+                <mixPort name="r_submix input" role="sink">
+                   <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+                            samplingRates="48000" channelMasks="AUDIO_CHANNEL_IN_STEREO"/>
+                </mixPort>
+           </mixPorts>
+           <devicePorts>
+               <!-- This port is missing "tagName" attribute. -->
+               <devicePort type="AUDIO_DEVICE_OUT_REMOTE_SUBMIX"  role="sink">
+                   <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+                            samplingRates="48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
+               </devicePort>
+               <devicePort tagName="Remote Submix In" type="AUDIO_DEVICE_IN_REMOTE_SUBMIX"  role="source">
+                   <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+                            samplingRates="48000" channelMasks="AUDIO_CHANNEL_IN_STEREO"/>
+                </devicePort>
+            </devicePorts>
+            <routes>
+                <route type="mix" sink="Remote Submix Out"
+                       sources="r_submix output"/>
+                <route type="mix" sink="r_submix input"
+                       sources="Remote Submix In"/>
+            </routes>
+        </module>
+    </modules>
+</audioPolicyConfiguration>
diff --git a/services/mediametrics/Android.bp b/services/mediametrics/Android.bp
index f033d5c..91590e1 100644
--- a/services/mediametrics/Android.bp
+++ b/services/mediametrics/Android.bp
@@ -131,6 +131,7 @@
         "statsd_codec.cpp",
         "statsd_drm.cpp",
         "statsd_extractor.cpp",
+        "statsd_mediaparser.cpp",
         "statsd_nuplayer.cpp",
         "statsd_recorder.cpp",
         "StringUtils.cpp"
diff --git a/services/mediametrics/AudioPowerUsage.cpp b/services/mediametrics/AudioPowerUsage.cpp
index 33dfa8fa..34be0b9 100644
--- a/services/mediametrics/AudioPowerUsage.cpp
+++ b/services/mediametrics/AudioPowerUsage.cpp
@@ -28,7 +28,7 @@
 #include <cutils/properties.h>
 #include <statslog.h>
 #include <sys/timerfd.h>
-#include <system/audio-base.h>
+#include <system/audio.h>
 
 // property to disable audio power use metrics feature, default is enabled
 #define PROP_AUDIO_METRICS_DISABLED "persist.media.audio_metrics.power_usage_disabled"
diff --git a/services/mediametrics/iface_statsd.cpp b/services/mediametrics/iface_statsd.cpp
index 6e51f72..16204de 100644
--- a/services/mediametrics/iface_statsd.cpp
+++ b/services/mediametrics/iface_statsd.cpp
@@ -64,6 +64,7 @@
     { "drmmanager", statsd_drmmanager },
     { "extractor", statsd_extractor },
     { "mediadrm", statsd_mediadrm },
+    { "mediaparser", statsd_mediaparser },
     { "nuplayer", statsd_nuplayer },
     { "nuplayer2", statsd_nuplayer },
     { "recorder", statsd_recorder },
diff --git a/services/mediametrics/iface_statsd.h b/services/mediametrics/iface_statsd.h
index 19505a4..9b49556 100644
--- a/services/mediametrics/iface_statsd.h
+++ b/services/mediametrics/iface_statsd.h
@@ -25,6 +25,7 @@
 extern bool statsd_audiotrack(const mediametrics::Item *);
 extern bool statsd_codec(const mediametrics::Item *);
 extern bool statsd_extractor(const mediametrics::Item *);
+extern bool statsd_mediaparser(const mediametrics::Item *);
 extern bool statsd_nuplayer(const mediametrics::Item *);
 extern bool statsd_recorder(const mediametrics::Item *);
 
diff --git a/services/mediametrics/statsd_mediaparser.cpp b/services/mediametrics/statsd_mediaparser.cpp
new file mode 100644
index 0000000..3258ebf
--- /dev/null
+++ b/services/mediametrics/statsd_mediaparser.cpp
@@ -0,0 +1,106 @@
+/*
+ * 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_TAG "statsd_mediaparser"
+#include <utils/Log.h>
+
+#include <dirent.h>
+#include <inttypes.h>
+#include <pthread.h>
+#include <pwd.h>
+#include <stdint.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <statslog.h>
+
+#include "MediaMetricsService.h"
+#include "frameworks/base/core/proto/android/stats/mediametrics/mediametrics.pb.h"
+#include "iface_statsd.h"
+
+namespace android {
+
+bool statsd_mediaparser(const mediametrics::Item *item)
+{
+    if (item == nullptr) {
+        return false;
+    }
+
+    // statsd wrapper data.
+    const nsecs_t timestamp = MediaMetricsService::roundTime(item->getTimestamp());
+    std::string pkgName = item->getPkgName();
+    int64_t pkgVersionCode = item->getPkgVersionCode();
+
+    std::string parserName;
+    item->getString("android.media.mediaparser.parserName", &parserName);
+
+    int32_t createdByName = -1;
+    item->getInt32("android.media.mediaparser.createdByName", &createdByName);
+
+    std::string parserPool;
+    item->getString("android.media.mediaparser.parserPool", &parserPool);
+
+    std::string lastException;
+    item->getString("android.media.mediaparser.lastException", &lastException);
+
+    int64_t resourceByteCount = -1;
+    item->getInt64("android.media.mediaparser.resourceByteCount", &resourceByteCount);
+
+    int64_t durationMillis = -1;
+    item->getInt64("android.media.mediaparser.durationMillis", &durationMillis);
+
+    std::string trackMimeTypes;
+    item->getString("android.media.mediaparser.trackMimeTypes", &trackMimeTypes);
+
+    std::string trackCodecs;
+    item->getString("android.media.mediaparser.trackCodecs", &trackCodecs);
+
+    std::string alteredParameters;
+    item->getString("android.media.mediaparser.alteredParameters", &alteredParameters);
+
+    int32_t videoWidth = -1;
+    item->getInt32("android.media.mediaparser.videoWidth", &videoWidth);
+
+    int32_t videoHeight = -1;
+    item->getInt32("android.media.mediaparser.videoHeight", &videoHeight);
+
+    if (enabled_statsd) {
+        (void) android::util::stats_write(android::util::MEDIAMETRICS_MEDIAPARSER_REPORTED,
+                                   timestamp,
+                                   pkgName.c_str(),
+                                   pkgVersionCode,
+                                   parserName.c_str(),
+                                   createdByName,
+                                   parserPool.c_str(),
+                                   lastException.c_str(),
+                                   resourceByteCount,
+                                   durationMillis,
+                                   trackMimeTypes.c_str(),
+                                   trackCodecs.c_str(),
+                                   alteredParameters.c_str(),
+                                   videoWidth,
+                                   videoHeight);
+    } else {
+        ALOGV("NOT sending MediaParser media metrics.");
+    }
+
+    return true;
+}
+
+} // namespace android
diff --git a/services/mediatranscoding/MediaTranscodingService.cpp b/services/mediatranscoding/MediaTranscodingService.cpp
index 9639c55..56f327e 100644
--- a/services/mediatranscoding/MediaTranscodingService.cpp
+++ b/services/mediatranscoding/MediaTranscodingService.cpp
@@ -24,8 +24,8 @@
 #include <cutils/properties.h>
 #include <media/TranscoderWrapper.h>
 #include <media/TranscodingClientManager.h>
-#include <media/TranscodingJobScheduler.h>
 #include <media/TranscodingResourcePolicy.h>
+#include <media/TranscodingSessionController.h>
 #include <media/TranscodingUidPolicy.h>
 #include <utils/Log.h>
 #include <utils/Vector.h>
@@ -44,12 +44,13 @@
         const std::shared_ptr<TranscoderInterface>& transcoder)
       : mUidPolicy(new TranscodingUidPolicy()),
         mResourcePolicy(new TranscodingResourcePolicy()),
-        mJobScheduler(new TranscodingJobScheduler(transcoder, mUidPolicy, mResourcePolicy)),
-        mClientManager(new TranscodingClientManager(mJobScheduler)) {
+        mSessionController(
+                new TranscodingSessionController(transcoder, mUidPolicy, mResourcePolicy)),
+        mClientManager(new TranscodingClientManager(mSessionController)) {
     ALOGV("MediaTranscodingService is created");
-    transcoder->setCallback(mJobScheduler);
-    mUidPolicy->setCallback(mJobScheduler);
-    mResourcePolicy->setCallback(mJobScheduler);
+    transcoder->setCallback(mSessionController);
+    mUidPolicy->setCallback(mSessionController);
+    mResourcePolicy->setCallback(mSessionController);
 }
 
 MediaTranscodingService::~MediaTranscodingService() {
@@ -78,7 +79,7 @@
 
     Vector<String16> args;
     mClientManager->dumpAllClients(fd, args);
-    mJobScheduler->dumpAllJobs(fd, args);
+    mSessionController->dumpAllSessions(fd, args);
     return OK;
 }
 
diff --git a/services/mediatranscoding/MediaTranscodingService.h b/services/mediatranscoding/MediaTranscodingService.h
index 0fe6864..428f777 100644
--- a/services/mediatranscoding/MediaTranscodingService.h
+++ b/services/mediatranscoding/MediaTranscodingService.h
@@ -26,17 +26,17 @@
 using ::aidl::android::media::BnMediaTranscodingService;
 using ::aidl::android::media::ITranscodingClient;
 using ::aidl::android::media::ITranscodingClientCallback;
-using ::aidl::android::media::TranscodingJobParcel;
 using ::aidl::android::media::TranscodingRequestParcel;
+using ::aidl::android::media::TranscodingSessionParcel;
 class TranscodingClientManager;
-class TranscodingJobScheduler;
+class TranscodingSessionController;
 class TranscoderInterface;
 class UidPolicyInterface;
 class ResourcePolicyInterface;
 
 class MediaTranscodingService : public BnMediaTranscodingService {
 public:
-    static constexpr int32_t kInvalidJobId = -1;
+    static constexpr int32_t kInvalidSessionId = -1;
     static constexpr int32_t kInvalidClientId = -1;
 
     MediaTranscodingService(const std::shared_ptr<TranscoderInterface>& transcoder);
@@ -61,7 +61,7 @@
 
     std::shared_ptr<UidPolicyInterface> mUidPolicy;
     std::shared_ptr<ResourcePolicyInterface> mResourcePolicy;
-    std::shared_ptr<TranscodingJobScheduler> mJobScheduler;
+    std::shared_ptr<TranscodingSessionController> mSessionController;
     std::shared_ptr<TranscodingClientManager> mClientManager;
 };
 
diff --git a/services/mediatranscoding/SimulatedTranscoder.cpp b/services/mediatranscoding/SimulatedTranscoder.cpp
index 97d5f5f..03ee886 100644
--- a/services/mediatranscoding/SimulatedTranscoder.cpp
+++ b/services/mediatranscoding/SimulatedTranscoder.cpp
@@ -48,58 +48,59 @@
 }
 
 void SimulatedTranscoder::start(
-        ClientIdType clientId, JobIdType jobId, const TranscodingRequestParcel& request,
+        ClientIdType clientId, SessionIdType sessionId, const TranscodingRequestParcel& request,
         const std::shared_ptr<ITranscodingClientCallback>& /*clientCallback*/) {
     if (request.testConfig.has_value() && request.testConfig->processingTotalTimeMs > 0) {
-        mJobProcessingTimeMs = request.testConfig->processingTotalTimeMs;
+        mSessionProcessingTimeMs = request.testConfig->processingTotalTimeMs;
     }
-    ALOGV("%s: job {%d}: processingTime: %lld", __FUNCTION__, jobId,
-          (long long)mJobProcessingTimeMs);
-    queueEvent(Event::Start, clientId, jobId, [=] {
+    ALOGV("%s: session {%d}: processingTime: %lld", __FUNCTION__, sessionId,
+          (long long)mSessionProcessingTimeMs);
+    queueEvent(Event::Start, clientId, sessionId, [=] {
         auto callback = mCallback.lock();
         if (callback != nullptr) {
-            callback->onStarted(clientId, jobId);
+            callback->onStarted(clientId, sessionId);
         }
     });
 }
 
-void SimulatedTranscoder::pause(ClientIdType clientId, JobIdType jobId) {
-    queueEvent(Event::Pause, clientId, jobId, [=] {
+void SimulatedTranscoder::pause(ClientIdType clientId, SessionIdType sessionId) {
+    queueEvent(Event::Pause, clientId, sessionId, [=] {
         auto callback = mCallback.lock();
         if (callback != nullptr) {
-            callback->onPaused(clientId, jobId);
+            callback->onPaused(clientId, sessionId);
         }
     });
 }
 
 void SimulatedTranscoder::resume(
-        ClientIdType clientId, JobIdType jobId, const TranscodingRequestParcel& /*request*/,
+        ClientIdType clientId, SessionIdType sessionId, const TranscodingRequestParcel& /*request*/,
         const std::shared_ptr<ITranscodingClientCallback>& /*clientCallback*/) {
-    queueEvent(Event::Resume, clientId, jobId, [=] {
+    queueEvent(Event::Resume, clientId, sessionId, [=] {
         auto callback = mCallback.lock();
         if (callback != nullptr) {
-            callback->onResumed(clientId, jobId);
+            callback->onResumed(clientId, sessionId);
         }
     });
 }
 
-void SimulatedTranscoder::stop(ClientIdType clientId, JobIdType jobId) {
-    queueEvent(Event::Stop, clientId, jobId, nullptr);
+void SimulatedTranscoder::stop(ClientIdType clientId, SessionIdType sessionId) {
+    queueEvent(Event::Stop, clientId, sessionId, nullptr);
 }
 
-void SimulatedTranscoder::queueEvent(Event::Type type, ClientIdType clientId, JobIdType jobId,
-                                     std::function<void()> runnable) {
-    ALOGV("%s: job {%lld, %d}: %s", __FUNCTION__, (long long)clientId, jobId, toString(type));
+void SimulatedTranscoder::queueEvent(Event::Type type, ClientIdType clientId,
+                                     SessionIdType sessionId, std::function<void()> runnable) {
+    ALOGV("%s: session {%lld, %d}: %s", __FUNCTION__, (long long)clientId, sessionId,
+          toString(type));
 
     auto lock = std::scoped_lock(mLock);
 
-    mQueue.push_back({type, clientId, jobId, runnable});
+    mQueue.push_back({type, clientId, sessionId, runnable});
     mCondition.notify_one();
 }
 
 void SimulatedTranscoder::threadLoop() {
     bool running = false;
-    std::chrono::microseconds remainingUs(kJobDurationUs);
+    std::chrono::microseconds remainingUs(kSessionDurationUs);
     std::chrono::system_clock::time_point lastRunningTime;
     Event lastRunningEvent;
 
@@ -113,7 +114,7 @@
                 mCondition.wait(lock);
                 continue;
             }
-            // If running, wait for the remaining life of this job. Report finish if timed out.
+            // If running, wait for the remaining life of this session. Report finish if timed out.
             std::cv_status status = mCondition.wait_for(lock, remainingUs);
             if (status == std::cv_status::timeout) {
                 running = false;
@@ -121,7 +122,7 @@
                 auto callback = mCallback.lock();
                 if (callback != nullptr) {
                     lock.unlock();
-                    callback->onFinish(lastRunningEvent.clientId, lastRunningEvent.jobId);
+                    callback->onFinish(lastRunningEvent.clientId, lastRunningEvent.sessionId);
                     lock.lock();
                 }
             } else {
@@ -139,22 +140,22 @@
             Event event = *mQueue.begin();
             mQueue.pop_front();
 
-            ALOGV("%s: job {%lld, %d}: %s", __FUNCTION__, (long long)event.clientId, event.jobId,
-                  toString(event.type));
+            ALOGV("%s: session {%lld, %d}: %s", __FUNCTION__, (long long)event.clientId,
+                  event.sessionId, toString(event.type));
 
             if (!running && (event.type == Event::Start || event.type == Event::Resume)) {
                 running = true;
                 lastRunningTime = std::chrono::system_clock::now();
                 lastRunningEvent = event;
                 if (event.type == Event::Start) {
-                    remainingUs = std::chrono::milliseconds(mJobProcessingTimeMs);
+                    remainingUs = std::chrono::milliseconds(mSessionProcessingTimeMs);
                 }
             } else if (running && (event.type == Event::Pause || event.type == Event::Stop)) {
                 running = false;
                 remainingUs -= (std::chrono::system_clock::now() - lastRunningTime);
             } else {
-                ALOGW("%s: discarding bad event: job {%lld, %d}: %s", __FUNCTION__,
-                      (long long)event.clientId, event.jobId, toString(event.type));
+                ALOGW("%s: discarding bad event: session {%lld, %d}: %s", __FUNCTION__,
+                      (long long)event.clientId, event.sessionId, toString(event.type));
                 continue;
             }
 
diff --git a/services/mediatranscoding/SimulatedTranscoder.h b/services/mediatranscoding/SimulatedTranscoder.h
index 1c359dd..ba2bba0 100644
--- a/services/mediatranscoding/SimulatedTranscoder.h
+++ b/services/mediatranscoding/SimulatedTranscoder.h
@@ -32,8 +32,8 @@
  * SimulatedTranscoder is used when useSimulatedTranscoder in TranscodingTestConfig
  * is set to true.
  *
- * SimulatedTranscoder simulates job execution by reporting finish after kJobDurationUs.
- * Job lifecycle events are reported via progress updates with special progress
+ * SimulatedTranscoder simulates session execution by reporting finish after kSessionDurationUs.
+ * Session lifecycle events are reported via progress updates with special progress
  * numbers (equal to the Event's type).
  */
 class SimulatedTranscoder : public TranscoderInterface {
@@ -41,22 +41,24 @@
     struct Event {
         enum Type { NoEvent, Start, Pause, Resume, Stop, Finished, Failed } type;
         ClientIdType clientId;
-        JobIdType jobId;
+        SessionIdType sessionId;
         std::function<void()> runnable;
     };
 
-    static constexpr int64_t kJobDurationUs = 1000000;
+    static constexpr int64_t kSessionDurationUs = 1000000;
 
     SimulatedTranscoder();
 
     // TranscoderInterface
     void setCallback(const std::shared_ptr<TranscoderCallbackInterface>& cb) override;
-    void start(ClientIdType clientId, JobIdType jobId, const TranscodingRequestParcel& request,
+    void start(ClientIdType clientId, SessionIdType sessionId,
+               const TranscodingRequestParcel& request,
                const std::shared_ptr<ITranscodingClientCallback>& clientCallback) override;
-    void pause(ClientIdType clientId, JobIdType jobId) override;
-    void resume(ClientIdType clientId, JobIdType jobId, const TranscodingRequestParcel& request,
+    void pause(ClientIdType clientId, SessionIdType sessionId) override;
+    void resume(ClientIdType clientId, SessionIdType sessionId,
+                const TranscodingRequestParcel& request,
                 const std::shared_ptr<ITranscodingClientCallback>& clientCallback) override;
-    void stop(ClientIdType clientId, JobIdType jobId) override;
+    void stop(ClientIdType clientId, SessionIdType sessionId) override;
     // ~TranscoderInterface
 
 private:
@@ -66,10 +68,10 @@
     std::list<Event> mQueue GUARDED_BY(mLock);
 
     // Minimum time spent on transcode the video. This is used just for testing.
-    int64_t mJobProcessingTimeMs = kJobDurationUs / 1000;
+    int64_t mSessionProcessingTimeMs = kSessionDurationUs / 1000;
 
     static const char* toString(Event::Type type);
-    void queueEvent(Event::Type type, ClientIdType clientId, JobIdType jobId,
+    void queueEvent(Event::Type type, ClientIdType clientId, SessionIdType sessionId,
                     std::function<void()> runnable);
     void threadLoop();
 };
diff --git a/services/mediatranscoding/tests/MediaTranscodingServiceTestHelper.h b/services/mediatranscoding/tests/MediaTranscodingServiceTestHelper.h
index 6d0b99e..f4d3ff8 100644
--- a/services/mediatranscoding/tests/MediaTranscodingServiceTestHelper.h
+++ b/services/mediatranscoding/tests/MediaTranscodingServiceTestHelper.h
@@ -20,9 +20,9 @@
 #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 <aidl/android/media/TranscodingSessionParcel.h>
+#include <aidl/android/media/TranscodingSessionPriority.h>
 #include <android-base/logging.h>
 #include <android/binder_manager.h>
 #include <android/binder_process.h>
@@ -48,9 +48,9 @@
 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::TranscodingSessionParcel;
+using aidl::android::media::TranscodingSessionPriority;
 using aidl::android::media::TranscodingVideoTrackFormat;
 
 constexpr int32_t kClientUseCallingPid = IMediaTranscodingService::USE_CALLING_PID;
@@ -112,12 +112,12 @@
     struct Event {
         enum { NoEvent, Start, Pause, Resume, Finished, Failed } type;
         int64_t clientId;
-        int32_t jobId;
+        int32_t sessionId;
     };
 
-#define DECLARE_EVENT(action)                              \
-    static Event action(int32_t clientId, int32_t jobId) { \
-        return {Event::action, clientId, jobId};           \
+#define DECLARE_EVENT(action)                                  \
+    static Event action(int32_t clientId, int32_t sessionId) { \
+        return {Event::action, clientId, sessionId};           \
     }
 
     DECLARE_EVENT(Start);
@@ -149,8 +149,8 @@
         default:
             return "NoEvent";
         }
-        return "job {" + std::to_string(event.clientId) + ", " + std::to_string(event.jobId) +
-               "}: " + eventStr;
+        return "session {" + std::to_string(event.clientId) + ", " +
+               std::to_string(event.sessionId) + "}: " + eventStr;
     }
 
     // Pop 1 event from front, wait for up to timeoutUs if empty.
@@ -238,7 +238,7 @@
 
 // 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;
+    return lhs.type == rhs.type && lhs.clientId == rhs.clientId && lhs.sessionId == rhs.sessionId;
 }
 
 std::ostream& operator<<(std::ostream& str, const EventTracker::Event& v) {
@@ -288,40 +288,41 @@
         return Status::ok();
     }
 
-    Status onTranscodingStarted(int32_t in_jobId) override {
-        append(EventTracker::Start(mClientId, in_jobId));
+    Status onTranscodingStarted(int32_t in_sessionId) override {
+        append(EventTracker::Start(mClientId, in_sessionId));
         return Status::ok();
     }
 
-    Status onTranscodingPaused(int32_t in_jobId) override {
-        append(EventTracker::Pause(mClientId, in_jobId));
+    Status onTranscodingPaused(int32_t in_sessionId) override {
+        append(EventTracker::Pause(mClientId, in_sessionId));
         return Status::ok();
     }
 
-    Status onTranscodingResumed(int32_t in_jobId) override {
-        append(EventTracker::Resume(mClientId, in_jobId));
+    Status onTranscodingResumed(int32_t in_sessionId) override {
+        append(EventTracker::Resume(mClientId, in_sessionId));
         return Status::ok();
     }
 
     Status onTranscodingFinished(
-            int32_t in_jobId,
+            int32_t in_sessionId,
             const ::aidl::android::media::TranscodingResultParcel& /* in_result */) override {
-        append(Finished(mClientId, in_jobId));
+        append(Finished(mClientId, in_sessionId));
         return Status::ok();
     }
 
-    Status onTranscodingFailed(int32_t in_jobId,
+    Status onTranscodingFailed(int32_t in_sessionId,
                                ::aidl::android::media::TranscodingErrorCode in_errorCode) override {
-        append(Failed(mClientId, in_jobId), in_errorCode);
+        append(Failed(mClientId, in_sessionId), in_errorCode);
         return Status::ok();
     }
 
-    Status onAwaitNumberOfJobsChanged(int32_t /* in_jobId */, int32_t /* in_oldAwaitNumber */,
-                                      int32_t /* in_newAwaitNumber */) override {
+    Status onAwaitNumberOfSessionsChanged(int32_t /* in_sessionId */,
+                                          int32_t /* in_oldAwaitNumber */,
+                                          int32_t /* in_newAwaitNumber */) override {
         return Status::ok();
     }
 
-    Status onProgressUpdate(int32_t /* in_jobId */, int32_t in_progress) override {
+    Status onProgressUpdate(int32_t /* in_sessionId */, int32_t in_progress) override {
         updateProgress(in_progress);
         return Status::ok();
     }
@@ -354,39 +355,41 @@
     }
 
     template <bool expectation = success>
-    bool submit(int32_t jobId, const char* sourceFilePath, const char* destinationFilePath,
-                TranscodingJobPriority priority = TranscodingJobPriority::kNormal,
+    bool submit(int32_t sessionId, const char* sourceFilePath, const char* destinationFilePath,
+                TranscodingSessionPriority priority = TranscodingSessionPriority::kNormal,
                 int bitrateBps = -1, int overridePid = -1, int overrideUid = -1) {
         constexpr bool shouldSucceed = (expectation == success);
         bool result;
         TranscodingRequestParcel request;
-        TranscodingJobParcel job;
+        TranscodingSessionParcel session;
 
         request.sourceFilePath = sourceFilePath;
         request.destinationFilePath = destinationFilePath;
         request.priority = priority;
         request.clientPid = (overridePid == -1) ? mClientPid : overridePid;
         request.clientUid = (overrideUid == -1) ? mClientUid : overrideUid;
+        request.clientPackageName = (overrideUid == -1) ? mPackageName : "";
         if (bitrateBps > 0) {
             request.requestedVideoTrackFormat.emplace(TranscodingVideoTrackFormat());
             request.requestedVideoTrackFormat->bitrateBps = bitrateBps;
         }
-        Status status = mClient->submitRequest(request, &job, &result);
+        Status status = mClient->submitRequest(request, &session, &result);
 
         EXPECT_TRUE(status.isOk());
         EXPECT_EQ(result, shouldSucceed);
         if (shouldSucceed) {
-            EXPECT_EQ(job.jobId, jobId);
+            EXPECT_EQ(session.sessionId, sessionId);
         }
 
-        return status.isOk() && (result == shouldSucceed) && (!shouldSucceed || job.jobId == jobId);
+        return status.isOk() && (result == shouldSucceed) &&
+               (!shouldSucceed || session.sessionId == sessionId);
     }
 
     template <bool expectation = success>
-    bool cancel(int32_t jobId) {
+    bool cancel(int32_t sessionId) {
         constexpr bool shouldSucceed = (expectation == success);
         bool result;
-        Status status = mClient->cancelJob(jobId, &result);
+        Status status = mClient->cancelSession(sessionId, &result);
 
         EXPECT_TRUE(status.isOk());
         EXPECT_EQ(result, shouldSucceed);
@@ -395,23 +398,24 @@
     }
 
     template <bool expectation = success>
-    bool getJob(int32_t jobId, const char* sourceFilePath, const char* destinationFilePath) {
+    bool getSession(int32_t sessionId, const char* sourceFilePath,
+                    const char* destinationFilePath) {
         constexpr bool shouldSucceed = (expectation == success);
         bool result;
-        TranscodingJobParcel job;
-        Status status = mClient->getJobWithId(jobId, &job, &result);
+        TranscodingSessionParcel session;
+        Status status = mClient->getSessionWithId(sessionId, &session, &result);
 
         EXPECT_TRUE(status.isOk());
         EXPECT_EQ(result, shouldSucceed);
         if (shouldSucceed) {
-            EXPECT_EQ(job.jobId, jobId);
-            EXPECT_EQ(job.request.sourceFilePath, sourceFilePath);
+            EXPECT_EQ(session.sessionId, sessionId);
+            EXPECT_EQ(session.request.sourceFilePath, sourceFilePath);
         }
 
         return status.isOk() && (result == shouldSucceed) &&
-               (!shouldSucceed ||
-                (job.jobId == jobId && job.request.sourceFilePath == sourceFilePath &&
-                 job.request.destinationFilePath == destinationFilePath));
+               (!shouldSucceed || (session.sessionId == sessionId &&
+                                   session.request.sourceFilePath == sourceFilePath &&
+                                   session.request.destinationFilePath == destinationFilePath));
     }
 
     int32_t mClientId;
diff --git a/services/mediatranscoding/tests/mediatranscodingservice_real_tests.cpp b/services/mediatranscoding/tests/mediatranscodingservice_real_tests.cpp
index 381bbf5..0550d77 100644
--- a/services/mediatranscoding/tests/mediatranscodingservice_real_tests.cpp
+++ b/services/mediatranscoding/tests/mediatranscodingservice_real_tests.cpp
@@ -34,7 +34,7 @@
 namespace media {
 
 constexpr int64_t kPaddingUs = 400000;
-constexpr int64_t kJobWithPaddingUs = 10000000 + kPaddingUs;
+constexpr int64_t kSessionWithPaddingUs = 10000000 + kPaddingUs;
 constexpr int32_t kBitRate = 8 * 1000 * 1000;  // 8Mbs
 
 constexpr const char* kShortSrcPath =
@@ -59,8 +59,9 @@
     const char* dstPath = OUTPATH(TestInvalidSource);
     deleteFile(dstPath);
 
-    // Submit one job.
-    EXPECT_TRUE(mClient1->submit(0, srcPath, dstPath, TranscodingJobPriority::kNormal, kBitRate));
+    // Submit one session.
+    EXPECT_TRUE(
+            mClient1->submit(0, srcPath, dstPath, TranscodingSessionPriority::kNormal, kBitRate));
 
     // Check expected error.
     EXPECT_EQ(mClient1->pop(kPaddingUs), EventTracker::Failed(CLIENT(1), 0));
@@ -75,12 +76,12 @@
     const char* dstPath = OUTPATH(TestPassthru);
     deleteFile(dstPath);
 
-    // Submit one job.
+    // Submit one session.
     EXPECT_TRUE(mClient1->submit(0, kShortSrcPath, dstPath));
 
-    // Wait for job to finish.
+    // Wait for session to finish.
     EXPECT_EQ(mClient1->pop(kPaddingUs), EventTracker::Start(CLIENT(1), 0));
-    EXPECT_EQ(mClient1->pop(kJobWithPaddingUs), EventTracker::Finished(CLIENT(1), 0));
+    EXPECT_EQ(mClient1->pop(kSessionWithPaddingUs), EventTracker::Finished(CLIENT(1), 0));
 
     unregisterMultipleClients();
 }
@@ -91,13 +92,13 @@
     const char* dstPath = OUTPATH(TestTranscodeVideo);
     deleteFile(dstPath);
 
-    // Submit one job.
-    EXPECT_TRUE(
-            mClient1->submit(0, kShortSrcPath, dstPath, TranscodingJobPriority::kNormal, kBitRate));
+    // Submit one session.
+    EXPECT_TRUE(mClient1->submit(0, kShortSrcPath, dstPath, TranscodingSessionPriority::kNormal,
+                                 kBitRate));
 
-    // Wait for job to finish.
+    // Wait for session to finish.
     EXPECT_EQ(mClient1->pop(kPaddingUs), EventTracker::Start(CLIENT(1), 0));
-    EXPECT_EQ(mClient1->pop(kJobWithPaddingUs), EventTracker::Finished(CLIENT(1), 0));
+    EXPECT_EQ(mClient1->pop(kSessionWithPaddingUs), EventTracker::Finished(CLIENT(1), 0));
 
     unregisterMultipleClients();
 }
@@ -108,13 +109,13 @@
     const char* dstPath = OUTPATH(TestTranscodeVideoProgress);
     deleteFile(dstPath);
 
-    // Submit one job.
-    EXPECT_TRUE(
-            mClient1->submit(0, kLongSrcPath, dstPath, TranscodingJobPriority::kNormal, kBitRate));
+    // Submit one session.
+    EXPECT_TRUE(mClient1->submit(0, kLongSrcPath, dstPath, TranscodingSessionPriority::kNormal,
+                                 kBitRate));
 
-    // Wait for job to finish.
+    // Wait for session to finish.
     EXPECT_EQ(mClient1->pop(kPaddingUs), EventTracker::Start(CLIENT(1), 0));
-    EXPECT_EQ(mClient1->pop(kJobWithPaddingUs), EventTracker::Finished(CLIENT(1), 0));
+    EXPECT_EQ(mClient1->pop(kSessionWithPaddingUs), EventTracker::Finished(CLIENT(1), 0));
 
     // Check the progress update messages are received. For this clip (around ~15 second long),
     // expect at least 10 updates, and the last update should be 100.
@@ -133,24 +134,26 @@
 
     const char* srcPath0 = kLongSrcPath;
     const char* srcPath1 = kShortSrcPath;
-    const char* dstPath0 = OUTPATH(TestCancelImmediately_Job0);
-    const char* dstPath1 = OUTPATH(TestCancelImmediately_Job1);
+    const char* dstPath0 = OUTPATH(TestCancelImmediately_Session0);
+    const char* dstPath1 = OUTPATH(TestCancelImmediately_Session1);
 
     deleteFile(dstPath0);
     deleteFile(dstPath1);
-    // Submit one job, should start immediately.
-    EXPECT_TRUE(mClient1->submit(0, srcPath0, dstPath0, TranscodingJobPriority::kNormal, kBitRate));
+    // Submit one session, should start immediately.
+    EXPECT_TRUE(
+            mClient1->submit(0, srcPath0, dstPath0, TranscodingSessionPriority::kNormal, kBitRate));
     EXPECT_EQ(mClient1->pop(kPaddingUs), EventTracker::Start(CLIENT(1), 0));
-    EXPECT_TRUE(mClient1->getJob(0, srcPath0, dstPath0));
+    EXPECT_TRUE(mClient1->getSession(0, srcPath0, dstPath0));
 
-    // Test cancel job immediately, getJob should fail after cancel.
+    // Test cancel session immediately, getSession should fail after cancel.
     EXPECT_TRUE(mClient1->cancel(0));
-    EXPECT_TRUE(mClient1->getJob<fail>(0, "", ""));
+    EXPECT_TRUE(mClient1->getSession<fail>(0, "", ""));
 
-    // Submit new job, new job should start immediately and finish.
-    EXPECT_TRUE(mClient1->submit(1, srcPath1, dstPath1, TranscodingJobPriority::kNormal, kBitRate));
+    // Submit new session, new session should start immediately and finish.
+    EXPECT_TRUE(
+            mClient1->submit(1, srcPath1, dstPath1, TranscodingSessionPriority::kNormal, kBitRate));
     EXPECT_EQ(mClient1->pop(kPaddingUs), EventTracker::Start(CLIENT(1), 1));
-    EXPECT_EQ(mClient1->pop(kJobWithPaddingUs), EventTracker::Finished(CLIENT(1), 1));
+    EXPECT_EQ(mClient1->pop(kSessionWithPaddingUs), EventTracker::Finished(CLIENT(1), 1));
 
     unregisterMultipleClients();
 }
@@ -163,26 +166,28 @@
 
     const char* srcPath0 = kLongSrcPath;
     const char* srcPath1 = kShortSrcPath;
-    const char* dstPath0 = OUTPATH(TestCancelWhileRunning_Job0);
-    const char* dstPath1 = OUTPATH(TestCancelWhileRunning_Job1);
+    const char* dstPath0 = OUTPATH(TestCancelWhileRunning_Session0);
+    const char* dstPath1 = OUTPATH(TestCancelWhileRunning_Session1);
 
     deleteFile(dstPath0);
     deleteFile(dstPath1);
-    // Submit two jobs, job 0 should start immediately, job 1 should be queued.
-    EXPECT_TRUE(mClient1->submit(0, srcPath0, dstPath0, TranscodingJobPriority::kNormal, kBitRate));
-    EXPECT_TRUE(mClient1->submit(1, srcPath1, dstPath1, TranscodingJobPriority::kNormal, kBitRate));
+    // Submit two sessions, session 0 should start immediately, session 1 should be queued.
+    EXPECT_TRUE(
+            mClient1->submit(0, srcPath0, dstPath0, TranscodingSessionPriority::kNormal, kBitRate));
+    EXPECT_TRUE(
+            mClient1->submit(1, srcPath1, dstPath1, TranscodingSessionPriority::kNormal, kBitRate));
     EXPECT_EQ(mClient1->pop(kPaddingUs), EventTracker::Start(CLIENT(1), 0));
-    EXPECT_TRUE(mClient1->getJob(0, srcPath0, dstPath0));
-    EXPECT_TRUE(mClient1->getJob(1, srcPath1, dstPath1));
+    EXPECT_TRUE(mClient1->getSession(0, srcPath0, dstPath0));
+    EXPECT_TRUE(mClient1->getSession(1, srcPath1, dstPath1));
 
-    // Job 0 (longtest) shouldn't finish in 1 seconds.
+    // Session 0 (longtest) shouldn't finish in 1 seconds.
     EXPECT_EQ(mClient1->pop(1000000), EventTracker::NoEvent);
 
-    // Now cancel job 0. Job 1 should start immediately and finish.
+    // Now cancel session 0. Session 1 should start immediately and finish.
     EXPECT_TRUE(mClient1->cancel(0));
-    EXPECT_TRUE(mClient1->getJob<fail>(0, "", ""));
+    EXPECT_TRUE(mClient1->getSession<fail>(0, "", ""));
     EXPECT_EQ(mClient1->pop(kPaddingUs), EventTracker::Start(CLIENT(1), 1));
-    EXPECT_EQ(mClient1->pop(kJobWithPaddingUs), EventTracker::Finished(CLIENT(1), 1));
+    EXPECT_EQ(mClient1->pop(kSessionWithPaddingUs), EventTracker::Finished(CLIENT(1), 1));
 
     unregisterMultipleClients();
 }
@@ -192,48 +197,49 @@
 
     const char* srcPath0 = kLongSrcPath;
     const char* srcPath1 = kShortSrcPath;
-    const char* dstPath0 = OUTPATH(TestPauseResumeSingleClient_Job0);
-    const char* dstPath1 = OUTPATH(TestPauseResumeSingleClient_Job1);
+    const char* dstPath0 = OUTPATH(TestPauseResumeSingleClient_Session0);
+    const char* dstPath1 = OUTPATH(TestPauseResumeSingleClient_Session1);
     deleteFile(dstPath0);
     deleteFile(dstPath1);
 
-    // Submit one offline job, should start immediately.
-    EXPECT_TRUE(mClient1->submit(0, srcPath0, dstPath0, TranscodingJobPriority::kUnspecified,
+    // Submit one offline session, should start immediately.
+    EXPECT_TRUE(mClient1->submit(0, srcPath0, dstPath0, TranscodingSessionPriority::kUnspecified,
                                  kBitRate));
     EXPECT_EQ(mClient1->pop(kPaddingUs), EventTracker::Start(CLIENT(1), 0));
-    // Test get job after starts.
-    EXPECT_TRUE(mClient1->getJob(0, srcPath0, dstPath0));
+    // Test get session after starts.
+    EXPECT_TRUE(mClient1->getSession(0, srcPath0, dstPath0));
 
-    // Submit one realtime job.
-    EXPECT_TRUE(mClient1->submit(1, srcPath1, dstPath1, TranscodingJobPriority::kNormal, kBitRate));
+    // Submit one realtime session.
+    EXPECT_TRUE(
+            mClient1->submit(1, srcPath1, dstPath1, TranscodingSessionPriority::kNormal, kBitRate));
 
-    // Offline job should pause.
+    // Offline session should pause.
     EXPECT_EQ(mClient1->pop(kPaddingUs), EventTracker::Pause(CLIENT(1), 0));
-    EXPECT_TRUE(mClient1->getJob(0, srcPath0, dstPath0));
+    EXPECT_TRUE(mClient1->getSession(0, srcPath0, dstPath0));
 
-    // Realtime job should start immediately, and run to finish.
+    // Realtime session should start immediately, and run to finish.
     EXPECT_EQ(mClient1->pop(kPaddingUs), EventTracker::Start(CLIENT(1), 1));
-    EXPECT_EQ(mClient1->pop(kJobWithPaddingUs), EventTracker::Finished(CLIENT(1), 1));
+    EXPECT_EQ(mClient1->pop(kSessionWithPaddingUs), EventTracker::Finished(CLIENT(1), 1));
 
-    // Test get job after finish fails.
-    EXPECT_TRUE(mClient1->getJob<fail>(1, "", ""));
+    // Test get session after finish fails.
+    EXPECT_TRUE(mClient1->getSession<fail>(1, "", ""));
 
-    // Then offline job should resume.
+    // Then offline session should resume.
     EXPECT_EQ(mClient1->pop(kPaddingUs), EventTracker::Resume(CLIENT(1), 0));
-    // Test get job after resume.
-    EXPECT_TRUE(mClient1->getJob(0, srcPath0, dstPath0));
+    // Test get session after resume.
+    EXPECT_TRUE(mClient1->getSession(0, srcPath0, dstPath0));
 
-    // Offline job should finish.
-    EXPECT_EQ(mClient1->pop(kJobWithPaddingUs), EventTracker::Finished(CLIENT(1), 0));
-    // Test get job after finish fails.
-    EXPECT_TRUE(mClient1->getJob<fail>(0, "", ""));
+    // Offline session should finish.
+    EXPECT_EQ(mClient1->pop(kSessionWithPaddingUs), EventTracker::Finished(CLIENT(1), 0));
+    // Test get session after finish fails.
+    EXPECT_TRUE(mClient1->getSession<fail>(0, "", ""));
 
     unregisterMultipleClients();
 }
 
 /*
- * Basic test for pause/resume with two clients, with one job each.
- * Top app's job should preempt the other app's job.
+ * Basic test for pause/resume with two clients, with one session each.
+ * Top app's session should preempt the other app's session.
  */
 TEST_F(MediaTranscodingServiceRealTest, TestPauseResumeMultiClients) {
     ALOGD("TestPauseResumeMultiClients starting...");
@@ -256,33 +262,35 @@
     ALOGD("Moving app A to top...");
     EXPECT_TRUE(ShellHelper::Start(kClientPackageA, kTestActivityName));
 
-    // Submit job to Client1.
-    ALOGD("Submitting job to client1 (app A) ...");
-    EXPECT_TRUE(mClient1->submit(0, srcPath0, dstPath0, TranscodingJobPriority::kNormal, kBitRate));
+    // Submit session to Client1.
+    ALOGD("Submitting session to client1 (app A) ...");
+    EXPECT_TRUE(
+            mClient1->submit(0, srcPath0, dstPath0, TranscodingSessionPriority::kNormal, kBitRate));
 
-    // Client1's job should start immediately.
+    // Client1's session should start immediately.
     EXPECT_EQ(mClient1->pop(kPaddingUs), EventTracker::Start(CLIENT(1), 0));
 
     ALOGD("Moving app B to top...");
     EXPECT_TRUE(ShellHelper::Start(kClientPackageB, kTestActivityName));
 
-    // Client1's job should continue to run, since Client2 (app B) doesn't have any job.
+    // Client1's session should continue to run, since Client2 (app B) doesn't have any session.
     EXPECT_EQ(mClient1->pop(1000000), EventTracker::NoEvent);
 
-    // Submit job to Client2.
-    ALOGD("Submitting job to client2 (app B) ...");
-    EXPECT_TRUE(mClient2->submit(0, srcPath1, dstPath1, TranscodingJobPriority::kNormal, kBitRate));
+    // Submit session to Client2.
+    ALOGD("Submitting session to client2 (app B) ...");
+    EXPECT_TRUE(
+            mClient2->submit(0, srcPath1, dstPath1, TranscodingSessionPriority::kNormal, kBitRate));
 
-    // Client1's job should pause, client2's job should start.
+    // Client1's session should pause, client2's session should start.
     EXPECT_EQ(mClient1->pop(kPaddingUs), EventTracker::Pause(CLIENT(1), 0));
     EXPECT_EQ(mClient2->pop(kPaddingUs), EventTracker::Start(CLIENT(2), 0));
 
-    // Client2's job should finish, then Client1's job should resume.
-    EXPECT_EQ(mClient2->pop(kJobWithPaddingUs), EventTracker::Finished(CLIENT(2), 0));
+    // Client2's session should finish, then Client1's session should resume.
+    EXPECT_EQ(mClient2->pop(kSessionWithPaddingUs), EventTracker::Finished(CLIENT(2), 0));
     EXPECT_EQ(mClient1->pop(kPaddingUs), EventTracker::Resume(CLIENT(1), 0));
 
-    // Client1's job should finish.
-    EXPECT_EQ(mClient1->pop(kJobWithPaddingUs), EventTracker::Finished(CLIENT(1), 0));
+    // Client1's session should finish.
+    EXPECT_EQ(mClient1->pop(kSessionWithPaddingUs), EventTracker::Finished(CLIENT(1), 0));
 
     unregisterMultipleClients();
 
diff --git a/services/mediatranscoding/tests/mediatranscodingservice_resource_tests.cpp b/services/mediatranscoding/tests/mediatranscodingservice_resource_tests.cpp
index 31697d5..bf99efc 100644
--- a/services/mediatranscoding/tests/mediatranscodingservice_resource_tests.cpp
+++ b/services/mediatranscoding/tests/mediatranscodingservice_resource_tests.cpp
@@ -55,13 +55,13 @@
 /**
  * Basic testing for handling resource lost.
  *
- * This test starts a transcoding job (that's somewhat long and takes several seconds),
+ * This test starts a transcoding session (that's somewhat long and takes several seconds),
  * then launches an activity that allocates video codec instances until it hits insufficient
  * resource error. Because the activity is running in foreground,
  * ResourceManager would reclaim codecs from transcoding service which should
- * cause the job to be paused. The activity will hold the codecs for a few seconds
+ * cause the session to be paused. The activity will hold the codecs for a few seconds
  * before releasing them, and the transcoding service should be able to resume
- * and complete the job.
+ * and complete the session.
  */
 TEST_F(MediaTranscodingServiceResourceTest, TestResourceLost) {
     ALOGD("TestResourceLost starting...");
@@ -79,21 +79,22 @@
     ALOGD("Moving app A to top...");
     EXPECT_TRUE(ShellHelper::Start(kClientPackageA, kTestActivityName));
 
-    // Submit job to Client1.
-    ALOGD("Submitting job to client1 (app A) ...");
-    EXPECT_TRUE(mClient1->submit(0, srcPath0, dstPath0, TranscodingJobPriority::kNormal, kBitRate));
+    // Submit session to Client1.
+    ALOGD("Submitting session to client1 (app A) ...");
+    EXPECT_TRUE(
+            mClient1->submit(0, srcPath0, dstPath0, TranscodingSessionPriority::kNormal, kBitRate));
 
-    // Client1's job should start immediately.
+    // Client1's session should start immediately.
     EXPECT_EQ(mClient1->pop(kPaddingUs), EventTracker::Start(CLIENT(1), 0));
 
     // Launch ResourcePolicyTestActivity, which will try to allocate up to 32
     // instances, which should trigger insufficient resources on most devices.
     // (Note that it's possible that the device supports a very high number of
-    // resource instances, in which case we'll simply require that the job completes.)
+    // resource instances, in which case we'll simply require that the session completes.)
     ALOGD("Launch ResourcePolicyTestActivity...");
     EXPECT_TRUE(ShellHelper::Start(kClientPackageA, kResourcePolicyTestActivity));
 
-    // The basic requirement is that the job should complete. Wait for finish
+    // The basic requirement is that the session should complete. Wait for finish
     // event to come and pop up all events received.
     std::list<EventTracker::Event> events;
     EXPECT_TRUE(mClient1->waitForSpecificEventAndPop(EventTracker::Finished(CLIENT(1), 0), &events,
diff --git a/services/mediatranscoding/tests/mediatranscodingservice_simulated_tests.cpp b/services/mediatranscoding/tests/mediatranscodingservice_simulated_tests.cpp
index 789b493..7dfda44 100644
--- a/services/mediatranscoding/tests/mediatranscodingservice_simulated_tests.cpp
+++ b/services/mediatranscoding/tests/mediatranscodingservice_simulated_tests.cpp
@@ -23,9 +23,9 @@
 #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 <aidl/android/media/TranscodingSessionParcel.h>
+#include <aidl/android/media/TranscodingSessionPriority.h>
 #include <android-base/logging.h>
 #include <android/binder_manager.h>
 #include <android/binder_process.h>
@@ -44,15 +44,15 @@
 
 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.
+// 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 int32_t kInvalidClientUid = -10;
 constexpr const char* kInvalidClientName = "";
 constexpr const char* kInvalidClientOpPackageName = "";
 
 constexpr int64_t kPaddingUs = 1000000;
-constexpr int64_t kJobWithPaddingUs = SimulatedTranscoder::kJobDurationUs + kPaddingUs;
+constexpr int64_t kSessionWithPaddingUs = SimulatedTranscoder::kSessionDurationUs + kPaddingUs;
 
 constexpr const char* kClientOpPackageName = "TestClientPackage";
 
@@ -135,18 +135,18 @@
     unregisterMultipleClients();
 }
 
-TEST_F(MediaTranscodingServiceSimulatedTest, TestJobIdIndependence) {
+TEST_F(MediaTranscodingServiceSimulatedTest, TestSessionIdIndependence) {
     registerMultipleClients();
 
     // Submit 2 requests on client1 first.
     EXPECT_TRUE(mClient1->submit(0, "test_source_file", "test_destination_file"));
     EXPECT_TRUE(mClient1->submit(1, "test_source_file", "test_destination_file"));
 
-    // Submit 2 requests on client2, jobId should be independent for each client.
+    // Submit 2 requests on client2, sessionId should be independent for each client.
     EXPECT_TRUE(mClient2->submit(0, "test_source_file", "test_destination_file"));
     EXPECT_TRUE(mClient2->submit(1, "test_source_file", "test_destination_file"));
 
-    // Cancel all jobs.
+    // Cancel all sessions.
     EXPECT_TRUE(mClient1->cancel(0));
     EXPECT_TRUE(mClient1->cancel(1));
     EXPECT_TRUE(mClient2->cancel(0));
@@ -155,10 +155,10 @@
     unregisterMultipleClients();
 }
 
-TEST_F(MediaTranscodingServiceSimulatedTest, TestSubmitCancelJobs) {
+TEST_F(MediaTranscodingServiceSimulatedTest, TestSubmitCancelSessions) {
     registerMultipleClients();
 
-    // Test jobId assignment.
+    // Test sessionId assignment.
     EXPECT_TRUE(mClient1->submit(0, "test_source_file_0", "test_destination_file"));
     EXPECT_TRUE(mClient1->submit(1, "test_source_file_1", "test_destination_file"));
     EXPECT_TRUE(mClient1->submit(2, "test_source_file_2", "test_destination_file"));
@@ -167,33 +167,33 @@
     EXPECT_TRUE(mClient1->submit<fail>(0, "", ""));
 
     // Test submit bad request (no valid sourceFilePath) fails.
-    EXPECT_TRUE(mClient1->submit<fail>(0, "src", "dst", TranscodingJobPriority::kNormal, 1000000,
-                                       kInvalidClientPid, kInvalidClientUid));
+    EXPECT_TRUE(mClient1->submit<fail>(0, "src", "dst", TranscodingSessionPriority::kNormal,
+                                       1000000, kInvalidClientPid, kInvalidClientUid));
 
-    // Test cancel non-existent job fails.
+    // Test cancel non-existent session fails.
     EXPECT_TRUE(mClient1->cancel<fail>(100));
 
-    // Job 0 should start immediately and finish in 2 seconds, followed by Job 1 start.
+    // Session 0 should start immediately and finish in 2 seconds, followed by Session 1 start.
     EXPECT_EQ(mClient1->pop(kPaddingUs), EventTracker::Start(CLIENT(1), 0));
-    EXPECT_EQ(mClient1->pop(kJobWithPaddingUs), EventTracker::Finished(CLIENT(1), 0));
+    EXPECT_EQ(mClient1->pop(kSessionWithPaddingUs), EventTracker::Finished(CLIENT(1), 0));
     EXPECT_EQ(mClient1->pop(kPaddingUs), EventTracker::Start(CLIENT(1), 1));
 
-    // Test cancel valid jobId in random order.
-    // Test cancel finished job fails.
+    // Test cancel valid sessionId in random order.
+    // Test cancel finished session fails.
     EXPECT_TRUE(mClient1->cancel(2));
     EXPECT_TRUE(mClient1->cancel<fail>(0));
     EXPECT_TRUE(mClient1->cancel(1));
 
-    // Test cancel job again fails.
+    // Test cancel session again fails.
     EXPECT_TRUE(mClient1->cancel<fail>(1));
 
     // Test no more events arriving after cancel.
-    EXPECT_EQ(mClient1->pop(kJobWithPaddingUs), EventTracker::NoEvent);
+    EXPECT_EQ(mClient1->pop(kSessionWithPaddingUs), EventTracker::NoEvent);
 
     unregisterMultipleClients();
 }
 
-TEST_F(MediaTranscodingServiceSimulatedTest, TestGetJobs) {
+TEST_F(MediaTranscodingServiceSimulatedTest, TestGetSessions) {
     registerMultipleClients();
 
     // Submit 3 requests.
@@ -201,69 +201,69 @@
     EXPECT_TRUE(mClient1->submit(1, "test_source_file_1", "test_destination_file_1"));
     EXPECT_TRUE(mClient1->submit(2, "test_source_file_2", "test_destination_file_2"));
 
-    // Test get jobs by id.
-    EXPECT_TRUE(mClient1->getJob(2, "test_source_file_2", "test_destination_file_2"));
-    EXPECT_TRUE(mClient1->getJob(1, "test_source_file_1", "test_destination_file_1"));
-    EXPECT_TRUE(mClient1->getJob(0, "test_source_file_0", "test_destination_file_0"));
+    // Test get sessions by id.
+    EXPECT_TRUE(mClient1->getSession(2, "test_source_file_2", "test_destination_file_2"));
+    EXPECT_TRUE(mClient1->getSession(1, "test_source_file_1", "test_destination_file_1"));
+    EXPECT_TRUE(mClient1->getSession(0, "test_source_file_0", "test_destination_file_0"));
 
-    // Test get job by invalid id fails.
-    EXPECT_TRUE(mClient1->getJob<fail>(100, "", ""));
-    EXPECT_TRUE(mClient1->getJob<fail>(-1, "", ""));
+    // Test get session by invalid id fails.
+    EXPECT_TRUE(mClient1->getSession<fail>(100, "", ""));
+    EXPECT_TRUE(mClient1->getSession<fail>(-1, "", ""));
 
-    // Test get job after cancel fails.
+    // Test get session after cancel fails.
     EXPECT_TRUE(mClient1->cancel(2));
-    EXPECT_TRUE(mClient1->getJob<fail>(2, "", ""));
+    EXPECT_TRUE(mClient1->getSession<fail>(2, "", ""));
 
-    // Job 0 should start immediately and finish in 2 seconds, followed by Job 1 start.
+    // Session 0 should start immediately and finish in 2 seconds, followed by Session 1 start.
     EXPECT_EQ(mClient1->pop(kPaddingUs), EventTracker::Start(CLIENT(1), 0));
-    EXPECT_EQ(mClient1->pop(kJobWithPaddingUs), EventTracker::Finished(CLIENT(1), 0));
+    EXPECT_EQ(mClient1->pop(kSessionWithPaddingUs), EventTracker::Finished(CLIENT(1), 0));
     EXPECT_EQ(mClient1->pop(kPaddingUs), EventTracker::Start(CLIENT(1), 1));
 
-    // Test get job after finish fails.
-    EXPECT_TRUE(mClient1->getJob<fail>(0, "", ""));
+    // Test get session after finish fails.
+    EXPECT_TRUE(mClient1->getSession<fail>(0, "", ""));
 
-    // Test get the remaining job 1.
-    EXPECT_TRUE(mClient1->getJob(1, "test_source_file_1", "test_destination_file_1"));
+    // Test get the remaining session 1.
+    EXPECT_TRUE(mClient1->getSession(1, "test_source_file_1", "test_destination_file_1"));
 
-    // Cancel remaining job 1.
+    // Cancel remaining session 1.
     EXPECT_TRUE(mClient1->cancel(1));
 
     unregisterMultipleClients();
 }
 
-TEST_F(MediaTranscodingServiceSimulatedTest, TestSubmitCancelWithOfflineJobs) {
+TEST_F(MediaTranscodingServiceSimulatedTest, TestSubmitCancelWithOfflineSessions) {
     registerMultipleClients();
 
-    // Submit some offline jobs first.
+    // Submit some offline sessions first.
     EXPECT_TRUE(mClient1->submit(0, "test_source_file_0", "test_destination_file_0",
-                                 TranscodingJobPriority::kUnspecified));
+                                 TranscodingSessionPriority::kUnspecified));
     EXPECT_TRUE(mClient1->submit(1, "test_source_file_1", "test_destination_file_1",
-                                 TranscodingJobPriority::kUnspecified));
+                                 TranscodingSessionPriority::kUnspecified));
 
-    // Job 0 should start immediately.
+    // Session 0 should start immediately.
     EXPECT_EQ(mClient1->pop(kPaddingUs), EventTracker::Start(CLIENT(1), 0));
 
-    // Submit more real-time jobs.
+    // Submit more real-time sessions.
     EXPECT_TRUE(mClient1->submit(2, "test_source_file_2", "test_destination_file_2"));
     EXPECT_TRUE(mClient1->submit(3, "test_source_file_3", "test_destination_file_3"));
 
-    // Job 0 should pause immediately and job 2 should start.
+    // Session 0 should pause immediately and session 2 should start.
     EXPECT_EQ(mClient1->pop(kPaddingUs), EventTracker::Pause(CLIENT(1), 0));
     EXPECT_EQ(mClient1->pop(kPaddingUs), EventTracker::Start(CLIENT(1), 2));
 
-    // Job 2 should finish in 2 seconds and job 3 should start.
-    EXPECT_EQ(mClient1->pop(kJobWithPaddingUs), EventTracker::Finished(CLIENT(1), 2));
+    // Session 2 should finish in 2 seconds and session 3 should start.
+    EXPECT_EQ(mClient1->pop(kSessionWithPaddingUs), EventTracker::Finished(CLIENT(1), 2));
     EXPECT_EQ(mClient1->pop(kPaddingUs), EventTracker::Start(CLIENT(1), 3));
 
-    // Cancel job 3 now
+    // Cancel session 3 now
     EXPECT_TRUE(mClient1->cancel(3));
 
-    // Job 0 should resume and finish in 2 seconds, followed by job 1 start.
+    // Session 0 should resume and finish in 2 seconds, followed by session 1 start.
     EXPECT_EQ(mClient1->pop(kPaddingUs), EventTracker::Resume(CLIENT(1), 0));
-    EXPECT_EQ(mClient1->pop(kJobWithPaddingUs), EventTracker::Finished(CLIENT(1), 0));
+    EXPECT_EQ(mClient1->pop(kSessionWithPaddingUs), EventTracker::Finished(CLIENT(1), 0));
     EXPECT_EQ(mClient1->pop(kPaddingUs), EventTracker::Start(CLIENT(1), 1));
 
-    // Cancel remaining job 1.
+    // Cancel remaining session 1.
     EXPECT_TRUE(mClient1->cancel(1));
 
     unregisterMultipleClients();
@@ -280,16 +280,16 @@
     EXPECT_TRUE(status.isOk());
 
     // Test various operations on the client, should fail with ERROR_DISCONNECTED.
-    TranscodingJobParcel job;
+    TranscodingSessionParcel session;
     bool result;
-    status = client->getJobWithId(0, &job, &result);
+    status = client->getSessionWithId(0, &session, &result);
     EXPECT_EQ(status.getServiceSpecificError(), IMediaTranscodingService::ERROR_DISCONNECTED);
 
-    status = client->cancelJob(0, &result);
+    status = client->cancelSession(0, &result);
     EXPECT_EQ(status.getServiceSpecificError(), IMediaTranscodingService::ERROR_DISCONNECTED);
 
     TranscodingRequestParcel request;
-    status = client->submitRequest(request, &job, &result);
+    status = client->submitRequest(request, &session, &result);
     EXPECT_EQ(status.getServiceSpecificError(), IMediaTranscodingService::ERROR_DISCONNECTED);
 }
 
@@ -308,41 +308,41 @@
     EXPECT_TRUE(ShellHelper::Start(kClientPackageA, kTestActivityName));
 
     // Submit 3 requests.
-    ALOGD("Submitting job to client1 (app A) ...");
+    ALOGD("Submitting session to client1 (app A) ...");
     EXPECT_TRUE(mClient1->submit(0, "test_source_file_0", "test_destination_file_0"));
     EXPECT_TRUE(mClient1->submit(1, "test_source_file_1", "test_destination_file_1"));
     EXPECT_TRUE(mClient1->submit(2, "test_source_file_2", "test_destination_file_2"));
 
-    // Job 0 should start immediately.
+    // Session 0 should start immediately.
     EXPECT_EQ(mClient1->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(mClient1->pop(kJobWithPaddingUs), EventTracker::Finished(CLIENT(1), 0));
+    // Session 0 should continue and finish in 2 seconds, then session 1 should start.
+    EXPECT_EQ(mClient1->pop(kSessionWithPaddingUs), EventTracker::Finished(CLIENT(1), 0));
     EXPECT_EQ(mClient1->pop(kPaddingUs), EventTracker::Start(CLIENT(1), 1));
 
-    ALOGD("Submitting job to client2 (app B) ...");
+    ALOGD("Submitting session to client2 (app B) ...");
     EXPECT_TRUE(mClient2->submit(0, "test_source_file_0", "test_destination_file_0"));
 
-    // Client1's job should pause, client2's job should start.
+    // Client1's session should pause, client2's session should start.
     EXPECT_EQ(mClient1->pop(kPaddingUs), EventTracker::Pause(CLIENT(1), 1));
     EXPECT_EQ(mClient2->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.
+    // Client2's session should pause, client1's session 1 should resume.
     EXPECT_EQ(mClient2->pop(kPaddingUs), EventTracker::Pause(CLIENT(2), 0));
     EXPECT_EQ(mClient1->pop(kPaddingUs), EventTracker::Resume(CLIENT(1), 1));
 
-    // Client2's job 1 should finish in 2 seconds, then its job 2 should start.
-    EXPECT_EQ(mClient1->pop(kJobWithPaddingUs), EventTracker::Finished(CLIENT(1), 1));
+    // Client2's session 1 should finish in 2 seconds, then its session 2 should start.
+    EXPECT_EQ(mClient1->pop(kSessionWithPaddingUs), EventTracker::Finished(CLIENT(1), 1));
     EXPECT_EQ(mClient1->pop(kPaddingUs), EventTracker::Start(CLIENT(1), 2));
 
-    // After client2's jobs finish, client1's job should resume.
-    EXPECT_EQ(mClient1->pop(kJobWithPaddingUs), EventTracker::Finished(CLIENT(1), 2));
+    // After client2's sessions finish, client1's session should resume.
+    EXPECT_EQ(mClient1->pop(kSessionWithPaddingUs), EventTracker::Finished(CLIENT(1), 2));
     EXPECT_EQ(mClient2->pop(kPaddingUs), EventTracker::Resume(CLIENT(2), 0));
 
     unregisterMultipleClients();
diff --git a/services/oboeservice/AAudioClientTracker.cpp b/services/oboeservice/AAudioClientTracker.cpp
index 3ec8dea..054a896 100644
--- a/services/oboeservice/AAudioClientTracker.cpp
+++ b/services/oboeservice/AAudioClientTracker.cpp
@@ -41,7 +41,7 @@
         : Singleton<AAudioClientTracker>() {
 }
 
-std::string AAudioClientTracker::dump() const {
+std::string AAudioClientTracker::dump() const NO_THREAD_SAFETY_ANALYSIS {
     std::stringstream result;
     const bool isLocked = AAudio_tryUntilTrue(
             [this]()->bool { return mLock.try_lock(); } /* f */,
@@ -207,7 +207,7 @@
 }
 
 
-std::string AAudioClientTracker::NotificationClient::dump() const {
+std::string AAudioClientTracker::NotificationClient::dump() const NO_THREAD_SAFETY_ANALYSIS {
     std::stringstream result;
     const bool isLocked = AAudio_tryUntilTrue(
             [this]()->bool { return mLock.try_lock(); } /* f */,
diff --git a/services/oboeservice/AAudioClientTracker.h b/services/oboeservice/AAudioClientTracker.h
index facfc3b..2b38621 100644
--- a/services/oboeservice/AAudioClientTracker.h
+++ b/services/oboeservice/AAudioClientTracker.h
@@ -21,6 +21,7 @@
 #include <mutex>
 #include <set>
 
+#include <android-base/thread_annotations.h>
 #include <utils/Singleton.h>
 
 #include <aaudio/AAudio.h>
@@ -114,10 +115,12 @@
     };
 
     // This must be called under mLock
-    android::sp<NotificationClient> getNotificationClient_l(pid_t pid);
+    android::sp<NotificationClient> getNotificationClient_l(pid_t pid)
+            REQUIRES(mLock);
 
     mutable std::mutex                               mLock;
-    std::map<pid_t, android::sp<NotificationClient>> mNotificationClients;
+    std::map<pid_t, android::sp<NotificationClient>> mNotificationClients
+            GUARDED_BY(mLock);
     android::AAudioService                          *mAAudioService = nullptr;
 };
 
diff --git a/services/oboeservice/AAudioEndpointManager.cpp b/services/oboeservice/AAudioEndpointManager.cpp
index 9f34153..407f6d5 100644
--- a/services/oboeservice/AAudioEndpointManager.cpp
+++ b/services/oboeservice/AAudioEndpointManager.cpp
@@ -43,7 +43,7 @@
         , mExclusiveStreams() {
 }
 
-std::string AAudioEndpointManager::dump() const {
+std::string AAudioEndpointManager::dump() const NO_THREAD_SAFETY_ANALYSIS {
     std::stringstream result;
     int index = 0;
 
@@ -306,6 +306,7 @@
                 mSharedStreams.end());
 
         serviceEndpoint->close();
+
         mSharedCloseCount++;
         ALOGV("%s(%p) closed for device %d",
               __func__, serviceEndpoint.get(), serviceEndpoint->getDeviceId());
diff --git a/services/oboeservice/AAudioEndpointManager.h b/services/oboeservice/AAudioEndpointManager.h
index ae776b1..b07bcef 100644
--- a/services/oboeservice/AAudioEndpointManager.h
+++ b/services/oboeservice/AAudioEndpointManager.h
@@ -20,6 +20,8 @@
 #include <map>
 #include <mutex>
 #include <sys/types.h>
+
+#include <android-base/thread_annotations.h>
 #include <utils/Singleton.h>
 
 #include "binding/AAudioServiceMessage.h"
@@ -70,10 +72,12 @@
                                               const aaudio::AAudioStreamRequest &request);
 
     android::sp<AAudioServiceEndpoint> findExclusiveEndpoint_l(
-            const AAudioStreamConfiguration& configuration);
+            const AAudioStreamConfiguration& configuration)
+            REQUIRES(mExclusiveLock);
 
     android::sp<AAudioServiceEndpointShared> findSharedEndpoint_l(
-            const AAudioStreamConfiguration& configuration);
+            const AAudioStreamConfiguration& configuration)
+            REQUIRES(mSharedLock);
 
     void closeExclusiveEndpoint(android::sp<AAudioServiceEndpoint> serviceEndpoint);
     void closeSharedEndpoint(android::sp<AAudioServiceEndpoint> serviceEndpoint);
@@ -83,23 +87,25 @@
     // Lock mSharedLock before mExclusiveLock.
     // it is OK to only lock mExclusiveLock.
     mutable std::mutex                                     mSharedLock;
-    std::vector<android::sp<AAudioServiceEndpointShared>>  mSharedStreams;
+    std::vector<android::sp<AAudioServiceEndpointShared>>  mSharedStreams
+            GUARDED_BY(mSharedLock);
 
     mutable std::mutex                                     mExclusiveLock;
-    std::vector<android::sp<AAudioServiceEndpointMMAP>>    mExclusiveStreams;
+    std::vector<android::sp<AAudioServiceEndpointMMAP>>    mExclusiveStreams
+            GUARDED_BY(mExclusiveLock);
 
-    // Modified under a lock.
-    int32_t mExclusiveSearchCount = 0; // number of times we SEARCHED for an exclusive endpoint
-    int32_t mExclusiveFoundCount  = 0; // number of times we FOUND an exclusive endpoint
-    int32_t mExclusiveOpenCount   = 0; // number of times we OPENED an exclusive endpoint
-    int32_t mExclusiveCloseCount  = 0; // number of times we CLOSED an exclusive endpoint
-    int32_t mExclusiveStolenCount = 0; // number of times we STOLE an exclusive endpoint
+    // Counts related to an exclusive endpoint.
+    int32_t mExclusiveSearchCount GUARDED_BY(mExclusiveLock) = 0; // # SEARCHED
+    int32_t mExclusiveFoundCount  GUARDED_BY(mExclusiveLock) = 0; // # FOUND
+    int32_t mExclusiveOpenCount   GUARDED_BY(mExclusiveLock) = 0; // # OPENED
+    int32_t mExclusiveCloseCount  GUARDED_BY(mExclusiveLock) = 0; // # CLOSED
+    int32_t mExclusiveStolenCount GUARDED_BY(mExclusiveLock) = 0; // # STOLEN
 
     // Same as above but for SHARED endpoints.
-    int32_t mSharedSearchCount    = 0;
-    int32_t mSharedFoundCount     = 0;
-    int32_t mSharedOpenCount      = 0;
-    int32_t mSharedCloseCount     = 0;
+    int32_t mSharedSearchCount    GUARDED_BY(mSharedLock) = 0;
+    int32_t mSharedFoundCount     GUARDED_BY(mSharedLock) = 0;
+    int32_t mSharedOpenCount      GUARDED_BY(mSharedLock) = 0;
+    int32_t mSharedCloseCount     GUARDED_BY(mSharedLock) = 0;
 
     // For easily disabling the stealing of exclusive streams.
     static constexpr bool kStealingEnabled = true;
diff --git a/services/oboeservice/AAudioServiceEndpoint.cpp b/services/oboeservice/AAudioServiceEndpoint.cpp
index b139be1..faea58f 100644
--- a/services/oboeservice/AAudioServiceEndpoint.cpp
+++ b/services/oboeservice/AAudioServiceEndpoint.cpp
@@ -38,7 +38,7 @@
 using namespace android;  // TODO just import names needed
 using namespace aaudio;   // TODO just import names needed
 
-std::string AAudioServiceEndpoint::dump() const {
+std::string AAudioServiceEndpoint::dump() const NO_THREAD_SAFETY_ANALYSIS {
     std::stringstream result;
 
     const bool isLocked = AAudio_tryUntilTrue(
diff --git a/services/oboeservice/AAudioServiceEndpoint.h b/services/oboeservice/AAudioServiceEndpoint.h
index 04b906a..72090c2 100644
--- a/services/oboeservice/AAudioServiceEndpoint.h
+++ b/services/oboeservice/AAudioServiceEndpoint.h
@@ -22,6 +22,8 @@
 #include <mutex>
 #include <vector>
 
+#include <android-base/thread_annotations.h>
+
 #include "client/AudioStreamInternal.h"
 #include "client/AudioStreamInternalPlay.h"
 #include "core/AAudioStreamParameters.h"
@@ -141,7 +143,8 @@
     std::vector<android::sp<AAudioServiceStreamBase>> disconnectRegisteredStreams();
 
     mutable std::mutex       mLockStreams;
-    std::vector<android::sp<AAudioServiceStreamBase>> mRegisteredStreams;
+    std::vector<android::sp<AAudioServiceStreamBase>> mRegisteredStreams
+            GUARDED_BY(mLockStreams);
 
     SimpleDoubleBuffer<Timestamp>  mAtomicEndpointTimestamp;
 
diff --git a/services/oboeservice/AAudioServiceEndpointCapture.cpp b/services/oboeservice/AAudioServiceEndpointCapture.cpp
index b86fe9d..bc769f0 100644
--- a/services/oboeservice/AAudioServiceEndpointCapture.cpp
+++ b/services/oboeservice/AAudioServiceEndpointCapture.cpp
@@ -65,7 +65,9 @@
         result = getStreamInternal()->read(mDistributionBuffer.get(),
                 getFramesPerBurst(), timeoutNanos);
         if (result == AAUDIO_ERROR_DISCONNECTED) {
-            ALOGV("%s() read() returned AAUDIO_ERROR_DISCONNECTED, break", __func__);
+            ALOGD("%s() read() returned AAUDIO_ERROR_DISCONNECTED", __func__);
+            // We do not need the returned vector.
+            (void) AAudioServiceEndpointShared::disconnectRegisteredStreams();
             break;
         } else if (result != getFramesPerBurst()) {
             ALOGW("callbackLoop() read %d / %d",
diff --git a/services/oboeservice/AAudioServiceEndpointPlay.cpp b/services/oboeservice/AAudioServiceEndpointPlay.cpp
index 53cb70b..4e46033 100644
--- a/services/oboeservice/AAudioServiceEndpointPlay.cpp
+++ b/services/oboeservice/AAudioServiceEndpointPlay.cpp
@@ -98,7 +98,7 @@
 
                 {
                     // Lock the AudioFifo to protect against close.
-                    std::lock_guard <std::mutex> lock(streamShared->getAudioDataQueueLock());
+                    std::lock_guard <std::mutex> lock(streamShared->audioDataQueueLock);
                     std::shared_ptr<SharedRingBuffer> audioDataQueue
                             = streamShared->getAudioDataQueue_l();
                     std::shared_ptr<FifoBuffer> fifo;
@@ -145,7 +145,9 @@
         result = getStreamInternal()->write(mMixer.getOutputBuffer(),
                                             getFramesPerBurst(), timeoutNanos);
         if (result == AAUDIO_ERROR_DISCONNECTED) {
-            ALOGV("%s() write() returned AAUDIO_ERROR_DISCONNECTED, break", __func__);
+            ALOGD("%s() write() returned AAUDIO_ERROR_DISCONNECTED", __func__);
+            // We do not need the returned vector.
+            (void) AAudioServiceEndpointShared::disconnectRegisteredStreams();
             break;
         } else if (result != getFramesPerBurst()) {
             ALOGW("callbackLoop() wrote %d / %d",
diff --git a/services/oboeservice/AAudioServiceEndpointShared.cpp b/services/oboeservice/AAudioServiceEndpointShared.cpp
index f5de59f..501e8c0 100644
--- a/services/oboeservice/AAudioServiceEndpointShared.cpp
+++ b/services/oboeservice/AAudioServiceEndpointShared.cpp
@@ -88,23 +88,30 @@
 }
 
 void AAudioServiceEndpointShared::close() {
-    getStreamInternal()->releaseCloseFinal();
+    stopSharingThread();
+    getStreamInternal()->safeReleaseClose();
 }
 
 // Glue between C and C++ callbacks.
 static void *aaudio_endpoint_thread_proc(void *arg) {
     assert(arg != nullptr);
+    ALOGD("%s() called", __func__);
 
-    // The caller passed in a smart pointer to prevent the endpoint from getting deleted
-    // while the thread was launching.
-    sp<AAudioServiceEndpointShared> *endpointForThread =
-            static_cast<sp<AAudioServiceEndpointShared> *>(arg);
-    sp<AAudioServiceEndpointShared> endpoint = *endpointForThread;
-    delete endpointForThread; // Just use scoped smart pointer. Don't need this anymore.
+    // Prevent the stream from being deleted while being used.
+    // This is just for extra safety. It is probably not needed because
+    // this callback should be joined before the stream is closed.
+    AAudioServiceEndpointShared *endpointPtr =
+        static_cast<AAudioServiceEndpointShared *>(arg);
+    android::sp<AAudioServiceEndpointShared> endpoint(endpointPtr);
+    // Balance the incStrong() in startSharingThread_l().
+    endpoint->decStrong(nullptr);
+
     void *result = endpoint->callbackLoop();
     // Close now so that the HW resource is freed and we can open a new device.
     if (!endpoint->isConnected()) {
-        endpoint->close();
+        ALOGD("%s() call safeReleaseCloseFromCallback()", __func__);
+        // Release and close under a lock with no check for callback collisions.
+        endpoint->getStreamInternal()->safeReleaseCloseFromCallback();
     }
 
     return result;
@@ -116,38 +123,39 @@
                           * AAUDIO_NANOS_PER_SECOND
                           / getSampleRate();
     mCallbackEnabled.store(true);
-    // Pass a smart pointer so the thread can hold a reference.
-    sp<AAudioServiceEndpointShared> *endpointForThread = new sp<AAudioServiceEndpointShared>(this);
-    aaudio_result_t result = getStreamInternal()->createThread(periodNanos,
-                                                               aaudio_endpoint_thread_proc,
-                                                               endpointForThread);
+    // Prevent this object from getting deleted before the thread has a chance to create
+    // its strong pointer. Assume the thread will call decStrong().
+    this->incStrong(nullptr);
+    aaudio_result_t result = getStreamInternal()->createThread_l(periodNanos,
+                                                                 aaudio_endpoint_thread_proc,
+                                                                 this);
     if (result != AAUDIO_OK) {
-        // The thread can't delete it so we have to do it here.
-        delete endpointForThread;
+        this->decStrong(nullptr); // Because the thread won't do it.
     }
     return result;
 }
 
 aaudio_result_t aaudio::AAudioServiceEndpointShared::stopSharingThread() {
     mCallbackEnabled.store(false);
-    aaudio_result_t result = getStreamInternal()->joinThread(NULL);
-    return result;
+    return getStreamInternal()->joinThread(NULL);
 }
 
-aaudio_result_t AAudioServiceEndpointShared::startStream(sp<AAudioServiceStreamBase> sharedStream,
-                                                         audio_port_handle_t *clientHandle) {
+aaudio_result_t AAudioServiceEndpointShared::startStream(
+        sp<AAudioServiceStreamBase> sharedStream,
+        audio_port_handle_t *clientHandle)
+        NO_THREAD_SAFETY_ANALYSIS {
     aaudio_result_t result = AAUDIO_OK;
 
     {
         std::lock_guard<std::mutex> lock(mLockStreams);
         if (++mRunningStreamCount == 1) { // atomic
-            result = getStreamInternal()->requestStart();
+            result = getStreamInternal()->systemStart();
             if (result != AAUDIO_OK) {
                 --mRunningStreamCount;
             } else {
                 result = startSharingThread_l();
                 if (result != AAUDIO_OK) {
-                    getStreamInternal()->requestStop();
+                    getStreamInternal()->systemStopFromApp();
                     --mRunningStreamCount;
                 }
             }
@@ -161,7 +169,7 @@
         if (result != AAUDIO_OK) {
             if (--mRunningStreamCount == 0) { // atomic
                 stopSharingThread();
-                getStreamInternal()->requestStop();
+                getStreamInternal()->systemStopFromApp();
             }
         }
     }
@@ -176,7 +184,7 @@
 
     if (--mRunningStreamCount == 0) { // atomic
         stopSharingThread(); // the sharing thread locks mLockStreams
-        getStreamInternal()->requestStop();
+        getStreamInternal()->systemStopFromApp();
     }
     return AAUDIO_OK;
 }
diff --git a/services/oboeservice/AAudioServiceEndpointShared.h b/services/oboeservice/AAudioServiceEndpointShared.h
index 020b926..8357567 100644
--- a/services/oboeservice/AAudioServiceEndpointShared.h
+++ b/services/oboeservice/AAudioServiceEndpointShared.h
@@ -20,6 +20,8 @@
 #include <atomic>
 #include <mutex>
 
+#include <android-base/thread_annotations.h>
+
 #include "AAudioServiceEndpoint.h"
 #include "client/AudioStreamInternal.h"
 #include "client/AudioStreamInternalPlay.h"
@@ -37,6 +39,8 @@
 public:
     explicit AAudioServiceEndpointShared(AudioStreamInternal *streamInternal);
 
+    virtual ~AAudioServiceEndpointShared() = default;
+
     std::string dump() const override;
 
     aaudio_result_t open(const aaudio::AAudioStreamRequest &request) override;
@@ -55,13 +59,13 @@
 
     virtual void   *callbackLoop() = 0;
 
-protected:
-
     AudioStreamInternal *getStreamInternal() const {
         return mStreamInternal.get();
     };
 
-    aaudio_result_t          startSharingThread_l();
+protected:
+
+    aaudio_result_t          startSharingThread_l() REQUIRES(mLockStreams);
 
     aaudio_result_t          stopSharingThread();
 
diff --git a/services/oboeservice/AAudioServiceStreamBase.cpp b/services/oboeservice/AAudioServiceStreamBase.cpp
index 9736091..7edc25c 100644
--- a/services/oboeservice/AAudioServiceStreamBase.cpp
+++ b/services/oboeservice/AAudioServiceStreamBase.cpp
@@ -595,14 +595,3 @@
 void AAudioServiceStreamBase::onVolumeChanged(float volume) {
     sendServiceEvent(AAUDIO_SERVICE_EVENT_VOLUME, volume);
 }
-
-int32_t AAudioServiceStreamBase::incrementServiceReferenceCount_l() {
-    return ++mCallingCount;
-}
-
-int32_t AAudioServiceStreamBase::decrementServiceReferenceCount_l() {
-    int32_t count = --mCallingCount;
-    // Each call to increment should be balanced with one call to decrement.
-    assert(count >= 0);
-    return count;
-}
diff --git a/services/oboeservice/AAudioServiceStreamBase.h b/services/oboeservice/AAudioServiceStreamBase.h
index f9efc2a..0f752b7 100644
--- a/services/oboeservice/AAudioServiceStreamBase.h
+++ b/services/oboeservice/AAudioServiceStreamBase.h
@@ -20,6 +20,7 @@
 #include <assert.h>
 #include <mutex>
 
+#include <android-base/thread_annotations.h>
 #include <media/AudioClient.h>
 #include <utils/RefBase.h>
 
@@ -209,25 +210,6 @@
         return mSuspended;
     }
 
-    /**
-     * Atomically increment the number of active references to the stream by AAudioService.
-     *
-     * This is called under a global lock in AAudioStreamTracker.
-     *
-     * @return value after the increment
-     */
-    int32_t incrementServiceReferenceCount_l();
-
-    /**
-     * Atomically decrement the number of active references to the stream by AAudioService.
-     * This should only be called after incrementServiceReferenceCount_l().
-     *
-     * This is called under a global lock in AAudioStreamTracker.
-     *
-     * @return value after the decrement
-     */
-    int32_t decrementServiceReferenceCount_l();
-
     bool isCloseNeeded() const {
         return mCloseNeeded.load();
     }
@@ -250,11 +232,10 @@
     aaudio_result_t open(const aaudio::AAudioStreamRequest &request,
                          aaudio_sharing_mode_t sharingMode);
 
-    // These must be called under mLock
-    virtual aaudio_result_t close_l();
-    virtual aaudio_result_t pause_l();
-    virtual aaudio_result_t stop_l();
-    void disconnect_l();
+    virtual aaudio_result_t close_l() REQUIRES(mLock);
+    virtual aaudio_result_t pause_l() REQUIRES(mLock);
+    virtual aaudio_result_t stop_l() REQUIRES(mLock);
+    void disconnect_l() REQUIRES(mLock);
 
     void setState(aaudio_stream_state_t state);
 
@@ -332,18 +313,17 @@
     aaudio_handle_t         mHandle = -1;
     bool                    mFlowing = false;
 
-    // This is modified under a global lock in AAudioStreamTracker.
-    int32_t                 mCallingCount = 0;
-
-    // This indicates that a stream that is being referenced by a binder call needs to closed.
-    std::atomic<bool>       mCloseNeeded{false};
+    // This indicates that a stream that is being referenced by a binder call
+    // and needs to closed.
+    std::atomic<bool>       mCloseNeeded{false}; // TODO remove
 
     // This indicate that a running stream should not be processed because of an error,
     // for example a full message queue. Note that this atomic is unrelated to mCloseNeeded.
     std::atomic<bool>       mSuspended{false};
 
+protected:
     // Locking order is important.
-    // Always acquire mLock before acquiring AAudioServiceEndpoint::mLockStreams
+    // Acquire mLock before acquiring AAudioServiceEndpoint::mLockStreams
     std::mutex              mLock; // Prevent start/stop/close etcetera from colliding
 };
 
diff --git a/services/oboeservice/AAudioServiceStreamMMAP.h b/services/oboeservice/AAudioServiceStreamMMAP.h
index 5902613..6ba1725 100644
--- a/services/oboeservice/AAudioServiceStreamMMAP.h
+++ b/services/oboeservice/AAudioServiceStreamMMAP.h
@@ -19,6 +19,7 @@
 
 #include <atomic>
 
+#include <android-base/thread_annotations.h>
 #include <android-base/unique_fd.h>
 #include <media/audiohal/StreamHalInterface.h>
 #include <media/MmapStreamCallback.h>
@@ -34,10 +35,8 @@
 #include "TimestampScheduler.h"
 #include "utility/MonotonicCounter.h"
 
-
 namespace aaudio {
 
-
 /**
  * These corresponds to an EXCLUSIVE mode MMAP client stream.
  * It has exclusive use of one AAudioServiceEndpointMMAP to communicate with the underlying
@@ -68,9 +67,9 @@
      * This is not guaranteed to be synchronous but it currently is.
      * An AAUDIO_SERVICE_EVENT_PAUSED will be sent to the client when complete.
     */
-    aaudio_result_t pause_l() override;
+    aaudio_result_t pause_l() REQUIRES(mLock) override;
 
-    aaudio_result_t stop_l() override;
+    aaudio_result_t stop_l() REQUIRES(mLock) override;
 
     aaudio_result_t getAudioDataDescription(AudioEndpointParcelable &parcelable) override;
 
diff --git a/services/oboeservice/AAudioServiceStreamShared.cpp b/services/oboeservice/AAudioServiceStreamShared.cpp
index 031468e..c665cda 100644
--- a/services/oboeservice/AAudioServiceStreamShared.cpp
+++ b/services/oboeservice/AAudioServiceStreamShared.cpp
@@ -52,14 +52,26 @@
     return result.str();
 }
 
-std::string AAudioServiceStreamShared::dump() const {
+std::string AAudioServiceStreamShared::dump() const NO_THREAD_SAFETY_ANALYSIS {
     std::stringstream result;
 
+    const bool isLocked = AAudio_tryUntilTrue(
+            [this]()->bool { return audioDataQueueLock.try_lock(); } /* f */,
+            50 /* times */,
+            20 /* sleepMs */);
+    if (!isLocked) {
+        result << "AAudioServiceStreamShared may be deadlocked\n";
+    }
+
     result << AAudioServiceStreamBase::dump();
 
     result << mAudioDataQueue->dump();
     result << std::setw(8) << getXRunCount();
 
+    if (isLocked) {
+        audioDataQueueLock.unlock();
+    }
+
     return result.str();
 }
 
@@ -171,7 +183,7 @@
     }
 
     {
-        std::lock_guard<std::mutex> lock(mAudioDataQueueLock);
+        std::lock_guard<std::mutex> lock(audioDataQueueLock);
         // Create audio data shared memory buffer for client.
         mAudioDataQueue = std::make_shared<SharedRingBuffer>();
         result = mAudioDataQueue->allocate(calculateBytesPerFrame(), getBufferCapacity());
@@ -202,7 +214,7 @@
 aaudio_result_t AAudioServiceStreamShared::getAudioDataDescription(
         AudioEndpointParcelable &parcelable)
 {
-    std::lock_guard<std::mutex> lock(mAudioDataQueueLock);
+    std::lock_guard<std::mutex> lock(audioDataQueueLock);
     if (mAudioDataQueue == nullptr) {
         ALOGW("%s(): mUpMessageQueue null! - stream not open", __func__);
         return AAUDIO_ERROR_NULL;
@@ -260,7 +272,7 @@
     int64_t clientFramesWritten = 0;
 
     // Lock the AudioFifo to protect against close.
-    std::lock_guard <std::mutex> lock(mAudioDataQueueLock);
+    std::lock_guard <std::mutex> lock(audioDataQueueLock);
 
     if (mAudioDataQueue != nullptr) {
         std::shared_ptr<FifoBuffer> fifo = mAudioDataQueue->getFifoBuffer();
diff --git a/services/oboeservice/AAudioServiceStreamShared.h b/services/oboeservice/AAudioServiceStreamShared.h
index 5b1f8da..4fae5b4 100644
--- a/services/oboeservice/AAudioServiceStreamShared.h
+++ b/services/oboeservice/AAudioServiceStreamShared.h
@@ -52,22 +52,15 @@
 
     aaudio_result_t open(const aaudio::AAudioStreamRequest &request) override;
 
-    /**
-     * This must be locked when calling getAudioDataQueue_l() and while
-     * using the FifoBuffer it contains.
-     */
-    std::mutex &getAudioDataQueueLock() {
-        return mAudioDataQueueLock;
-    }
-
     void writeDataIfRoom(int64_t mmapFramesRead, const void *buffer, int32_t numFrames);
 
     /**
      * This must only be called under getAudioDataQueueLock().
      * @return
      */
-    std::shared_ptr<SharedRingBuffer> getAudioDataQueue_l() {
-      return mAudioDataQueue;
+    std::shared_ptr<SharedRingBuffer> getAudioDataQueue_l()
+            REQUIRES(audioDataQueueLock) {
+        return mAudioDataQueue;
     }
 
     /* Keep a record of when a buffer transfer completed.
@@ -89,6 +82,10 @@
 
     const char *getTypeText() const override { return "Shared"; }
 
+    // This is public so that the thread safety annotation, GUARDED_BY(),
+    // Can work when another object takes the lock.
+    mutable std::mutex   audioDataQueueLock;
+
 protected:
 
     aaudio_result_t getAudioDataDescription(AudioEndpointParcelable &parcelable) override;
@@ -107,8 +104,7 @@
 
 private:
 
-    std::shared_ptr<SharedRingBuffer> mAudioDataQueue; // protected by mAudioDataQueueLock
-    std::mutex               mAudioDataQueueLock;
+    std::shared_ptr<SharedRingBuffer> mAudioDataQueue GUARDED_BY(audioDataQueueLock);
 
     std::atomic<int64_t>     mTimestampPositionOffset;
     std::atomic<int32_t>     mXRunCount;
diff --git a/services/oboeservice/AAudioStreamTracker.cpp b/services/oboeservice/AAudioStreamTracker.cpp
index 8e66b94..9bbbc73 100644
--- a/services/oboeservice/AAudioStreamTracker.cpp
+++ b/services/oboeservice/AAudioStreamTracker.cpp
@@ -96,7 +96,7 @@
     return handle;
 }
 
-std::string AAudioStreamTracker::dump() const {
+std::string AAudioStreamTracker::dump() const NO_THREAD_SAFETY_ANALYSIS {
     std::stringstream result;
     const bool isLocked = AAudio_tryUntilTrue(
             [this]()->bool { return mHandleLock.try_lock(); } /* f */,
diff --git a/services/oboeservice/AAudioStreamTracker.h b/services/oboeservice/AAudioStreamTracker.h
index d1301a2..43870fc 100644
--- a/services/oboeservice/AAudioStreamTracker.h
+++ b/services/oboeservice/AAudioStreamTracker.h
@@ -17,13 +17,13 @@
 #ifndef AAUDIO_AAUDIO_STREAM_TRACKER_H
 #define AAUDIO_AAUDIO_STREAM_TRACKER_H
 
+#include <mutex>
 #include <time.h>
-#include <pthread.h>
 
+#include <android-base/thread_annotations.h>
 #include <aaudio/AAudio.h>
 
 #include "binding/AAudioCommon.h"
-
 #include "AAudioServiceStreamBase.h"
 
 namespace aaudio {
@@ -75,11 +75,10 @@
     static aaudio_handle_t bumpHandle(aaudio_handle_t handle);
 
     // Track stream using a unique handle that wraps. Only use positive half.
-    mutable std::mutex                mHandleLock;
-    // protected by mHandleLock
-    aaudio_handle_t                   mPreviousHandle = 0;
-    // protected by mHandleLock
-    std::map<aaudio_handle_t, android::sp<aaudio::AAudioServiceStreamBase>> mStreamsByHandle;
+    mutable std::mutex            mHandleLock;
+    aaudio_handle_t               mPreviousHandle GUARDED_BY(mHandleLock) = 0;
+    std::map<aaudio_handle_t, android::sp<aaudio::AAudioServiceStreamBase>>
+            mStreamsByHandle GUARDED_BY(mHandleLock);
 };
 
 
diff --git a/services/oboeservice/AAudioThread.cpp b/services/oboeservice/AAudioThread.cpp
index ed7895b..68496ac 100644
--- a/services/oboeservice/AAudioThread.cpp
+++ b/services/oboeservice/AAudioThread.cpp
@@ -37,10 +37,13 @@
     setup("AAudio");
 }
 
-void AAudioThread::setup(const char *prefix) {
-    // mThread is a pthread_t of unknown size so we need memset().
-    memset(&mThread, 0, sizeof(mThread));
+AAudioThread::~AAudioThread() {
+    ALOGE_IF(pthread_equal(pthread_self(), mThread),
+            "%s() destructor running in thread", __func__);
+    ALOGE_IF(mHasThread, "%s() thread never joined", __func__);
+}
 
+void AAudioThread::setup(const char *prefix) {
     // Name the thread with an increasing index, "prefix_#", for debugging.
     uint32_t index = mNextThreadIndex++;
     // Wrap the index so that we do not hit the 16 char limit
@@ -57,7 +60,7 @@
     }
 }
 
-// This is the entry point for the new thread created by createThread().
+// This is the entry point for the new thread created by createThread_l().
 // It converts the 'C' function call to a C++ method call.
 static void * AAudioThread_internalThreadProc(void *arg) {
     AAudioThread *aaudioThread = (AAudioThread *) arg;
@@ -90,13 +93,18 @@
         ALOGE("stop() but no thread running");
         return AAUDIO_ERROR_INVALID_STATE;
     }
+    // Check to see if the thread is trying to stop itself.
+    if (pthread_equal(pthread_self(), mThread)) {
+        ALOGE("%s() attempt to pthread_join() from launched thread!", __func__);
+        return AAUDIO_ERROR_INTERNAL;
+    }
+
     int err = pthread_join(mThread, nullptr);
-    mHasThread = false;
     if (err != 0) {
         ALOGE("stop() - pthread_join() returned %d %s", err, strerror(err));
         return AAudioConvert_androidToAAudioResult(-err);
     } else {
+        mHasThread = false;
         return AAUDIO_OK;
     }
 }
-
diff --git a/services/oboeservice/AAudioThread.h b/services/oboeservice/AAudioThread.h
index dcce68a..08a8a98 100644
--- a/services/oboeservice/AAudioThread.h
+++ b/services/oboeservice/AAudioThread.h
@@ -46,7 +46,7 @@
 
     explicit AAudioThread(const char *prefix);
 
-    virtual ~AAudioThread() = default;
+    virtual ~AAudioThread();
 
     /**
      * Start the thread running.
@@ -73,7 +73,7 @@
 
     Runnable    *mRunnable = nullptr;
     bool         mHasThread = false;
-    pthread_t    mThread; // initialized in constructor
+    pthread_t    mThread = {};
 
     static std::atomic<uint32_t> mNextThreadIndex;
     char         mName[16]; // max length for a pthread_name
diff --git a/services/oboeservice/Android.bp b/services/oboeservice/Android.bp
index 31e590e..80f17f4 100644
--- a/services/oboeservice/Android.bp
+++ b/services/oboeservice/Android.bp
@@ -37,6 +37,7 @@
     ],
 
     cflags: [
+        "-Wthread-safety",
         "-Wno-unused-parameter",
         "-Wall",
         "-Werror",
diff --git a/services/tuner/Android.bp b/services/tuner/Android.bp
new file mode 100644
index 0000000..0562b45
--- /dev/null
+++ b/services/tuner/Android.bp
@@ -0,0 +1,73 @@
+filegroup {
+    name: "tv_tuner_aidl",
+    srcs: [
+        "aidl/android/media/tv/tuner/ITunerService.aidl",
+    ],
+    path: "aidl",
+}
+
+aidl_interface {
+    name: "tv_tuner_aidl_interface",
+    unstable: true,
+    local_include_dir: "aidl",
+    srcs: [
+        ":tv_tuner_aidl",
+    ],
+}
+
+cc_library {
+    name: "libtunerservice",
+
+    srcs: [
+        "TunerService.cpp",
+    ],
+
+    shared_libs: [
+        "android.hardware.tv.tuner@1.0",
+        "libbinder",
+        "libbinder_ndk",
+        "libhidlbase",
+        "liblog",
+        "libmedia",
+        "libutils",
+        "tv_tuner_aidl_interface-ndk_platform",
+    ],
+
+    include_dirs: ["frameworks/av/include"],
+
+    cflags: [
+        "-Werror",
+        "-Wall",
+    ],
+
+    export_include_dirs: ["."],
+}
+
+
+cc_binary {
+    name: "mediatuner",
+
+    srcs: [
+        "main_tunerservice.cpp",
+    ],
+
+    shared_libs: [
+        "android.hardware.tv.tuner@1.0",
+        "libbase",
+        "libbinder",
+        "liblog",
+        "libtunerservice",
+        "libutils",
+    ],
+
+    static_libs: [
+        "tv_tuner_aidl_interface-ndk_platform",
+    ],
+
+    init_rc: ["mediatuner.rc"],
+
+    cflags: [
+        "-Werror",
+        "-Wall",
+    ],
+}
\ No newline at end of file
diff --git a/services/tuner/OWNERS b/services/tuner/OWNERS
new file mode 100644
index 0000000..0ceb8e8
--- /dev/null
+++ b/services/tuner/OWNERS
@@ -0,0 +1,2 @@
+nchalko@google.com
+quxiangfang@google.com
diff --git a/services/tuner/TunerService.cpp b/services/tuner/TunerService.cpp
new file mode 100644
index 0000000..92008a9
--- /dev/null
+++ b/services/tuner/TunerService.cpp
@@ -0,0 +1,65 @@
+/**
+ * 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_TAG "TunerService"
+
+#include <android/binder_manager.h>
+#include <utils/Log.h>
+#include "TunerService.h"
+
+using ::android::hardware::hidl_vec;
+using ::android::hardware::tv::tuner::V1_0::FrontendId;
+using ::android::hardware::tv::tuner::V1_0::Result;
+
+namespace android {
+
+sp<ITuner> TunerService::mTuner;
+
+TunerService::TunerService() {}
+TunerService::~TunerService() {}
+
+void TunerService::instantiate() {
+    std::shared_ptr<TunerService> service =
+            ::ndk::SharedRefBase::make<TunerService>();
+    AServiceManager_addService(service->asBinder().get(), getServiceName());
+}
+
+Status TunerService::getFrontendIds(std::vector<int32_t>* ids, int32_t* /* _aidl_return */) {
+    if (mTuner == nullptr) {
+        // TODO: create a method for init.
+        mTuner = ITuner::getService();
+        if (mTuner == nullptr) {
+            ALOGE("Failed to get ITuner service.");
+            return ::ndk::ScopedAStatus::fromServiceSpecificError(
+                    static_cast<int32_t>(Result::UNAVAILABLE));
+        }
+    }
+    hidl_vec<FrontendId> feIds;
+    Result res;
+    mTuner->getFrontendIds([&](Result r, const hidl_vec<FrontendId>& frontendIds) {
+        feIds = frontendIds;
+        res = r;
+    });
+    if (res != Result::SUCCESS) {
+        return ::ndk::ScopedAStatus::fromServiceSpecificError(static_cast<int32_t>(res));
+    }
+    ids->resize(feIds.size());
+    std::copy(feIds.begin(), feIds.end(), ids->begin());
+
+    return ::ndk::ScopedAStatus::ok();
+}
+
+} // namespace android
diff --git a/services/tuner/TunerService.h b/services/tuner/TunerService.h
new file mode 100644
index 0000000..bda6c65
--- /dev/null
+++ b/services/tuner/TunerService.h
@@ -0,0 +1,44 @@
+/**
+ * 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_TUNERSERVICE_H
+#define ANDROID_MEDIA_TUNERSERVICE_H
+
+#include <aidl/android/media/tv/tuner/BnTunerService.h>
+#include <android/hardware/tv/tuner/1.0/ITuner.h>
+
+using Status = ::ndk::ScopedAStatus;
+using ::aidl::android::media::tv::tuner::BnTunerService;
+using ::android::hardware::tv::tuner::V1_0::ITuner;
+
+namespace android {
+
+class TunerService : public BnTunerService {
+
+public:
+    static char const *getServiceName() { return "media.tuner"; }
+    static void instantiate();
+    TunerService();
+    virtual ~TunerService();
+    Status getFrontendIds(std::vector<int32_t>* ids, int32_t* _aidl_return) override;
+
+private:
+    static sp<ITuner> mTuner;
+};
+
+} // namespace android
+
+#endif // ANDROID_MEDIA_TUNERSERVICE_H
diff --git a/services/tuner/aidl/android/media/tv/OWNERS b/services/tuner/aidl/android/media/tv/OWNERS
new file mode 100644
index 0000000..0ceb8e8
--- /dev/null
+++ b/services/tuner/aidl/android/media/tv/OWNERS
@@ -0,0 +1,2 @@
+nchalko@google.com
+quxiangfang@google.com
diff --git a/services/tuner/aidl/android/media/tv/tuner/ITunerService.aidl b/services/tuner/aidl/android/media/tv/tuner/ITunerService.aidl
new file mode 100644
index 0000000..53cd90d
--- /dev/null
+++ b/services/tuner/aidl/android/media/tv/tuner/ITunerService.aidl
@@ -0,0 +1,32 @@
+/**
+ * Copyright (c) 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.tuner;
+
+/**
+ * TunerService interface handles tuner related operations.
+ *
+ * {@hide}
+ */
+interface ITunerService {
+
+    /**
+     * Gets frontend IDs.
+     *
+     * @return the result code of the operation.
+     */
+    int getFrontendIds(out int[] ids);
+}
diff --git a/services/tuner/main_tunerservice.cpp b/services/tuner/main_tunerservice.cpp
new file mode 100644
index 0000000..a0e7a9f
--- /dev/null
+++ b/services/tuner/main_tunerservice.cpp
@@ -0,0 +1,39 @@
+/*
+ * 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.
+ */
+
+#include <utils/Log.h>
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+#include <binder/ProcessState.h>
+#include <hidl/HidlTransportSupport.h>
+
+#include "TunerService.h"
+
+using namespace android;
+
+int main(int argc __unused, char** argv) {
+    ALOGD("Tuner service starting");
+
+    strcpy(argv[0], "media.tuner");
+    sp<ProcessState> proc(ProcessState::self());
+    sp<IServiceManager> sm = defaultServiceManager();
+    ALOGD("ServiceManager: %p", sm.get());
+
+    TunerService::instantiate();
+
+    ProcessState::self()->startThreadPool();
+    IPCThreadState::self()->joinThreadPool();
+}
diff --git a/services/tuner/mediatuner.rc b/services/tuner/mediatuner.rc
new file mode 100644
index 0000000..b0347be
--- /dev/null
+++ b/services/tuner/mediatuner.rc
@@ -0,0 +1,6 @@
+service media.tuner /system/bin/mediatuner
+    class main
+    user media
+    group media
+    ioprio rt 4
+    task_profiles ProcessCapacityHigh HighPerformance
\ No newline at end of file