Merge "Fix android.media.cts.ImageReaderDecoderTest fail on MT6580 project"
diff --git a/drm/libmediadrm/Android.bp b/drm/libmediadrm/Android.bp
index 84f2f6d..52c7438 100644
--- a/drm/libmediadrm/Android.bp
+++ b/drm/libmediadrm/Android.bp
@@ -44,9 +44,11 @@
         "libcutils",
         "libdl",
         "liblog",
+        "libmedia",
         "libmediadrmmetrics_lite",
         "libmediametrics",
         "libmediautils",
+        "libresourcemanagerservice",
         "libstagefright_foundation",
         "libutils",
         "android.hardware.drm@1.0",
diff --git a/drm/libmediadrm/DrmHal.cpp b/drm/libmediadrm/DrmHal.cpp
index e79fd4b..a2234e6 100644
--- a/drm/libmediadrm/DrmHal.cpp
+++ b/drm/libmediadrm/DrmHal.cpp
@@ -295,38 +295,45 @@
     }
 }
 
-
 Mutex DrmHal::mLock;
 
-struct DrmSessionClient : public DrmSessionClientInterface {
-    explicit DrmSessionClient(DrmHal* drm) : mDrm(drm) {}
-
-    virtual bool reclaimSession(const Vector<uint8_t>& sessionId) {
-        sp<DrmHal> drm = mDrm.promote();
-        if (drm == NULL) {
-            return true;
-        }
-        status_t err = drm->closeSession(sessionId);
-        if (err != OK) {
-            return false;
-        }
-        drm->sendEvent(EventType::SESSION_RECLAIMED,
-                toHidlVec(sessionId), hidl_vec<uint8_t>());
+bool DrmHal::DrmSessionClient::reclaimResource() {
+    sp<DrmHal> drm = mDrm.promote();
+    if (drm == NULL) {
         return true;
     }
+    status_t err = drm->closeSession(mSessionId);
+    if (err != OK) {
+        return false;
+    }
+    drm->sendEvent(EventType::SESSION_RECLAIMED,
+            toHidlVec(mSessionId), hidl_vec<uint8_t>());
+    return true;
+}
 
-protected:
-    virtual ~DrmSessionClient() {}
+String8 DrmHal::DrmSessionClient::getName() {
+    String8 name;
+    sp<DrmHal> drm = mDrm.promote();
+    if (drm == NULL) {
+        name.append("<deleted>");
+    } else if (drm->getPropertyStringInternal(String8("vendor"), name) != OK
+        || name.isEmpty()) {
+      name.append("<Get vendor failed or is empty>");
+    }
+    name.append("[");
+    for (size_t i = 0; i < mSessionId.size(); ++i) {
+        name.appendFormat("%02x", mSessionId[i]);
+    }
+    name.append("]");
+    return name;
+}
 
-private:
-    wp<DrmHal> mDrm;
-
-    DISALLOW_EVIL_CONSTRUCTORS(DrmSessionClient);
-};
+DrmHal::DrmSessionClient::~DrmSessionClient() {
+    DrmSessionManager::Instance()->removeSession(mSessionId);
+}
 
 DrmHal::DrmHal()
-   : mDrmSessionClient(new DrmSessionClient(this)),
-     mFactories(makeDrmFactories()),
+   : mFactories(makeDrmFactories()),
      mInitCheck((mFactories.size() == 0) ? ERROR_UNSUPPORTED : NO_INIT) {
 }
 
@@ -335,14 +342,13 @@
     auto openSessions = mOpenSessions;
     for (size_t i = 0; i < openSessions.size(); i++) {
         mLock.unlock();
-        closeSession(openSessions[i]);
+        closeSession(openSessions[i]->mSessionId);
         mLock.lock();
     }
     mOpenSessions.clear();
 }
 
 DrmHal::~DrmHal() {
-    DrmSessionManager::Instance()->removeDrm(mDrmSessionClient);
 }
 
 void DrmHal::cleanup() {
@@ -748,9 +754,9 @@
     } while (retry);
 
     if (err == OK) {
-        DrmSessionManager::Instance()->addSession(getCallingPid(),
-                mDrmSessionClient, sessionId);
-        mOpenSessions.push(sessionId);
+        sp<DrmSessionClient> client(new DrmSessionClient(this, sessionId));
+        DrmSessionManager::Instance()->addSession(getCallingPid(), client, sessionId);
+        mOpenSessions.push(client);
         mMetrics.SetSessionStart(sessionId);
     }
 
@@ -767,7 +773,7 @@
         if (status == Status::OK) {
             DrmSessionManager::Instance()->removeSession(sessionId);
             for (size_t i = 0; i < mOpenSessions.size(); i++) {
-                if (mOpenSessions[i] == sessionId) {
+                if (isEqualSessionId(mOpenSessions[i]->mSessionId, sessionId)) {
                     mOpenSessions.removeAt(i);
                     break;
                 }
diff --git a/drm/libmediadrm/DrmSessionManager.cpp b/drm/libmediadrm/DrmSessionManager.cpp
index 375644c..0b927ef 100644
--- a/drm/libmediadrm/DrmSessionManager.cpp
+++ b/drm/libmediadrm/DrmSessionManager.cpp
@@ -21,12 +21,17 @@
 #include <binder/IPCThreadState.h>
 #include <binder/IProcessInfoService.h>
 #include <binder/IServiceManager.h>
-#include <media/stagefright/ProcessInfo.h>
-#include <mediadrm/DrmSessionClientInterface.h>
+#include <cutils/properties.h>
+#include <media/IResourceManagerClient.h>
+#include <media/MediaResource.h>
 #include <mediadrm/DrmSessionManager.h>
 #include <unistd.h>
 #include <utils/String8.h>
 
+#include <vector>
+
+#include "ResourceManagerService.h"
+
 namespace android {
 
 static String8 GetSessionIdString(const Vector<uint8_t> &sessionId) {
@@ -37,6 +42,35 @@
     return sessionIdStr;
 }
 
+static std::vector<uint8_t> toStdVec(const Vector<uint8_t> &vector) {
+    const uint8_t *v = vector.array();
+    std::vector<uint8_t> vec(v, v + vector.size());
+    return vec;
+}
+
+static uint64_t toClientId(const sp<IResourceManagerClient>& drm) {
+    return reinterpret_cast<int64_t>(drm.get());
+}
+
+static Vector<MediaResource> toResourceVec(const Vector<uint8_t> &sessionId) {
+    Vector<MediaResource> resources;
+    // use UINT64_MAX to decrement through addition overflow
+    resources.push_back(MediaResource(MediaResource::kDrmSession, toStdVec(sessionId), UINT64_MAX));
+    return resources;
+}
+
+static sp<IResourceManagerService> getResourceManagerService() {
+    if (property_get_bool("persist.device_config.media_native.mediadrmserver", 1)) {
+        return new ResourceManagerService();
+    }
+    sp<IServiceManager> sm = defaultServiceManager();
+    if (sm == NULL) {
+        return NULL;
+    }
+    sp<IBinder> binder = sm->getService(String16("media.resource_manager"));
+    return interface_cast<IResourceManagerService>(binder);
+}
+
 bool isEqualSessionId(const Vector<uint8_t> &sessionId1, const Vector<uint8_t> &sessionId2) {
     if (sessionId1.size() != sessionId2.size()) {
         return false;
@@ -51,189 +85,114 @@
 
 sp<DrmSessionManager> DrmSessionManager::Instance() {
     static sp<DrmSessionManager> drmSessionManager = new DrmSessionManager();
+    drmSessionManager->init();
     return drmSessionManager;
 }
 
 DrmSessionManager::DrmSessionManager()
-    : mProcessInfo(new ProcessInfo()),
-      mTime(0) {}
+    : DrmSessionManager(getResourceManagerService()) {
+}
 
-DrmSessionManager::DrmSessionManager(sp<ProcessInfoInterface> processInfo)
-    : mProcessInfo(processInfo),
-      mTime(0) {}
+DrmSessionManager::DrmSessionManager(const sp<IResourceManagerService> &service)
+    : mService(service),
+      mInitialized(false) {
+    if (mService == NULL) {
+        ALOGE("Failed to init ResourceManagerService");
+    }
+}
 
-DrmSessionManager::~DrmSessionManager() {}
+DrmSessionManager::~DrmSessionManager() {
+    if (mService != NULL) {
+        IInterface::asBinder(mService)->unlinkToDeath(this);
+    }
+}
 
-void DrmSessionManager::addSession(
-        int pid, const sp<DrmSessionClientInterface>& drm, const Vector<uint8_t> &sessionId) {
-    ALOGV("addSession(pid %d, drm %p, sessionId %s)", pid, drm.get(),
+void DrmSessionManager::init() {
+    Mutex::Autolock lock(mLock);
+    if (mInitialized) {
+        return;
+    }
+    mInitialized = true;
+    if (mService != NULL) {
+        IInterface::asBinder(mService)->linkToDeath(this);
+    }
+}
+
+void DrmSessionManager::addSession(int pid,
+        const sp<IResourceManagerClient>& drm, const Vector<uint8_t> &sessionId) {
+    uid_t uid = IPCThreadState::self()->getCallingUid();
+    ALOGV("addSession(pid %d, uid %d, drm %p, sessionId %s)", pid, uid, drm.get(),
             GetSessionIdString(sessionId).string());
 
     Mutex::Autolock lock(mLock);
-    SessionInfo info;
-    info.drm = drm;
-    info.sessionId = sessionId;
-    info.timeStamp = getTime_l();
-    ssize_t index = mSessionMap.indexOfKey(pid);
-    if (index < 0) {
-        // new pid
-        SessionInfos infosForPid;
-        infosForPid.push_back(info);
-        mSessionMap.add(pid, infosForPid);
-    } else {
-        mSessionMap.editValueAt(index).push_back(info);
+    if (mService == NULL) {
+        return;
     }
+
+    int64_t clientId = toClientId(drm);
+    mSessionMap[toStdVec(sessionId)] = (SessionInfo){pid, uid, clientId};
+    mService->addResource(pid, uid, clientId, drm, toResourceVec(sessionId));
 }
 
 void DrmSessionManager::useSession(const Vector<uint8_t> &sessionId) {
     ALOGV("useSession(%s)", GetSessionIdString(sessionId).string());
 
     Mutex::Autolock lock(mLock);
-    for (size_t i = 0; i < mSessionMap.size(); ++i) {
-        SessionInfos& infos = mSessionMap.editValueAt(i);
-        for (size_t j = 0; j < infos.size(); ++j) {
-            SessionInfo& info = infos.editItemAt(j);
-            if (isEqualSessionId(sessionId, info.sessionId)) {
-                info.timeStamp = getTime_l();
-                return;
-            }
-        }
+    auto it = mSessionMap.find(toStdVec(sessionId));
+    if (mService == NULL || it == mSessionMap.end()) {
+        return;
     }
+
+    auto info = it->second;
+    mService->addResource(info.pid, info.uid, info.clientId, NULL, toResourceVec(sessionId));
 }
 
 void DrmSessionManager::removeSession(const Vector<uint8_t> &sessionId) {
     ALOGV("removeSession(%s)", GetSessionIdString(sessionId).string());
 
     Mutex::Autolock lock(mLock);
-    for (size_t i = 0; i < mSessionMap.size(); ++i) {
-        SessionInfos& infos = mSessionMap.editValueAt(i);
-        for (size_t j = 0; j < infos.size(); ++j) {
-            if (isEqualSessionId(sessionId, infos[j].sessionId)) {
-                infos.removeAt(j);
-                return;
-            }
-        }
+    auto it = mSessionMap.find(toStdVec(sessionId));
+    if (mService == NULL || it == mSessionMap.end()) {
+        return;
     }
-}
 
-void DrmSessionManager::removeDrm(const sp<DrmSessionClientInterface>& drm) {
-    ALOGV("removeDrm(%p)", drm.get());
-
-    Mutex::Autolock lock(mLock);
-    bool found = false;
-    for (size_t i = 0; i < mSessionMap.size(); ++i) {
-        SessionInfos& infos = mSessionMap.editValueAt(i);
-        for (size_t j = 0; j < infos.size();) {
-            if (infos[j].drm == drm) {
-                ALOGV("removed session (%s)", GetSessionIdString(infos[j].sessionId).string());
-                j = infos.removeAt(j);
-                found = true;
-            } else {
-                ++j;
-            }
-        }
-        if (found) {
-            break;
-        }
-    }
+    auto info = it->second;
+    mService->removeResource(info.pid, info.clientId, toResourceVec(sessionId));
+    mSessionMap.erase(it);
 }
 
 bool DrmSessionManager::reclaimSession(int callingPid) {
     ALOGV("reclaimSession(%d)", callingPid);
 
-    sp<DrmSessionClientInterface> drm;
-    Vector<uint8_t> sessionId;
-    int lowestPriorityPid;
-    int lowestPriority;
-    {
-        Mutex::Autolock lock(mLock);
-        int callingPriority;
-        if (!mProcessInfo->getPriority(callingPid, &callingPriority)) {
-            return false;
-        }
-        if (!getLowestPriority_l(&lowestPriorityPid, &lowestPriority)) {
-            return false;
-        }
-        if (lowestPriority <= callingPriority) {
-            return false;
-        }
+    // unlock early because reclaimResource might callback into removeSession
+    mLock.lock();
+    sp<IResourceManagerService> service(mService);
+    mLock.unlock();
 
-        if (!getLeastUsedSession_l(lowestPriorityPid, &drm, &sessionId)) {
-            return false;
-        }
-    }
-
-    if (drm == NULL) {
+    if (service == NULL) {
         return false;
     }
 
-    ALOGV("reclaim session(%s) opened by pid %d",
-            GetSessionIdString(sessionId).string(), lowestPriorityPid);
-
-    return drm->reclaimSession(sessionId);
+    // cannot update mSessionMap because we do not know which sessionId is reclaimed;
+    // we rely on IResourceManagerClient to removeSession in reclaimResource
+    Vector<uint8_t> dummy;
+    return service->reclaimResource(callingPid, toResourceVec(dummy));
 }
 
-int64_t DrmSessionManager::getTime_l() {
-    return mTime++;
+size_t DrmSessionManager::getSessionCount() const {
+    Mutex::Autolock lock(mLock);
+    return mSessionMap.size();
 }
 
-bool DrmSessionManager::getLowestPriority_l(int* lowestPriorityPid, int* lowestPriority) {
-    int pid = -1;
-    int priority = -1;
-    for (size_t i = 0; i < mSessionMap.size(); ++i) {
-        if (mSessionMap.valueAt(i).size() == 0) {
-            // no opened session by this process.
-            continue;
-        }
-        int tempPid = mSessionMap.keyAt(i);
-        int tempPriority;
-        if (!mProcessInfo->getPriority(tempPid, &tempPriority)) {
-            // shouldn't happen.
-            return false;
-        }
-        if (pid == -1) {
-            pid = tempPid;
-            priority = tempPriority;
-        } else {
-            if (tempPriority > priority) {
-                pid = tempPid;
-                priority = tempPriority;
-            }
-        }
-    }
-    if (pid != -1) {
-        *lowestPriorityPid = pid;
-        *lowestPriority = priority;
-    }
-    return (pid != -1);
+bool DrmSessionManager::containsSession(const Vector<uint8_t>& sessionId) const {
+    Mutex::Autolock lock(mLock);
+    return mSessionMap.count(toStdVec(sessionId));
 }
 
-bool DrmSessionManager::getLeastUsedSession_l(
-        int pid, sp<DrmSessionClientInterface>* drm, Vector<uint8_t>* sessionId) {
-    ssize_t index = mSessionMap.indexOfKey(pid);
-    if (index < 0) {
-        return false;
-    }
-
-    int leastUsedIndex = -1;
-    int64_t minTs = LLONG_MAX;
-    const SessionInfos& infos = mSessionMap.valueAt(index);
-    for (size_t j = 0; j < infos.size(); ++j) {
-        if (leastUsedIndex == -1) {
-            leastUsedIndex = j;
-            minTs = infos[j].timeStamp;
-        } else {
-            if (infos[j].timeStamp < minTs) {
-                leastUsedIndex = j;
-                minTs = infos[j].timeStamp;
-            }
-        }
-    }
-    if (leastUsedIndex != -1) {
-        *drm = infos[leastUsedIndex].drm;
-        *sessionId = infos[leastUsedIndex].sessionId;
-    }
-    return (leastUsedIndex != -1);
+void DrmSessionManager::binderDied(const wp<IBinder>& /*who*/) {
+    ALOGW("ResourceManagerService died.");
+    Mutex::Autolock lock(mLock);
+    mService.clear();
 }
 
 }  // namespace android
diff --git a/drm/libmediadrm/include/mediadrm/DrmHal.h b/drm/libmediadrm/include/mediadrm/DrmHal.h
index bdf1b30..542d300 100644
--- a/drm/libmediadrm/include/mediadrm/DrmHal.h
+++ b/drm/libmediadrm/include/mediadrm/DrmHal.h
@@ -26,8 +26,10 @@
 #include <android/hardware/drm/1.2/IDrmPlugin.h>
 #include <android/hardware/drm/1.2/IDrmPluginListener.h>
 
+#include <media/IResourceManagerService.h>
 #include <media/MediaAnalyticsItem.h>
 #include <mediadrm/DrmMetrics.h>
+#include <mediadrm/DrmSessionManager.h>
 #include <mediadrm/IDrm.h>
 #include <mediadrm/IDrmClient.h>
 #include <utils/threads.h>
@@ -59,6 +61,26 @@
 struct DrmHal : public BnDrm,
                 public IBinder::DeathRecipient,
                 public IDrmPluginListener_V1_2 {
+
+    struct DrmSessionClient : public BnResourceManagerClient {
+        explicit DrmSessionClient(DrmHal* drm, const Vector<uint8_t>& sessionId)
+          : mSessionId(sessionId),
+            mDrm(drm) {}
+
+        virtual bool reclaimResource();
+        virtual String8 getName();
+
+        const Vector<uint8_t> mSessionId;
+
+    protected:
+        virtual ~DrmSessionClient();
+
+    private:
+        wp<DrmHal> mDrm;
+
+        DISALLOW_EVIL_CONSTRUCTORS(DrmSessionClient);
+    };
+
     DrmHal();
     virtual ~DrmHal();
 
@@ -193,8 +215,6 @@
 private:
     static Mutex mLock;
 
-    sp<DrmSessionClientInterface> mDrmSessionClient;
-
     sp<IDrmClient> mListener;
     mutable Mutex mEventLock;
     mutable Mutex mNotifyLock;
@@ -208,7 +228,7 @@
     // Mutable to allow modification within GetPropertyByteArray.
     mutable MediaDrmMetrics mMetrics;
 
-    Vector<Vector<uint8_t>> mOpenSessions;
+    Vector<sp<DrmSessionClient>> mOpenSessions;
     void closeOpenSessions();
     void cleanup();
 
diff --git a/drm/libmediadrm/include/mediadrm/DrmSessionManager.h b/drm/libmediadrm/include/mediadrm/DrmSessionManager.h
index ba27199..b1ad580 100644
--- a/drm/libmediadrm/include/mediadrm/DrmSessionManager.h
+++ b/drm/libmediadrm/include/mediadrm/DrmSessionManager.h
@@ -18,56 +18,61 @@
 
 #define DRM_SESSION_MANAGER_H_
 
+#include <binder/IBinder.h>
+#include <media/IResourceManagerService.h>
 #include <media/stagefright/foundation/ABase.h>
 #include <utils/RefBase.h>
 #include <utils/KeyedVector.h>
 #include <utils/threads.h>
 #include <utils/Vector.h>
 
+#include <map>
+#include <utility>
+#include <vector>
+
 namespace android {
 
 class DrmSessionManagerTest;
-struct DrmSessionClientInterface;
-struct ProcessInfoInterface;
+class IResourceManagerClient;
 
 bool isEqualSessionId(const Vector<uint8_t> &sessionId1, const Vector<uint8_t> &sessionId2);
 
 struct SessionInfo {
-    sp<DrmSessionClientInterface> drm;
-    Vector<uint8_t> sessionId;
-    int64_t timeStamp;
+    pid_t pid;
+    uid_t uid;
+    int64_t clientId;
 };
 
-typedef Vector<SessionInfo > SessionInfos;
-typedef KeyedVector<int, SessionInfos > PidSessionInfosMap;
+typedef std::map<std::vector<uint8_t>, SessionInfo> SessionInfoMap;
 
-struct DrmSessionManager : public RefBase {
+struct DrmSessionManager : public IBinder::DeathRecipient {
     static sp<DrmSessionManager> Instance();
 
     DrmSessionManager();
-    explicit DrmSessionManager(sp<ProcessInfoInterface> processInfo);
+    explicit DrmSessionManager(const sp<IResourceManagerService> &service);
 
-    void addSession(int pid, const sp<DrmSessionClientInterface>& drm, const Vector<uint8_t>& sessionId);
+    void addSession(int pid, const sp<IResourceManagerClient>& drm, const Vector<uint8_t>& sessionId);
     void useSession(const Vector<uint8_t>& sessionId);
     void removeSession(const Vector<uint8_t>& sessionId);
-    void removeDrm(const sp<DrmSessionClientInterface>& drm);
     bool reclaimSession(int callingPid);
 
+    // sanity check APIs
+    size_t getSessionCount() const;
+    bool containsSession(const Vector<uint8_t>& sessionId) const;
+
+    // implements DeathRecipient
+    virtual void binderDied(const wp<IBinder>& /*who*/);
+
 protected:
     virtual ~DrmSessionManager();
 
 private:
-    friend class DrmSessionManagerTest;
+    void init();
 
-    int64_t getTime_l();
-    bool getLowestPriority_l(int* lowestPriorityPid, int* lowestPriority);
-    bool getLeastUsedSession_l(
-            int pid, sp<DrmSessionClientInterface>* drm, Vector<uint8_t>* sessionId);
-
-    sp<ProcessInfoInterface> mProcessInfo;
+    sp<IResourceManagerService> mService;
     mutable Mutex mLock;
-    PidSessionInfosMap mSessionMap;
-    int64_t mTime;
+    SessionInfoMap mSessionMap;
+    bool mInitialized;
 
     DISALLOW_EVIL_CONSTRUCTORS(DrmSessionManager);
 };
diff --git a/media/codec2/sfplugin/CCodecBufferChannel.cpp b/media/codec2/sfplugin/CCodecBufferChannel.cpp
index 2efb987..375ce17 100644
--- a/media/codec2/sfplugin/CCodecBufferChannel.cpp
+++ b/media/codec2/sfplugin/CCodecBufferChannel.cpp
@@ -29,6 +29,7 @@
 #include <android/hardware/cas/native/1.0/IDescrambler.h>
 #include <android-base/stringprintf.h>
 #include <binder/MemoryDealer.h>
+#include <cutils/properties.h>
 #include <gui/Surface.h>
 #include <media/openmax/OMX_Core.h>
 #include <media/stagefright/foundation/ABuffer.h>
diff --git a/media/codec2/vndk/Android.bp b/media/codec2/vndk/Android.bp
index 52cc7ad..6fbad0a 100644
--- a/media/codec2/vndk/Android.bp
+++ b/media/codec2/vndk/Android.bp
@@ -71,6 +71,8 @@
         "libutils",
     ],
 
+    tidy: false, // b/146435095, clang-tidy segmentation fault
+
     cflags: [
         "-Werror",
         "-Wall",
diff --git a/media/extractors/aac/Android.bp b/media/extractors/aac/Android.bp
index a58167a..53b394f 100644
--- a/media/extractors/aac/Android.bp
+++ b/media/extractors/aac/Android.bp
@@ -1,4 +1,4 @@
-cc_library_shared {
+cc_library {
 
     srcs: ["AACExtractor.cpp"],
 
diff --git a/media/extractors/amr/Android.bp b/media/extractors/amr/Android.bp
index 4bd933d..cd76062 100644
--- a/media/extractors/amr/Android.bp
+++ b/media/extractors/amr/Android.bp
@@ -1,4 +1,4 @@
-cc_library_shared {
+cc_library {
 
     srcs: ["AMRExtractor.cpp"],
 
diff --git a/media/extractors/flac/Android.bp b/media/extractors/flac/Android.bp
index 3a3d051..c669b34 100644
--- a/media/extractors/flac/Android.bp
+++ b/media/extractors/flac/Android.bp
@@ -1,4 +1,4 @@
-cc_library_shared {
+cc_library {
 
     srcs: ["FLACExtractor.cpp"],
 
diff --git a/media/extractors/midi/Android.bp b/media/extractors/midi/Android.bp
index d36cb49..40c91e7 100644
--- a/media/extractors/midi/Android.bp
+++ b/media/extractors/midi/Android.bp
@@ -1,4 +1,4 @@
-cc_library_shared {
+cc_library {
 
     srcs: ["MidiExtractor.cpp"],
 
diff --git a/media/extractors/mkv/Android.bp b/media/extractors/mkv/Android.bp
index 1744d3d..650d79d 100644
--- a/media/extractors/mkv/Android.bp
+++ b/media/extractors/mkv/Android.bp
@@ -1,4 +1,4 @@
-cc_library_shared {
+cc_library {
 
     srcs: ["MatroskaExtractor.cpp"],
 
diff --git a/media/extractors/mp3/Android.bp b/media/extractors/mp3/Android.bp
index 4e2f248..6f02b0f 100644
--- a/media/extractors/mp3/Android.bp
+++ b/media/extractors/mp3/Android.bp
@@ -1,4 +1,4 @@
-cc_library_shared {
+cc_library {
 
     srcs: [
             "MP3Extractor.cpp",
diff --git a/media/extractors/mp4/Android.bp b/media/extractors/mp4/Android.bp
index 1b308aa..d9f11fc 100644
--- a/media/extractors/mp4/Android.bp
+++ b/media/extractors/mp4/Android.bp
@@ -35,7 +35,7 @@
     compile_multilib: "first",
 }
 
-cc_library_shared {
+cc_library {
 
 
     name: "libmp4extractor",
diff --git a/media/extractors/mpeg2/Android.bp b/media/extractors/mpeg2/Android.bp
index 1d9e1e6..14bd644 100644
--- a/media/extractors/mpeg2/Android.bp
+++ b/media/extractors/mpeg2/Android.bp
@@ -1,4 +1,4 @@
-cc_library_shared {
+cc_library {
 
     srcs: [
         "ExtractorBundle.cpp",
diff --git a/media/extractors/ogg/Android.bp b/media/extractors/ogg/Android.bp
index 604ec59..e661b5d 100644
--- a/media/extractors/ogg/Android.bp
+++ b/media/extractors/ogg/Android.bp
@@ -1,4 +1,4 @@
-cc_library_shared {
+cc_library {
 
     srcs: ["OggExtractor.cpp"],
 
diff --git a/media/extractors/wav/Android.bp b/media/extractors/wav/Android.bp
index 7e89271..51e3c31 100644
--- a/media/extractors/wav/Android.bp
+++ b/media/extractors/wav/Android.bp
@@ -1,4 +1,4 @@
-cc_library_shared {
+cc_library {
 
     srcs: ["WAVExtractor.cpp"],
 
diff --git a/media/libaudioclient/AudioSystem.cpp b/media/libaudioclient/AudioSystem.cpp
index cb9c7ba..480930b 100644
--- a/media/libaudioclient/AudioSystem.cpp
+++ b/media/libaudioclient/AudioSystem.cpp
@@ -1491,7 +1491,14 @@
             }
         }
     }
-    ALOGE("invalid attributes %s when converting to stream",  toString(attr).c_str());
+    switch (attr.usage) {
+        case AUDIO_USAGE_VIRTUAL_SOURCE:
+            // virtual source is not expected to have an associated product strategy
+            break;
+        default:
+            ALOGE("invalid attributes %s when converting to stream",  toString(attr).c_str());
+            break;
+    }
     return AUDIO_STREAM_MUSIC;
 }
 
diff --git a/media/libmedia/MediaResource.cpp b/media/libmedia/MediaResource.cpp
index e636a50..8626009 100644
--- a/media/libmedia/MediaResource.cpp
+++ b/media/libmedia/MediaResource.cpp
@@ -19,6 +19,8 @@
 #include <utils/Log.h>
 #include <media/MediaResource.h>
 
+#include <vector>
+
 namespace android {
 
 MediaResource::MediaResource()
@@ -36,26 +38,48 @@
           mSubType(subType),
           mValue(value) {}
 
+MediaResource::MediaResource(Type type, const std::vector<uint8_t> &id, uint64_t value)
+        : mType(type),
+          mSubType(kUnspecifiedSubType),
+          mValue(value),
+          mId(id) {}
+
 void MediaResource::readFromParcel(const Parcel &parcel) {
     mType = static_cast<Type>(parcel.readInt32());
     mSubType = static_cast<SubType>(parcel.readInt32());
     mValue = parcel.readUint64();
+    parcel.readByteVector(&mId);
 }
 
 void MediaResource::writeToParcel(Parcel *parcel) const {
     parcel->writeInt32(static_cast<int32_t>(mType));
     parcel->writeInt32(static_cast<int32_t>(mSubType));
     parcel->writeUint64(mValue);
+    parcel->writeByteVector(mId);
+}
+
+static String8 bytesToHexString(const std::vector<uint8_t> &bytes) {
+    String8 str;
+    for (auto &b : bytes) {
+        str.appendFormat("%02x", b);
+    }
+    return str;
 }
 
 String8 MediaResource::toString() const {
     String8 str;
-    str.appendFormat("%s/%s:%llu", asString(mType), asString(mSubType), (unsigned long long)mValue);
+    str.appendFormat("%s/%s:[%s]:%llu",
+        asString(mType), asString(mSubType),
+        bytesToHexString(mId).c_str(),
+        (unsigned long long)mValue);
     return str;
 }
 
 bool MediaResource::operator==(const MediaResource &other) const {
-    return (other.mType == mType) && (other.mSubType == mSubType) && (other.mValue == mValue);
+    return (other.mType == mType)
+      && (other.mSubType == mSubType)
+      && (other.mValue == mValue)
+      && (other.mId == mId);
 }
 
 bool MediaResource::operator!=(const MediaResource &other) const {
diff --git a/media/libmedia/include/media/MediaResource.h b/media/libmedia/include/media/MediaResource.h
index 10a07bb..e9684f0 100644
--- a/media/libmedia/include/media/MediaResource.h
+++ b/media/libmedia/include/media/MediaResource.h
@@ -20,6 +20,7 @@
 
 #include <binder/Parcel.h>
 #include <utils/String8.h>
+#include <vector>
 
 namespace android {
 
@@ -32,6 +33,7 @@
         kGraphicMemory,
         kCpuBoost,
         kBattery,
+        kDrmSession,
     };
 
     enum SubType {
@@ -43,6 +45,7 @@
     MediaResource();
     MediaResource(Type type, uint64_t value);
     MediaResource(Type type, SubType subType, uint64_t value);
+    MediaResource(Type type, const std::vector<uint8_t> &id, uint64_t value);
 
     void readFromParcel(const Parcel &parcel);
     void writeToParcel(Parcel *parcel) const;
@@ -55,6 +58,8 @@
     Type mType;
     SubType mSubType;
     uint64_t mValue;
+    // for kDrmSession-type mId is the unique session id obtained via MediaDrm#openSession
+    std::vector<uint8_t> mId;
 };
 
 inline static const char *asString(MediaResource::Type i, const char *def = "??") {
@@ -65,6 +70,7 @@
         case MediaResource::kGraphicMemory:  return "graphic-memory";
         case MediaResource::kCpuBoost:       return "cpu-boost";
         case MediaResource::kBattery:        return "battery";
+        case MediaResource::kDrmSession:     return "drm-session";
         default:                             return def;
     }
 }
diff --git a/media/libmediametrics/MediaAnalyticsItem.cpp b/media/libmediametrics/MediaAnalyticsItem.cpp
index 02c23b1..b7856a6 100644
--- a/media/libmediametrics/MediaAnalyticsItem.cpp
+++ b/media/libmediametrics/MediaAnalyticsItem.cpp
@@ -64,6 +64,16 @@
     return item;
 }
 
+MediaAnalyticsItem* MediaAnalyticsItem::convert(mediametrics_handle_t handle) {
+    MediaAnalyticsItem *item = (android::MediaAnalyticsItem *) handle;
+    return item;
+}
+
+mediametrics_handle_t MediaAnalyticsItem::convert(MediaAnalyticsItem *item ) {
+    mediametrics_handle_t handle = (mediametrics_handle_t) item;
+    return handle;
+}
+
 // access functions for the class
 MediaAnalyticsItem::MediaAnalyticsItem()
     : mPid(-1),
diff --git a/media/libmediametrics/MediaMetrics.cpp b/media/libmediametrics/MediaMetrics.cpp
index 6109190..360ae0c 100644
--- a/media/libmediametrics/MediaMetrics.cpp
+++ b/media/libmediametrics/MediaMetrics.cpp
@@ -169,6 +169,11 @@
     return item->selfrecord();
 }
 
+mediametrics_handle_t mediametrics_dup(mediametrics_handle_t handle) {
+    android::MediaAnalyticsItem *item = (android::MediaAnalyticsItem *) handle;
+    if (item == NULL) return android::MediaAnalyticsItem::convert(item);
+    return android::MediaAnalyticsItem::convert(item->dup());
+}
 
 const char *mediametrics_readable(mediametrics_handle_t handle) {
     android::MediaAnalyticsItem *item = (android::MediaAnalyticsItem *) handle;
diff --git a/media/libmediametrics/include/MediaAnalyticsItem.h b/media/libmediametrics/include/MediaAnalyticsItem.h
index 4a36f6a..42a2f5b 100644
--- a/media/libmediametrics/include/MediaAnalyticsItem.h
+++ b/media/libmediametrics/include/MediaAnalyticsItem.h
@@ -17,6 +17,8 @@
 #ifndef ANDROID_MEDIA_MEDIAANALYTICSITEM_H
 #define ANDROID_MEDIA_MEDIAANALYTICSITEM_H
 
+#include "MediaMetrics.h"
+
 #include <string>
 #include <sys/types.h>
 
@@ -94,6 +96,9 @@
         static MediaAnalyticsItem* create(Key key);
         static MediaAnalyticsItem* create();
 
+        static MediaAnalyticsItem* convert(mediametrics_handle_t);
+        static mediametrics_handle_t convert(MediaAnalyticsItem *);
+
         // access functions for the class
         ~MediaAnalyticsItem();
 
diff --git a/media/libmediametrics/include/MediaMetrics.h b/media/libmediametrics/include/MediaMetrics.h
index a4e1ed2..29fb241 100644
--- a/media/libmediametrics/include/MediaMetrics.h
+++ b/media/libmediametrics/include/MediaMetrics.h
@@ -79,6 +79,7 @@
 // # of attributes set within this record.
 int32_t mediametrics_count(mediametrics_handle_t handle);
 
+mediametrics_handle_t mediametrics_dup(mediametrics_handle_t handle);
 bool mediametrics_selfRecord(mediametrics_handle_t handle);
 
 const char *mediametrics_readable(mediametrics_handle_t handle);
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
index 3388097..c1c4b55 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
@@ -1798,7 +1798,9 @@
 }
 
 void NuPlayer::closeAudioSink() {
-    mRenderer->closeAudioSink();
+    if (mRenderer != NULL) {
+        mRenderer->closeAudioSink();
+    }
 }
 
 void NuPlayer::restartAudio(
diff --git a/media/libmediaplayerservice/tests/Android.bp b/media/libmediaplayerservice/tests/Android.bp
index f8c89e5..8357925 100644
--- a/media/libmediaplayerservice/tests/Android.bp
+++ b/media/libmediaplayerservice/tests/Android.bp
@@ -6,14 +6,22 @@
 
     shared_libs: [
         "liblog",
+        "libbinder",
+        "libmedia",
         "libmediaplayerservice",
         "libmediadrm",
+        "libresourcemanagerservice",
         "libutils",
         "android.hardware.drm@1.0",
         "android.hardware.drm@1.1",
         "android.hardware.drm@1.2",
     ],
 
+    include_dirs: [
+        "frameworks/av/include",
+        "frameworks/av/services/mediaresourcemanager",
+    ],
+
     cflags: [
         "-Werror",
         "-Wall",
diff --git a/media/libmediaplayerservice/tests/DrmSessionManager_test.cpp b/media/libmediaplayerservice/tests/DrmSessionManager_test.cpp
index d81ee05..58e4bee 100644
--- a/media/libmediaplayerservice/tests/DrmSessionManager_test.cpp
+++ b/media/libmediaplayerservice/tests/DrmSessionManager_test.cpp
@@ -20,14 +20,29 @@
 
 #include <gtest/gtest.h>
 
+#include <media/IResourceManagerService.h>
+#include <media/IResourceManagerClient.h>
 #include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/ProcessInfoInterface.h>
 #include <mediadrm/DrmHal.h>
 #include <mediadrm/DrmSessionClientInterface.h>
 #include <mediadrm/DrmSessionManager.h>
 
+#include <algorithm>
+#include <vector>
+
+#include "ResourceManagerService.h"
+
 namespace android {
 
+static Vector<uint8_t> toAndroidVector(const std::vector<uint8_t> &vec) {
+    Vector<uint8_t> aVec;
+    for (auto b : vec) {
+        aVec.push_back(b);
+    }
+    return aVec;
+}
+
 struct FakeProcessInfo : public ProcessInfoInterface {
     FakeProcessInfo() {}
     virtual ~FakeProcessInfo() {}
@@ -47,173 +62,128 @@
     DISALLOW_EVIL_CONSTRUCTORS(FakeProcessInfo);
 };
 
-struct FakeDrm : public DrmSessionClientInterface {
-    FakeDrm() {}
+struct FakeDrm : public BnResourceManagerClient {
+    FakeDrm(const std::vector<uint8_t>& sessionId, const sp<DrmSessionManager>& manager)
+        : mSessionId(toAndroidVector(sessionId)),
+          mReclaimed(false),
+          mDrmSessionManager(manager) {}
+
     virtual ~FakeDrm() {}
 
-    virtual bool reclaimSession(const Vector<uint8_t>& sessionId) {
-        mReclaimedSessions.push_back(sessionId);
+    virtual bool reclaimResource() {
+        mReclaimed = true;
+        mDrmSessionManager->removeSession(mSessionId);
         return true;
     }
 
-    const Vector<Vector<uint8_t> >& reclaimedSessions() const {
-        return mReclaimedSessions;
+    virtual String8 getName() {
+        String8 name("FakeDrm[");
+        for (size_t i = 0; i < mSessionId.size(); ++i) {
+            name.appendFormat("%02x", mSessionId[i]);
+        }
+        name.append("]");
+        return name;
     }
 
+    bool isReclaimed() const {
+        return mReclaimed;
+    }
+
+    const Vector<uint8_t> mSessionId;
+
 private:
-    Vector<Vector<uint8_t> > mReclaimedSessions;
+    bool mReclaimed;
+    const sp<DrmSessionManager> mDrmSessionManager;
 
     DISALLOW_EVIL_CONSTRUCTORS(FakeDrm);
 };
 
+struct FakeSystemCallback :
+        public ResourceManagerService::SystemCallbackInterface {
+    FakeSystemCallback() {}
+
+    virtual void noteStartVideo(int /*uid*/) override {}
+
+    virtual void noteStopVideo(int /*uid*/) override {}
+
+    virtual void noteResetVideo() override {}
+
+    virtual bool requestCpusetBoost(
+            bool /*enable*/, const sp<IInterface> &/*client*/) override {
+        return true;
+    }
+
+protected:
+    virtual ~FakeSystemCallback() {}
+
+private:
+
+    DISALLOW_EVIL_CONSTRUCTORS(FakeSystemCallback);
+};
+
 static const int kTestPid1 = 30;
 static const int kTestPid2 = 20;
-static const uint8_t kTestSessionId1[] = {1, 2, 3};
-static const uint8_t kTestSessionId2[] = {4, 5, 6, 7, 8};
-static const uint8_t kTestSessionId3[] = {9, 0};
+static const std::vector<uint8_t> kTestSessionId1{1, 2, 3};
+static const std::vector<uint8_t> kTestSessionId2{4, 5, 6, 7, 8};
+static const std::vector<uint8_t> kTestSessionId3{9, 0};
 
 class DrmSessionManagerTest : public ::testing::Test {
 public:
     DrmSessionManagerTest()
-        : mDrmSessionManager(new DrmSessionManager(new FakeProcessInfo())),
-          mTestDrm1(new FakeDrm()),
-          mTestDrm2(new FakeDrm()) {
-        GetSessionId(kTestSessionId1, ARRAY_SIZE(kTestSessionId1), &mSessionId1);
-        GetSessionId(kTestSessionId2, ARRAY_SIZE(kTestSessionId2), &mSessionId2);
-        GetSessionId(kTestSessionId3, ARRAY_SIZE(kTestSessionId3), &mSessionId3);
+        : mService(new ResourceManagerService(new FakeProcessInfo(), new FakeSystemCallback())),
+          mDrmSessionManager(new DrmSessionManager(mService)),
+          mTestDrm1(new FakeDrm(kTestSessionId1, mDrmSessionManager)),
+          mTestDrm2(new FakeDrm(kTestSessionId2, mDrmSessionManager)),
+          mTestDrm3(new FakeDrm(kTestSessionId3, mDrmSessionManager)) {
+        DrmSessionManager *ptr = new DrmSessionManager(mService);
+        EXPECT_NE(ptr, nullptr);
+        /* mDrmSessionManager = ptr; */
     }
 
 protected:
-    static void GetSessionId(const uint8_t* ids, size_t num, Vector<uint8_t>* sessionId) {
-        for (size_t i = 0; i < num; ++i) {
-            sessionId->push_back(ids[i]);
-        }
-    }
-
-    static void ExpectEqSessionInfo(const SessionInfo& info, sp<DrmSessionClientInterface> drm,
-            const Vector<uint8_t>& sessionId, int64_t timeStamp) {
-        EXPECT_EQ(drm, info.drm);
-        EXPECT_TRUE(isEqualSessionId(sessionId, info.sessionId));
-        EXPECT_EQ(timeStamp, info.timeStamp);
-    }
-
     void addSession() {
-        mDrmSessionManager->addSession(kTestPid1, mTestDrm1, mSessionId1);
-        mDrmSessionManager->addSession(kTestPid2, mTestDrm2, mSessionId2);
-        mDrmSessionManager->addSession(kTestPid2, mTestDrm2, mSessionId3);
-        const PidSessionInfosMap& map = sessionMap();
-        EXPECT_EQ(2u, map.size());
-        ssize_t index1 = map.indexOfKey(kTestPid1);
-        ASSERT_GE(index1, 0);
-        const SessionInfos& infos1 = map[index1];
-        EXPECT_EQ(1u, infos1.size());
-        ExpectEqSessionInfo(infos1[0], mTestDrm1, mSessionId1, 0);
-
-        ssize_t index2 = map.indexOfKey(kTestPid2);
-        ASSERT_GE(index2, 0);
-        const SessionInfos& infos2 = map[index2];
-        EXPECT_EQ(2u, infos2.size());
-        ExpectEqSessionInfo(infos2[0], mTestDrm2, mSessionId2, 1);
-        ExpectEqSessionInfo(infos2[1], mTestDrm2, mSessionId3, 2);
+        mDrmSessionManager->addSession(kTestPid1, mTestDrm1, mTestDrm1->mSessionId);
+        mDrmSessionManager->addSession(kTestPid2, mTestDrm2, mTestDrm2->mSessionId);
+        mDrmSessionManager->addSession(kTestPid2, mTestDrm3, mTestDrm3->mSessionId);
     }
 
-    const PidSessionInfosMap& sessionMap() {
-        return mDrmSessionManager->mSessionMap;
-    }
-
-    void testGetLowestPriority() {
-        int pid;
-        int priority;
-        EXPECT_FALSE(mDrmSessionManager->getLowestPriority_l(&pid, &priority));
-
-        addSession();
-        EXPECT_TRUE(mDrmSessionManager->getLowestPriority_l(&pid, &priority));
-
-        EXPECT_EQ(kTestPid1, pid);
-        FakeProcessInfo processInfo;
-        int priority1;
-        processInfo.getPriority(kTestPid1, &priority1);
-        EXPECT_EQ(priority1, priority);
-    }
-
-    void testGetLeastUsedSession() {
-        sp<DrmSessionClientInterface> drm;
-        Vector<uint8_t> sessionId;
-        EXPECT_FALSE(mDrmSessionManager->getLeastUsedSession_l(kTestPid1, &drm, &sessionId));
-
-        addSession();
-
-        EXPECT_TRUE(mDrmSessionManager->getLeastUsedSession_l(kTestPid1, &drm, &sessionId));
-        EXPECT_EQ(mTestDrm1, drm);
-        EXPECT_TRUE(isEqualSessionId(mSessionId1, sessionId));
-
-        EXPECT_TRUE(mDrmSessionManager->getLeastUsedSession_l(kTestPid2, &drm, &sessionId));
-        EXPECT_EQ(mTestDrm2, drm);
-        EXPECT_TRUE(isEqualSessionId(mSessionId2, sessionId));
-
-        // mSessionId2 is no longer the least used session.
-        mDrmSessionManager->useSession(mSessionId2);
-        EXPECT_TRUE(mDrmSessionManager->getLeastUsedSession_l(kTestPid2, &drm, &sessionId));
-        EXPECT_EQ(mTestDrm2, drm);
-        EXPECT_TRUE(isEqualSessionId(mSessionId3, sessionId));
-    }
-
+    sp<IResourceManagerService> mService;
     sp<DrmSessionManager> mDrmSessionManager;
     sp<FakeDrm> mTestDrm1;
     sp<FakeDrm> mTestDrm2;
-    Vector<uint8_t> mSessionId1;
-    Vector<uint8_t> mSessionId2;
-    Vector<uint8_t> mSessionId3;
+    sp<FakeDrm> mTestDrm3;
 };
 
 TEST_F(DrmSessionManagerTest, addSession) {
     addSession();
+
+    EXPECT_EQ(3u, mDrmSessionManager->getSessionCount());
+    EXPECT_TRUE(mDrmSessionManager->containsSession(mTestDrm1->mSessionId));
+    EXPECT_TRUE(mDrmSessionManager->containsSession(mTestDrm2->mSessionId));
+    EXPECT_TRUE(mDrmSessionManager->containsSession(mTestDrm3->mSessionId));
 }
 
 TEST_F(DrmSessionManagerTest, useSession) {
     addSession();
 
-    mDrmSessionManager->useSession(mSessionId1);
-    mDrmSessionManager->useSession(mSessionId3);
+    mDrmSessionManager->useSession(mTestDrm1->mSessionId);
+    mDrmSessionManager->useSession(mTestDrm3->mSessionId);
 
-    const PidSessionInfosMap& map = sessionMap();
-    const SessionInfos& infos1 = map.valueFor(kTestPid1);
-    const SessionInfos& infos2 = map.valueFor(kTestPid2);
-    ExpectEqSessionInfo(infos1[0], mTestDrm1, mSessionId1, 3);
-    ExpectEqSessionInfo(infos2[1], mTestDrm2, mSessionId3, 4);
+    EXPECT_EQ(3u, mDrmSessionManager->getSessionCount());
+    EXPECT_TRUE(mDrmSessionManager->containsSession(mTestDrm1->mSessionId));
+    EXPECT_TRUE(mDrmSessionManager->containsSession(mTestDrm2->mSessionId));
+    EXPECT_TRUE(mDrmSessionManager->containsSession(mTestDrm3->mSessionId));
 }
 
 TEST_F(DrmSessionManagerTest, removeSession) {
     addSession();
 
-    mDrmSessionManager->removeSession(mSessionId2);
+    mDrmSessionManager->removeSession(mTestDrm2->mSessionId);
 
-    const PidSessionInfosMap& map = sessionMap();
-    EXPECT_EQ(2u, map.size());
-    const SessionInfos& infos1 = map.valueFor(kTestPid1);
-    const SessionInfos& infos2 = map.valueFor(kTestPid2);
-    EXPECT_EQ(1u, infos1.size());
-    EXPECT_EQ(1u, infos2.size());
-    // mSessionId2 has been removed.
-    ExpectEqSessionInfo(infos2[0], mTestDrm2, mSessionId3, 2);
-}
-
-TEST_F(DrmSessionManagerTest, removeDrm) {
-    addSession();
-
-    sp<FakeDrm> drm = new FakeDrm;
-    const uint8_t ids[] = {123};
-    Vector<uint8_t> sessionId;
-    GetSessionId(ids, ARRAY_SIZE(ids), &sessionId);
-    mDrmSessionManager->addSession(kTestPid2, drm, sessionId);
-
-    mDrmSessionManager->removeDrm(mTestDrm2);
-
-    const PidSessionInfosMap& map = sessionMap();
-    const SessionInfos& infos2 = map.valueFor(kTestPid2);
-    EXPECT_EQ(1u, infos2.size());
-    // mTestDrm2 has been removed.
-    ExpectEqSessionInfo(infos2[0], drm, sessionId, 3);
+    EXPECT_EQ(2u, mDrmSessionManager->getSessionCount());
+    EXPECT_TRUE(mDrmSessionManager->containsSession(mTestDrm1->mSessionId));
+    EXPECT_FALSE(mDrmSessionManager->containsSession(mTestDrm2->mSessionId));
+    EXPECT_TRUE(mDrmSessionManager->containsSession(mTestDrm3->mSessionId));
 }
 
 TEST_F(DrmSessionManagerTest, reclaimSession) {
@@ -224,30 +194,63 @@
     EXPECT_FALSE(mDrmSessionManager->reclaimSession(50));
 
     EXPECT_TRUE(mDrmSessionManager->reclaimSession(10));
-    EXPECT_EQ(1u, mTestDrm1->reclaimedSessions().size());
-    EXPECT_TRUE(isEqualSessionId(mSessionId1, mTestDrm1->reclaimedSessions()[0]));
-
-    mDrmSessionManager->removeSession(mSessionId1);
+    EXPECT_TRUE(mTestDrm1->isReclaimed());
 
     // add a session from a higher priority process.
-    sp<FakeDrm> drm = new FakeDrm;
-    const uint8_t ids[] = {1, 3, 5};
-    Vector<uint8_t> sessionId;
-    GetSessionId(ids, ARRAY_SIZE(ids), &sessionId);
-    mDrmSessionManager->addSession(15, drm, sessionId);
+    const std::vector<uint8_t> sid{1, 3, 5};
+    sp<FakeDrm> drm = new FakeDrm(sid, mDrmSessionManager);
+    mDrmSessionManager->addSession(15, drm, drm->mSessionId);
 
+    // make sure mTestDrm2 is reclaimed next instead of mTestDrm3
+    mDrmSessionManager->useSession(mTestDrm3->mSessionId);
     EXPECT_TRUE(mDrmSessionManager->reclaimSession(18));
-    EXPECT_EQ(1u, mTestDrm2->reclaimedSessions().size());
-    // mSessionId2 is reclaimed.
-    EXPECT_TRUE(isEqualSessionId(mSessionId2, mTestDrm2->reclaimedSessions()[0]));
+    EXPECT_TRUE(mTestDrm2->isReclaimed());
+
+    EXPECT_EQ(2u, mDrmSessionManager->getSessionCount());
+    EXPECT_FALSE(mDrmSessionManager->containsSession(mTestDrm1->mSessionId));
+    EXPECT_FALSE(mDrmSessionManager->containsSession(mTestDrm2->mSessionId));
+    EXPECT_TRUE(mDrmSessionManager->containsSession(mTestDrm3->mSessionId));
+    EXPECT_TRUE(mDrmSessionManager->containsSession(drm->mSessionId));
 }
 
-TEST_F(DrmSessionManagerTest, getLowestPriority) {
-    testGetLowestPriority();
-}
+TEST_F(DrmSessionManagerTest, reclaimAfterUse) {
+    // nothing to reclaim yet
+    EXPECT_FALSE(mDrmSessionManager->reclaimSession(kTestPid1));
+    EXPECT_FALSE(mDrmSessionManager->reclaimSession(kTestPid2));
 
-TEST_F(DrmSessionManagerTest, getLeastUsedSession_l) {
-    testGetLeastUsedSession();
+    // add sessions from same pid
+    mDrmSessionManager->addSession(kTestPid2, mTestDrm1, mTestDrm1->mSessionId);
+    mDrmSessionManager->addSession(kTestPid2, mTestDrm2, mTestDrm2->mSessionId);
+    mDrmSessionManager->addSession(kTestPid2, mTestDrm3, mTestDrm3->mSessionId);
+
+    // use some but not all sessions
+    mDrmSessionManager->useSession(mTestDrm1->mSessionId);
+    mDrmSessionManager->useSession(mTestDrm1->mSessionId);
+    mDrmSessionManager->useSession(mTestDrm2->mSessionId);
+
+    // calling pid priority is too low
+    int lowPriorityPid = kTestPid2 + 1;
+    EXPECT_FALSE(mDrmSessionManager->reclaimSession(lowPriorityPid));
+
+    // unused session is reclaimed first
+    int highPriorityPid = kTestPid2 - 1;
+    EXPECT_TRUE(mDrmSessionManager->reclaimSession(highPriorityPid));
+    EXPECT_FALSE(mTestDrm1->isReclaimed());
+    EXPECT_FALSE(mTestDrm2->isReclaimed());
+    EXPECT_TRUE(mTestDrm3->isReclaimed());
+    mDrmSessionManager->removeSession(mTestDrm3->mSessionId);
+
+    // less-used session is reclaimed next
+    EXPECT_TRUE(mDrmSessionManager->reclaimSession(highPriorityPid));
+    EXPECT_FALSE(mTestDrm1->isReclaimed());
+    EXPECT_TRUE(mTestDrm2->isReclaimed());
+    EXPECT_TRUE(mTestDrm3->isReclaimed());
+
+    // most-used session still open
+    EXPECT_EQ(1u, mDrmSessionManager->getSessionCount());
+    EXPECT_TRUE(mDrmSessionManager->containsSession(mTestDrm1->mSessionId));
+    EXPECT_FALSE(mDrmSessionManager->containsSession(mTestDrm2->mSessionId));
+    EXPECT_FALSE(mDrmSessionManager->containsSession(mTestDrm3->mSessionId));
 }
 
 } // namespace android
diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp
index 3d6ebc3..77eace9 100644
--- a/media/libstagefright/MediaCodec.cpp
+++ b/media/libstagefright/MediaCodec.cpp
@@ -527,7 +527,7 @@
       mFlags(0),
       mStickyError(OK),
       mSoftRenderer(NULL),
-      mAnalyticsItem(NULL),
+      mMetricsHandle(0),
       mIsVideo(false),
       mVideoWidth(0),
       mVideoHeight(0),
@@ -548,19 +548,19 @@
     mResourceManagerClient = new ResourceManagerClient(this);
     mResourceManagerService = new ResourceManagerServiceProxy(pid, mUid);
 
-    initAnalyticsItem();
+    initMediametrics();
 }
 
 MediaCodec::~MediaCodec() {
     CHECK_EQ(mState, UNINITIALIZED);
     mResourceManagerService->removeClient(getId(mResourceManagerClient));
 
-    flushAnalyticsItem();
+    flushMediametrics();
 }
 
-void MediaCodec::initAnalyticsItem() {
-    if (mAnalyticsItem == NULL) {
-        mAnalyticsItem = MediaAnalyticsItem::create(kCodecKeyName);
+void MediaCodec::initMediametrics() {
+    if (mMetricsHandle == 0) {
+        mMetricsHandle = mediametrics_create(kCodecKeyName);
     }
 
     mLatencyHist.setup(kLatencyHistBuckets, kLatencyHistWidth, kLatencyHistFloor);
@@ -574,38 +574,39 @@
     }
 }
 
-void MediaCodec::updateAnalyticsItem() {
-    ALOGV("MediaCodec::updateAnalyticsItem");
-    if (mAnalyticsItem == NULL) {
+void MediaCodec::updateMediametrics() {
+    ALOGV("MediaCodec::updateMediametrics");
+    if (mMetricsHandle == 0) {
         return;
     }
 
+
     if (mLatencyHist.getCount() != 0 ) {
-        mAnalyticsItem->setInt64(kCodecLatencyMax, mLatencyHist.getMax());
-        mAnalyticsItem->setInt64(kCodecLatencyMin, mLatencyHist.getMin());
-        mAnalyticsItem->setInt64(kCodecLatencyAvg, mLatencyHist.getAvg());
-        mAnalyticsItem->setInt64(kCodecLatencyCount, mLatencyHist.getCount());
+        mediametrics_setInt64(mMetricsHandle, kCodecLatencyMax, mLatencyHist.getMax());
+        mediametrics_setInt64(mMetricsHandle, kCodecLatencyMin, mLatencyHist.getMin());
+        mediametrics_setInt64(mMetricsHandle, kCodecLatencyAvg, mLatencyHist.getAvg());
+        mediametrics_setInt64(mMetricsHandle, kCodecLatencyCount, mLatencyHist.getCount());
 
         if (kEmitHistogram) {
             // and the histogram itself
             std::string hist = mLatencyHist.emit();
-            mAnalyticsItem->setCString(kCodecLatencyHist, hist.c_str());
+            mediametrics_setCString(mMetricsHandle, kCodecLatencyHist, hist.c_str());
         }
     }
     if (mLatencyUnknown > 0) {
-        mAnalyticsItem->setInt64(kCodecLatencyUnknown, mLatencyUnknown);
+        mediametrics_setInt64(mMetricsHandle, kCodecLatencyUnknown, mLatencyUnknown);
     }
 
 #if 0
     // enable for short term, only while debugging
-    updateEphemeralAnalytics(mAnalyticsItem);
+    updateEphemeralMediametrics(mMetricsHandle);
 #endif
 }
 
-void MediaCodec::updateEphemeralAnalytics(MediaAnalyticsItem *item) {
-    ALOGD("MediaCodec::updateEphemeralAnalytics()");
+void MediaCodec::updateEphemeralMediametrics(mediametrics_handle_t item) {
+    ALOGD("MediaCodec::updateEphemeralMediametrics()");
 
-    if (item == NULL) {
+    if (item == 0) {
         return;
     }
 
@@ -628,28 +629,27 @@
 
     // spit the data (if any) into the supplied analytics record
     if (recentHist.getCount()!= 0 ) {
-        item->setInt64(kCodecRecentLatencyMax, recentHist.getMax());
-        item->setInt64(kCodecRecentLatencyMin, recentHist.getMin());
-        item->setInt64(kCodecRecentLatencyAvg, recentHist.getAvg());
-        item->setInt64(kCodecRecentLatencyCount, recentHist.getCount());
+        mediametrics_setInt64(item, kCodecRecentLatencyMax, recentHist.getMax());
+        mediametrics_setInt64(item, kCodecRecentLatencyMin, recentHist.getMin());
+        mediametrics_setInt64(item, kCodecRecentLatencyAvg, recentHist.getAvg());
+        mediametrics_setInt64(item, kCodecRecentLatencyCount, recentHist.getCount());
 
         if (kEmitHistogram) {
             // and the histogram itself
             std::string hist = recentHist.emit();
-            item->setCString(kCodecRecentLatencyHist, hist.c_str());
+            mediametrics_setCString(item, kCodecRecentLatencyHist, hist.c_str());
         }
     }
 }
 
-void MediaCodec::flushAnalyticsItem() {
-    updateAnalyticsItem();
-    if (mAnalyticsItem != NULL) {
-        // don't log empty records
-        if (mAnalyticsItem->count() > 0) {
-            mAnalyticsItem->selfrecord();
+void MediaCodec::flushMediametrics() {
+    updateMediametrics();
+    if (mMetricsHandle != 0) {
+        if (mediametrics_count(mMetricsHandle) > 0) {
+            mediametrics_selfRecord(mMetricsHandle);
         }
-        delete mAnalyticsItem;
-        mAnalyticsItem = NULL;
+        mediametrics_delete(mMetricsHandle);
+        mMetricsHandle = 0;
     }
 }
 
@@ -981,9 +981,10 @@
     // ".secure"
     msg->setString("name", name);
 
-    if (mAnalyticsItem != NULL) {
-        mAnalyticsItem->setCString(kCodecCodec, name.c_str());
-        mAnalyticsItem->setCString(kCodecMode, mIsVideo ? kCodecModeVideo : kCodecModeAudio);
+    if (mMetricsHandle != 0) {
+        mediametrics_setCString(mMetricsHandle, kCodecCodec, name.c_str());
+        mediametrics_setCString(mMetricsHandle, kCodecMode,
+                                mIsVideo ? kCodecModeVideo : kCodecModeAudio);
     }
 
     if (mIsVideo) {
@@ -1044,16 +1045,17 @@
         uint32_t flags) {
     sp<AMessage> msg = new AMessage(kWhatConfigure, this);
 
-    if (mAnalyticsItem != NULL) {
+    if (mMetricsHandle != 0) {
         int32_t profile = 0;
         if (format->findInt32("profile", &profile)) {
-            mAnalyticsItem->setInt32(kCodecProfile, profile);
+            mediametrics_setInt32(mMetricsHandle, kCodecProfile, profile);
         }
         int32_t level = 0;
         if (format->findInt32("level", &level)) {
-            mAnalyticsItem->setInt32(kCodecLevel, level);
+            mediametrics_setInt32(mMetricsHandle, kCodecLevel, level);
         }
-        mAnalyticsItem->setInt32(kCodecEncoder, (flags & CONFIGURE_FLAG_ENCODE) ? 1 : 0);
+        mediametrics_setInt32(mMetricsHandle, kCodecEncoder,
+                              (flags & CONFIGURE_FLAG_ENCODE) ? 1 : 0);
     }
 
     if (mIsVideo) {
@@ -1063,17 +1065,17 @@
             mRotationDegrees = 0;
         }
 
-        if (mAnalyticsItem != NULL) {
-            mAnalyticsItem->setInt32(kCodecWidth, mVideoWidth);
-            mAnalyticsItem->setInt32(kCodecHeight, mVideoHeight);
-            mAnalyticsItem->setInt32(kCodecRotation, mRotationDegrees);
+        if (mMetricsHandle != 0) {
+            mediametrics_setInt32(mMetricsHandle, kCodecWidth, mVideoWidth);
+            mediametrics_setInt32(mMetricsHandle, kCodecHeight, mVideoHeight);
+            mediametrics_setInt32(mMetricsHandle, kCodecRotation, mRotationDegrees);
             int32_t maxWidth = 0;
             if (format->findInt32("max-width", &maxWidth)) {
-                mAnalyticsItem->setInt32(kCodecMaxWidth, maxWidth);
+                mediametrics_setInt32(mMetricsHandle, kCodecMaxWidth, maxWidth);
             }
             int32_t maxHeight = 0;
             if (format->findInt32("max-height", &maxHeight)) {
-                mAnalyticsItem->setInt32(kCodecMaxHeight, maxHeight);
+                mediametrics_setInt32(mMetricsHandle, kCodecMaxHeight, maxHeight);
             }
         }
 
@@ -1095,8 +1097,8 @@
         } else {
             msg->setPointer("descrambler", descrambler.get());
         }
-        if (mAnalyticsItem != NULL) {
-            mAnalyticsItem->setInt32(kCodecCrypto, 1);
+        if (mMetricsHandle != 0) {
+            mediametrics_setInt32(mMetricsHandle, kCodecCrypto, 1);
         }
     } else if (mFlags & kFlagIsSecure) {
         ALOGW("Crypto or descrambler should be given for secure codec");
@@ -1561,22 +1563,22 @@
     return OK;
 }
 
-status_t MediaCodec::getMetrics(MediaAnalyticsItem * &reply) {
+status_t MediaCodec::getMetrics(mediametrics_handle_t &reply) {
 
-    reply = NULL;
+    reply = 0;
 
     // shouldn't happen, but be safe
-    if (mAnalyticsItem == NULL) {
+    if (mMetricsHandle == 0) {
         return UNKNOWN_ERROR;
     }
 
     // update any in-flight data that's not carried within the record
-    updateAnalyticsItem();
+    updateMediametrics();
 
     // send it back to the caller.
-    reply = mAnalyticsItem->dup();
+    reply = mediametrics_dup(mMetricsHandle);
 
-    updateEphemeralAnalytics(reply);
+    updateEphemeralMediametrics(reply);
 
     return OK;
 }
@@ -1890,10 +1892,11 @@
                         case CONFIGURING:
                         {
                             if (actionCode == ACTION_CODE_FATAL) {
-                                mAnalyticsItem->setInt32(kCodecError, err);
-                                mAnalyticsItem->setCString(kCodecErrorState, stateString(mState).c_str());
-                                flushAnalyticsItem();
-                                initAnalyticsItem();
+                                mediametrics_setInt32(mMetricsHandle, kCodecError, err);
+                                mediametrics_setCString(mMetricsHandle, kCodecErrorState,
+                                                        stateString(mState).c_str());
+                                flushMediametrics();
+                                initMediametrics();
                             }
                             setState(actionCode == ACTION_CODE_FATAL ?
                                     UNINITIALIZED : INITIALIZED);
@@ -1903,10 +1906,11 @@
                         case STARTING:
                         {
                             if (actionCode == ACTION_CODE_FATAL) {
-                                mAnalyticsItem->setInt32(kCodecError, err);
-                                mAnalyticsItem->setCString(kCodecErrorState, stateString(mState).c_str());
-                                flushAnalyticsItem();
-                                initAnalyticsItem();
+                                mediametrics_setInt32(mMetricsHandle, kCodecError, err);
+                                mediametrics_setCString(mMetricsHandle, kCodecErrorState,
+                                                        stateString(mState).c_str());
+                                flushMediametrics();
+                                initMediametrics();
                             }
                             setState(actionCode == ACTION_CODE_FATAL ?
                                     UNINITIALIZED : CONFIGURED);
@@ -1944,10 +1948,11 @@
                         case FLUSHING:
                         {
                             if (actionCode == ACTION_CODE_FATAL) {
-                                mAnalyticsItem->setInt32(kCodecError, err);
-                                mAnalyticsItem->setCString(kCodecErrorState, stateString(mState).c_str());
-                                flushAnalyticsItem();
-                                initAnalyticsItem();
+                                mediametrics_setInt32(mMetricsHandle, kCodecError, err);
+                                mediametrics_setCString(mMetricsHandle, kCodecErrorState,
+                                                        stateString(mState).c_str());
+                                flushMediametrics();
+                                initMediametrics();
 
                                 setState(UNINITIALIZED);
                             } else {
@@ -1977,10 +1982,11 @@
                                 setState(INITIALIZED);
                                 break;
                             default:
-                                mAnalyticsItem->setInt32(kCodecError, err);
-                                mAnalyticsItem->setCString(kCodecErrorState, stateString(mState).c_str());
-                                flushAnalyticsItem();
-                                initAnalyticsItem();
+                                mediametrics_setInt32(mMetricsHandle, kCodecError, err);
+                                mediametrics_setCString(mMetricsHandle, kCodecErrorState,
+                                                        stateString(mState).c_str());
+                                flushMediametrics();
+                                initMediametrics();
                                 setState(UNINITIALIZED);
                                 break;
                             }
@@ -2037,7 +2043,8 @@
                     CHECK(msg->findString("componentName", &mComponentName));
 
                     if (mComponentName.c_str()) {
-                        mAnalyticsItem->setCString(kCodecCodec, mComponentName.c_str());
+                        mediametrics_setCString(mMetricsHandle, kCodecCodec,
+                                                mComponentName.c_str());
                     }
 
                     const char *owner = mCodecInfo->getOwnerName();
@@ -2053,11 +2060,11 @@
                     if (mComponentName.endsWith(".secure")) {
                         mFlags |= kFlagIsSecure;
                         resourceType = MediaResource::kSecureCodec;
-                        mAnalyticsItem->setInt32(kCodecSecure, 1);
+                        mediametrics_setInt32(mMetricsHandle, kCodecSecure, 1);
                     } else {
                         mFlags &= ~kFlagIsSecure;
                         resourceType = MediaResource::kNonSecureCodec;
-                        mAnalyticsItem->setInt32(kCodecSecure, 0);
+                        mediametrics_setInt32(mMetricsHandle, kCodecSecure, 0);
                     }
 
                     if (mIsVideo) {
@@ -2105,14 +2112,15 @@
                     (new AMessage)->postReply(mReplyID);
 
                     // augment our media metrics info, now that we know more things
-                    if (mAnalyticsItem != NULL) {
+                    if (mMetricsHandle != 0) {
                         sp<AMessage> format;
                         if (mConfigureMsg != NULL &&
                             mConfigureMsg->findMessage("format", &format)) {
                                 // format includes: mime
                                 AString mime;
                                 if (format->findString("mime", &mime)) {
-                                    mAnalyticsItem->setCString(kCodecMime, mime.c_str());
+                                    mediametrics_setCString(mMetricsHandle, kCodecMime,
+                                                            mime.c_str());
                                 }
                             }
                     }
diff --git a/media/libstagefright/codecs/amrnb/dec/test/AmrnbDecTestEnvironment.h b/media/libstagefright/codecs/amrnb/dec/test/AmrnbDecTestEnvironment.h
new file mode 100644
index 0000000..0344ac5
--- /dev/null
+++ b/media/libstagefright/codecs/amrnb/dec/test/AmrnbDecTestEnvironment.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __AMRNBDEC_TEST_ENVIRONMENT_H__
+#define __AMRNBDEC_TEST_ENVIRONMENT_H__
+
+#include <gtest/gtest.h>
+
+#include <getopt.h>
+
+using namespace std;
+
+class AmrnbDecTestEnvironment : public ::testing::Environment {
+  public:
+    AmrnbDecTestEnvironment() : res("/data/local/tmp/") {}
+
+    // Parses the command line arguments
+    int initFromOptions(int argc, char **argv);
+
+    void setRes(const char *_res) { res = _res; }
+
+    const string getRes() const { return res; }
+
+  private:
+    string res;
+};
+
+int AmrnbDecTestEnvironment::initFromOptions(int argc, char **argv) {
+    static struct option options[] = {{"res", required_argument, 0, 'P'}, {0, 0, 0, 0}};
+
+    while (true) {
+        int index = 0;
+        int c = getopt_long(argc, argv, "P:", options, &index);
+        if (c == -1) {
+            break;
+        }
+
+        switch (c) {
+            case 'P':
+                setRes(optarg);
+                break;
+            default:
+                break;
+        }
+    }
+
+    if (optind < argc) {
+        fprintf(stderr,
+                "unrecognized option: %s\n\n"
+                "usage: %s <gtest options> <test options>\n\n"
+                "test options are:\n\n"
+                "-P, --path: Resource files directory location\n",
+                argv[optind ?: 1], argv[0]);
+        return 2;
+    }
+    return 0;
+}
+
+#endif  // __AMRNBDEC_TEST_ENVIRONMENT_H__
diff --git a/media/libstagefright/codecs/amrnb/dec/test/AmrnbDecoderTest.cpp b/media/libstagefright/codecs/amrnb/dec/test/AmrnbDecoderTest.cpp
new file mode 100644
index 0000000..af62074
--- /dev/null
+++ b/media/libstagefright/codecs/amrnb/dec/test/AmrnbDecoderTest.cpp
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "AmrnbDecoderTest"
+#define OUTPUT_FILE "/data/local/tmp/amrnbDecode.out"
+
+#include <utils/Log.h>
+
+#include <audio_utils/sndfile.h>
+#include <stdio.h>
+
+#include "gsmamr_dec.h"
+
+#include "AmrnbDecTestEnvironment.h"
+
+// Constants for AMR-NB
+constexpr int32_t kInputBufferSize = 64;
+constexpr int32_t kSamplesPerFrame = L_FRAME;
+constexpr int32_t kBitsPerSample = 16;
+constexpr int32_t kSampleRate = 8000;
+constexpr int32_t kChannels = 1;
+constexpr int32_t kOutputBufferSize = kSamplesPerFrame * kBitsPerSample / 8;
+const int32_t kFrameSizes[] = {12, 13, 15, 17, 19, 20, 26, 31, -1, -1, -1, -1, -1, -1, -1, -1};
+
+constexpr int32_t kNumFrameReset = 150;
+
+static AmrnbDecTestEnvironment *gEnv = nullptr;
+
+class AmrnbDecoderTest : public ::testing::TestWithParam<string> {
+  public:
+    AmrnbDecoderTest() : mFpInput(nullptr) {}
+
+    ~AmrnbDecoderTest() {
+        if (mFpInput) {
+            fclose(mFpInput);
+            mFpInput = nullptr;
+        }
+    }
+
+    FILE *mFpInput;
+    SNDFILE *openOutputFile(SF_INFO *sfInfo);
+    int32_t DecodeFrames(void *amrHandle, SNDFILE *outFileHandle, int32_t frameCount = INT32_MAX);
+};
+
+SNDFILE *AmrnbDecoderTest::openOutputFile(SF_INFO *sfInfo) {
+    memset(sfInfo, 0, sizeof(SF_INFO));
+    sfInfo->channels = kChannels;
+    sfInfo->format = SF_FORMAT_WAV | SF_FORMAT_PCM_16;
+    sfInfo->samplerate = kSampleRate;
+    SNDFILE *outFileHandle = sf_open(OUTPUT_FILE, SFM_WRITE, sfInfo);
+    return outFileHandle;
+}
+
+int32_t AmrnbDecoderTest::DecodeFrames(void *amrHandle, SNDFILE *outFileHandle,
+                                       int32_t frameCount) {
+    uint8_t inputBuf[kInputBufferSize];
+    int16_t outputBuf[kOutputBufferSize];
+
+    while (frameCount > 0) {
+        uint8_t mode;
+        int32_t bytesRead = fread(&mode, 1, 1, mFpInput);
+        if (bytesRead != 1) break;
+
+        // Find frame type
+        Frame_Type_3GPP frameType = (Frame_Type_3GPP)((mode >> 3) & 0x0f);
+        int32_t frameSize = kFrameSizes[frameType];
+        if (frameSize < 0) {
+            ALOGE("Illegal frame type");
+            return -1;
+        }
+        bytesRead = fread(inputBuf, 1, frameSize, mFpInput);
+        if (bytesRead != frameSize) break;
+
+        int32_t bytesDecoded = AMRDecode(amrHandle, frameType, inputBuf, outputBuf, MIME_IETF);
+        if (bytesDecoded == -1) {
+            ALOGE("Failed to decode the input file");
+            return -1;
+        }
+
+        sf_writef_short(outFileHandle, outputBuf, kSamplesPerFrame);
+        frameCount--;
+    }
+    return 0;
+}
+
+TEST_F(AmrnbDecoderTest, CreateAmrnbDecoderTest) {
+    void *amrHandle;
+    int32_t status = GSMInitDecode(&amrHandle, (Word8 *)"AMRNBDecoder");
+    ASSERT_EQ(status, 0) << "Error creating AMR-NB decoder";
+    GSMDecodeFrameExit(&amrHandle);
+    ASSERT_EQ(amrHandle, nullptr) << "Error deleting AMR-NB decoder";
+}
+
+TEST_P(AmrnbDecoderTest, DecodeTest) {
+    string inputFile = gEnv->getRes() + GetParam();
+    mFpInput = fopen(inputFile.c_str(), "rb");
+    ASSERT_NE(mFpInput, nullptr) << "Error opening input file " << inputFile;
+
+    // Open the output file.
+    SF_INFO sfInfo;
+    SNDFILE *outFileHandle = openOutputFile(&sfInfo);
+    ASSERT_NE(outFileHandle, nullptr) << "Error opening output file for writing decoded output";
+
+    void *amrHandle;
+    int32_t status = GSMInitDecode(&amrHandle, (Word8 *)"AMRNBDecoder");
+    ASSERT_EQ(status, 0) << "Error creating AMR-NB decoder";
+
+    // Decode
+    int32_t decoderErr = DecodeFrames(amrHandle, outFileHandle);
+    ASSERT_EQ(decoderErr, 0) << "DecodeFrames returned error";
+
+    sf_close(outFileHandle);
+    GSMDecodeFrameExit(&amrHandle);
+    ASSERT_EQ(amrHandle, nullptr) << "Error deleting AMR-NB decoder";
+}
+
+TEST_P(AmrnbDecoderTest, ResetDecodeTest) {
+    string inputFile = gEnv->getRes() + GetParam();
+    mFpInput = fopen(inputFile.c_str(), "rb");
+    ASSERT_NE(mFpInput, nullptr) << "Error opening input file " << inputFile;
+
+    // Open the output file.
+    SF_INFO sfInfo;
+    SNDFILE *outFileHandle = openOutputFile(&sfInfo);
+    ASSERT_NE(outFileHandle, nullptr) << "Error opening output file for writing decoded output";
+
+    void *amrHandle;
+    int32_t status = GSMInitDecode(&amrHandle, (Word8 *)"AMRNBDecoder");
+    ASSERT_EQ(status, 0) << "Error creating AMR-NB decoder";
+
+    // Decode kNumFrameReset first
+    int32_t decoderErr = DecodeFrames(amrHandle, outFileHandle, kNumFrameReset);
+    ASSERT_EQ(decoderErr, 0) << "DecodeFrames returned error";
+
+    status = Speech_Decode_Frame_reset(amrHandle);
+    ASSERT_EQ(status, 0) << "Error resting AMR-NB decoder";
+
+    // Start decoding again
+    decoderErr = DecodeFrames(amrHandle, outFileHandle);
+    ASSERT_EQ(decoderErr, 0) << "DecodeFrames returned error";
+
+    sf_close(outFileHandle);
+    GSMDecodeFrameExit(&amrHandle);
+    ASSERT_EQ(amrHandle, nullptr) << "Error deleting AMR-NB decoder";
+}
+
+INSTANTIATE_TEST_SUITE_P(AmrnbDecoderTestAll, AmrnbDecoderTest,
+                         ::testing::Values(("bbb_8000hz_1ch_8kbps_amrnb_30sec.amrnb"),
+                                           ("sine_amrnb_1ch_12kbps_8000hz.amrnb")));
+
+int main(int argc, char **argv) {
+    gEnv = new AmrnbDecTestEnvironment();
+    ::testing::AddGlobalTestEnvironment(gEnv);
+    ::testing::InitGoogleTest(&argc, argv);
+    int status = gEnv->initFromOptions(argc, argv);
+    if (status == 0) {
+        status = RUN_ALL_TESTS();
+        ALOGV("Test result = %d\n", status);
+    }
+    return status;
+}
diff --git a/media/libstagefright/codecs/amrnb/dec/test/Android.bp b/media/libstagefright/codecs/amrnb/dec/test/Android.bp
new file mode 100644
index 0000000..7a95cfa
--- /dev/null
+++ b/media/libstagefright/codecs/amrnb/dec/test/Android.bp
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+cc_test {
+    name: "AmrnbDecoderTest",
+    gtest: true,
+
+    srcs: [
+        "AmrnbDecoderTest.cpp",
+    ],
+
+    static_libs: [
+        "libstagefright_amrnb_common",
+        "libstagefright_amrnbdec",
+        "libaudioutils",
+        "libsndfile",
+    ],
+
+    shared_libs: [
+        "liblog",
+    ],
+
+    cflags: [
+        "-Werror",
+        "-Wall",
+    ],
+
+    sanitize: {
+        cfi: true,
+        misc_undefined: [
+            "unsigned-integer-overflow",
+            "signed-integer-overflow",
+        ],
+    },
+}
diff --git a/media/libstagefright/codecs/amrnb/dec/test/README.md b/media/libstagefright/codecs/amrnb/dec/test/README.md
new file mode 100644
index 0000000..62e13ae
--- /dev/null
+++ b/media/libstagefright/codecs/amrnb/dec/test/README.md
@@ -0,0 +1,34 @@
+## Media Testing ##
+---
+#### AMR-NB Decoder :
+The Amr-Nb Decoder Test Suite validates the amrnb decoder available in libstagefright.
+
+Run the following steps to build the test suite:
+```
+m AmrnbDecoderTest
+```
+
+The 32-bit binaries will be created in the following path : ${OUT}/data/nativetest/
+
+The 64-bit binaries will be created in the following path : ${OUT}/data/nativetest64/
+
+To test 64-bit binary push binaries from nativetest64.
+```
+adb push ${OUT}/data/nativetest64/AmrnbDecoderTest/AmrnbDecoderTest /data/local/tmp/
+```
+
+To test 32-bit binary push binaries from nativetest.
+```
+adb push ${OUT}/data/nativetest/AmrnbDecoderTest/AmrnbDecoderTest /data/local/tmp/
+```
+
+The resource file for the tests is taken from [here](https://drive.google.com/drive/folders/13cM4tAaVFrmr-zGFqaAzFBbKs75pnm9b). Push these files into device for testing.
+Download amr-nb folder and push all the files in this folder to /data/local/tmp/ on the device.
+```
+adb push amr-nb/. /data/local/tmp/
+```
+
+usage: AmrnbDecoderTest -P \<path_to_folder\>
+```
+adb shell /data/local/tmp/AmrnbDecoderTest -P /data/local/tmp/
+```
diff --git a/media/libstagefright/codecs/amrwb/test/AmrwbDecTestEnvironment.h b/media/libstagefright/codecs/amrwb/test/AmrwbDecTestEnvironment.h
new file mode 100644
index 0000000..84d337d
--- /dev/null
+++ b/media/libstagefright/codecs/amrwb/test/AmrwbDecTestEnvironment.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __AMRWBDEC_TEST_ENVIRONMENT_H__
+#define __AMRWBDEC_TEST_ENVIRONMENT_H__
+
+#include <gtest/gtest.h>
+
+#include <getopt.h>
+
+using namespace std;
+
+class AmrwbDecTestEnvironment : public ::testing::Environment {
+  public:
+    AmrwbDecTestEnvironment() : res("/data/local/tmp/") {}
+
+    // Parses the command line arguments
+    int initFromOptions(int argc, char **argv);
+
+    void setRes(const char *_res) { res = _res; }
+
+    const string getRes() const { return res; }
+
+  private:
+    string res;
+};
+
+int AmrwbDecTestEnvironment::initFromOptions(int argc, char **argv) {
+    static struct option options[] = {{"res", required_argument, 0, 'P'}, {0, 0, 0, 0}};
+
+    while (true) {
+        int index = 0;
+        int c = getopt_long(argc, argv, "P:", options, &index);
+        if (c == -1) {
+            break;
+        }
+
+        switch (c) {
+            case 'P':
+                setRes(optarg);
+                break;
+            default:
+                break;
+        }
+    }
+
+    if (optind < argc) {
+        fprintf(stderr,
+                "unrecognized option: %s\n\n"
+                "usage: %s <gtest options> <test options>\n\n"
+                "test options are:\n\n"
+                "-P, --path: Resource files directory location\n",
+                argv[optind ?: 1], argv[0]);
+        return 2;
+    }
+    return 0;
+}
+
+#endif  // __AMRWBDEC_TEST_ENVIRONMENT_H__
diff --git a/media/libstagefright/codecs/amrwb/test/AmrwbDecoderTest.cpp b/media/libstagefright/codecs/amrwb/test/AmrwbDecoderTest.cpp
new file mode 100644
index 0000000..2aad81b
--- /dev/null
+++ b/media/libstagefright/codecs/amrwb/test/AmrwbDecoderTest.cpp
@@ -0,0 +1,223 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "AmrwbDecoderTest"
+#define OUTPUT_FILE "/data/local/tmp/amrwbDecode.out"
+
+#include <utils/Log.h>
+
+#include <audio_utils/sndfile.h>
+#include <stdio.h>
+
+#include "pvamrwbdecoder.h"
+#include "pvamrwbdecoder_api.h"
+
+#include "AmrwbDecTestEnvironment.h"
+
+// Constants for AMR-WB.
+constexpr int32_t kInputBufferSize = 64;
+constexpr int32_t kSamplesPerFrame = 320;
+constexpr int32_t kBitsPerSample = 16;
+constexpr int32_t kSampleRate = 16000;
+constexpr int32_t kChannels = 1;
+constexpr int32_t kMaxSourceDataUnitSize = KAMRWB_NB_BITS_MAX * sizeof(int16_t);
+constexpr int32_t kOutputBufferSize = kSamplesPerFrame * kBitsPerSample / 8;
+const int32_t kFrameSizes[16] = {17, 23, 32, 36, 40, 46, 50, 58, 60, -1, -1, -1, -1, -1, -1, -1};
+constexpr int32_t kNumFrameReset = 150;
+
+constexpr int32_t kMaxCount = 10;
+
+static AmrwbDecTestEnvironment *gEnv = nullptr;
+
+class AmrwbDecoderTest : public ::testing::TestWithParam<string> {
+  public:
+    AmrwbDecoderTest() : mFpInput(nullptr) {}
+
+    ~AmrwbDecoderTest() {
+        if (mFpInput) {
+            fclose(mFpInput);
+            mFpInput = nullptr;
+        }
+    }
+
+    FILE *mFpInput;
+    int32_t DecodeFrames(int16_t *decoderCookie, void *decoderBuf, SNDFILE *outFileHandle,
+                         int32_t frameCount = INT32_MAX);
+    SNDFILE *openOutputFile(SF_INFO *sfInfo);
+};
+
+SNDFILE *AmrwbDecoderTest::openOutputFile(SF_INFO *sfInfo) {
+    memset(sfInfo, 0, sizeof(SF_INFO));
+    sfInfo->channels = kChannels;
+    sfInfo->format = SF_FORMAT_WAV | SF_FORMAT_PCM_16;
+    sfInfo->samplerate = kSampleRate;
+    SNDFILE *outFileHandle = sf_open(OUTPUT_FILE, SFM_WRITE, sfInfo);
+    return outFileHandle;
+}
+
+int32_t AmrwbDecoderTest::DecodeFrames(int16_t *decoderCookie, void *decoderBuf,
+                                       SNDFILE *outFileHandle, int32_t frameCount) {
+    uint8_t inputBuf[kInputBufferSize];
+    int16_t inputSampleBuf[kMaxSourceDataUnitSize];
+    int16_t outputBuf[kOutputBufferSize];
+
+    while (frameCount > 0) {
+        uint8_t modeByte;
+        int32_t bytesRead = fread(&modeByte, 1, 1, mFpInput);
+        if (bytesRead != 1) break;
+
+        int16 mode = ((modeByte >> 3) & 0x0f);
+        if (mode >= 9) {
+            // Produce silence for comfort noise, speech lost and no data.
+            int32_t outputBufferSize = kSamplesPerFrame * kBitsPerSample / 8;
+            memset(outputBuf, 0, outputBufferSize);
+        } else {
+            // Read rest of the frame.
+            int32_t frameSize = kFrameSizes[mode];
+            // AMR-WB file format cannot have mode 10, 11, 12 and 13.
+            if (frameSize < 0) {
+                ALOGE("Illegal frame mode");
+                return -1;
+            }
+            bytesRead = fread(inputBuf, 1, frameSize, mFpInput);
+            if (bytesRead != frameSize) break;
+
+            int16 frameMode = mode;
+            int16 frameType;
+            RX_State_wb rx_state;
+            mime_unsorting(inputBuf, inputSampleBuf, &frameType, &frameMode, 1, &rx_state);
+
+            int16_t numSamplesOutput;
+            pvDecoder_AmrWb(frameMode, inputSampleBuf, outputBuf, &numSamplesOutput, decoderBuf,
+                            frameType, decoderCookie);
+            if (numSamplesOutput != kSamplesPerFrame) {
+                ALOGE("Failed to decode the input file");
+                return -1;
+            }
+            for (int count = 0; count < kSamplesPerFrame; ++count) {
+                /* Delete the 2 LSBs (14-bit output) */
+                outputBuf[count] &= 0xfffc;
+            }
+        }
+        sf_writef_short(outFileHandle, outputBuf, kSamplesPerFrame / kChannels);
+        frameCount--;
+    }
+    return 0;
+}
+
+TEST_F(AmrwbDecoderTest, MultiCreateAmrwbDecoderTest) {
+    uint32_t memRequirements = pvDecoder_AmrWbMemRequirements();
+    void *decoderBuf = malloc(memRequirements);
+    ASSERT_NE(decoderBuf, nullptr)
+            << "Failed to allocate decoder memory of size " << memRequirements;
+
+    // Create AMR-WB decoder instance.
+    void *amrHandle = nullptr;
+    int16_t *decoderCookie;
+    for (int count = 0; count < kMaxCount; count++) {
+        pvDecoder_AmrWb_Init(&amrHandle, decoderBuf, &decoderCookie);
+        ASSERT_NE(amrHandle, nullptr) << "Failed to initialize decoder";
+        ALOGV("Decoder created successfully");
+    }
+    if (decoderBuf) {
+        free(decoderBuf);
+        decoderBuf = nullptr;
+    }
+}
+
+TEST_P(AmrwbDecoderTest, DecodeTest) {
+    uint32_t memRequirements = pvDecoder_AmrWbMemRequirements();
+    void *decoderBuf = malloc(memRequirements);
+    ASSERT_NE(decoderBuf, nullptr)
+            << "Failed to allocate decoder memory of size " << memRequirements;
+
+    void *amrHandle = nullptr;
+    int16_t *decoderCookie;
+    pvDecoder_AmrWb_Init(&amrHandle, decoderBuf, &decoderCookie);
+    ASSERT_NE(amrHandle, nullptr) << "Failed to initialize decoder";
+
+    string inputFile = gEnv->getRes() + GetParam();
+    mFpInput = fopen(inputFile.c_str(), "rb");
+    ASSERT_NE(mFpInput, nullptr) << "Error opening input file " << inputFile;
+
+    // Open the output file.
+    SF_INFO sfInfo;
+    SNDFILE *outFileHandle = openOutputFile(&sfInfo);
+    ASSERT_NE(outFileHandle, nullptr) << "Error opening output file for writing decoded output";
+
+    int32_t decoderErr = DecodeFrames(decoderCookie, decoderBuf, outFileHandle);
+    ASSERT_EQ(decoderErr, 0) << "DecodeFrames returned error";
+
+    sf_close(outFileHandle);
+    if (decoderBuf) {
+        free(decoderBuf);
+        decoderBuf = nullptr;
+    }
+}
+
+TEST_P(AmrwbDecoderTest, ResetDecoderTest) {
+    uint32_t memRequirements = pvDecoder_AmrWbMemRequirements();
+    void *decoderBuf = malloc(memRequirements);
+    ASSERT_NE(decoderBuf, nullptr)
+            << "Failed to allocate decoder memory of size " << memRequirements;
+
+    void *amrHandle = nullptr;
+    int16_t *decoderCookie;
+    pvDecoder_AmrWb_Init(&amrHandle, decoderBuf, &decoderCookie);
+    ASSERT_NE(amrHandle, nullptr) << "Failed to initialize decoder";
+
+    string inputFile = gEnv->getRes() + GetParam();
+    mFpInput = fopen(inputFile.c_str(), "rb");
+    ASSERT_NE(mFpInput, nullptr) << "Error opening input file " << inputFile;
+
+    // Open the output file.
+    SF_INFO sfInfo;
+    SNDFILE *outFileHandle = openOutputFile(&sfInfo);
+    ASSERT_NE(outFileHandle, nullptr) << "Error opening output file for writing decoded output";
+
+    // Decode 150 frames first
+    int32_t decoderErr = DecodeFrames(decoderCookie, decoderBuf, outFileHandle, kNumFrameReset);
+    ASSERT_EQ(decoderErr, 0) << "DecodeFrames returned error";
+
+    // Reset Decoder
+    pvDecoder_AmrWb_Reset(decoderBuf, 1);
+
+    // Start decoding again
+    decoderErr = DecodeFrames(decoderCookie, decoderBuf, outFileHandle);
+    ASSERT_EQ(decoderErr, 0) << "DecodeFrames returned error";
+
+    sf_close(outFileHandle);
+    if (decoderBuf) {
+        free(decoderBuf);
+    }
+}
+
+INSTANTIATE_TEST_SUITE_P(AmrwbDecoderTestAll, AmrwbDecoderTest,
+                         ::testing::Values(("bbb_amrwb_1ch_14kbps_16000hz.amrwb"),
+                                           ("bbb_16000hz_1ch_9kbps_amrwb_30sec.amrwb")));
+
+int main(int argc, char **argv) {
+    gEnv = new AmrwbDecTestEnvironment();
+    ::testing::AddGlobalTestEnvironment(gEnv);
+    ::testing::InitGoogleTest(&argc, argv);
+    int status = gEnv->initFromOptions(argc, argv);
+    if (status == 0) {
+        status = RUN_ALL_TESTS();
+        ALOGV("Test result = %d\n", status);
+    }
+    return status;
+}
diff --git a/media/libstagefright/codecs/amrwb/test/Android.bp b/media/libstagefright/codecs/amrwb/test/Android.bp
new file mode 100644
index 0000000..968215a
--- /dev/null
+++ b/media/libstagefright/codecs/amrwb/test/Android.bp
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+cc_test {
+    name: "AmrwbDecoderTest",
+    gtest: true,
+
+    srcs: [
+        "AmrwbDecoderTest.cpp",
+    ],
+
+    static_libs: [
+        "libstagefright_amrwbdec",
+        "libsndfile",
+        "libaudioutils",
+    ],
+
+    shared_libs: [
+        "liblog",
+    ],
+
+    cflags: [
+        "-Werror",
+        "-Wall",
+    ],
+
+    sanitize: {
+        cfi: true,
+        misc_undefined: [
+            "unsigned-integer-overflow",
+            "signed-integer-overflow",
+        ],
+    },
+}
diff --git a/media/libstagefright/codecs/amrwb/test/README.md b/media/libstagefright/codecs/amrwb/test/README.md
new file mode 100644
index 0000000..502a20d
--- /dev/null
+++ b/media/libstagefright/codecs/amrwb/test/README.md
@@ -0,0 +1,34 @@
+## Media Testing ##
+---
+#### AMR-WB Decoder :
+The Amr-Wb Decoder Test Suite validates the amrwb decoder available in libstagefright.
+
+Run the following steps to build the test suite:
+```
+m AmrwbDecoderTest
+```
+
+The 32-bit binaries will be created in the following path : ${OUT}/data/nativetest/
+
+The 64-bit binaries will be created in the following path : ${OUT}/data/nativetest64/
+
+To test 64-bit binary push binaries from nativetest64.
+```
+adb push ${OUT}/data/nativetest64/AmrwbDecoderTest/AmrwbDecoderTest /data/local/tmp/
+```
+
+To test 32-bit binary push binaries from nativetest.
+```
+adb push ${OUT}/data/nativetest/AmrwbDecoderTest/AmrwbDecoderTest /data/local/tmp/
+```
+
+The resource file for the tests is taken from [here](https://drive.google.com/drive/folders/13cM4tAaVFrmr-zGFqaAzFBbKs75pnm9b). Push these files into device for testing.
+Download amr-wb folder and push all the files in this folder to /data/local/tmp/ on the device.
+```
+adb push amr-wb/. /data/local/tmp/
+```
+
+usage: AmrwbDecoderTest -P \<path_to_folder\>
+```
+adb shell /data/local/tmp/AmrwbDecoderTest -P /data/local/tmp/
+```
diff --git a/media/libstagefright/id3/test/Android.bp b/media/libstagefright/id3/test/Android.bp
new file mode 100644
index 0000000..9d26eec
--- /dev/null
+++ b/media/libstagefright/id3/test/Android.bp
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+cc_test {
+    name: "ID3Test",
+    gtest: true,
+
+    srcs: ["ID3Test.cpp"],
+
+    static_libs: [
+        "libdatasource",
+        "libstagefright_id3",
+        "libstagefright",
+        "libstagefright_foundation",
+    ],
+
+    shared_libs: [
+        "libutils",
+        "liblog",
+        "libbinder",
+    ],
+
+    cflags: [
+        "-Werror",
+        "-Wall",
+    ],
+
+    sanitize: {
+        cfi: true,
+        misc_undefined: [
+            "unsigned-integer-overflow",
+            "signed-integer-overflow",
+        ],
+    },
+}
diff --git a/media/libstagefright/id3/test/ID3Test.cpp b/media/libstagefright/id3/test/ID3Test.cpp
new file mode 100644
index 0000000..a8f1470
--- /dev/null
+++ b/media/libstagefright/id3/test/ID3Test.cpp
@@ -0,0 +1,217 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "ID3Test"
+#include <utils/Log.h>
+
+#include <ctype.h>
+#include <string>
+#include <sys/stat.h>
+#include <datasource/FileSource.h>
+
+#include <media/stagefright/foundation/hexdump.h>
+#include <ID3.h>
+
+#include "ID3TestEnvironment.h"
+
+using namespace android;
+
+static ID3TestEnvironment *gEnv = nullptr;
+
+class ID3tagTest : public ::testing::TestWithParam<string> {};
+class ID3versionTest : public ::testing::TestWithParam<pair<string, int>> {};
+class ID3textTagTest : public ::testing::TestWithParam<pair<string, int>> {};
+class ID3albumArtTest : public ::testing::TestWithParam<pair<string, bool>> {};
+class ID3multiAlbumArtTest : public ::testing::TestWithParam<pair<string, int>> {};
+
+TEST_P(ID3tagTest, TagTest) {
+    string path = gEnv->getRes() + GetParam();
+    sp<FileSource> file = new FileSource(path.c_str());
+    ASSERT_EQ(file->initCheck(), (status_t)OK) << "File initialization failed! \n";
+    ID3 tag(file.get());
+    ASSERT_TRUE(tag.isValid()) << "No valid ID3 tag found for " << path.c_str() << "\n";
+
+    ID3::Iterator it(tag, nullptr);
+    while (!it.done()) {
+        String8 id;
+        it.getID(&id);
+        ASSERT_GT(id.length(), 0) << "No ID tag found! \n";
+        ALOGV("Found ID tag: %s\n", String8(id).c_str());
+        it.next();
+    }
+}
+
+TEST_P(ID3versionTest, VersionTest) {
+    int versionNumber = GetParam().second;
+    string path = gEnv->getRes() + GetParam().first;
+    sp<android::FileSource> file = new FileSource(path.c_str());
+    ASSERT_EQ(file->initCheck(), (status_t)OK) << "File initialization failed! \n";
+
+    ID3 tag(file.get());
+    ASSERT_TRUE(tag.isValid()) << "No valid ID3 tag found for " << path.c_str() << "\n";
+    ASSERT_TRUE(tag.version() >= versionNumber)
+            << "Expected version: " << tag.version() << " Found version: " << versionNumber;
+}
+
+TEST_P(ID3textTagTest, TextTagTest) {
+    int numTextFrames = GetParam().second;
+    string path = gEnv->getRes() + GetParam().first;
+    sp<android::FileSource> file = new FileSource(path.c_str());
+    ASSERT_EQ(file->initCheck(), (status_t)OK) << "File initialization failed! \n";
+
+    ID3 tag(file.get());
+    ASSERT_TRUE(tag.isValid()) << "No valid ID3 tag found for " << path.c_str() << "\n";
+    int countTextFrames = 0;
+    ID3::Iterator it(tag, nullptr);
+    while (!it.done()) {
+        String8 id;
+        it.getID(&id);
+        ASSERT_GT(id.length(), 0);
+        if (id[0] == 'T') {
+            String8 text;
+            countTextFrames++;
+            it.getString(&text);
+            ALOGV("Found text frame %s : %s \n", id.string(), text.string());
+        }
+        it.next();
+    }
+    ASSERT_EQ(countTextFrames, numTextFrames)
+            << "Expected " << numTextFrames << " text frames, found " << countTextFrames;
+}
+
+TEST_P(ID3albumArtTest, AlbumArtTest) {
+    bool albumArtPresent = GetParam().second;
+    string path = gEnv->getRes() + GetParam().first;
+    sp<android::FileSource> file = new FileSource(path.c_str());
+    ASSERT_EQ(file->initCheck(), (status_t)OK) << "File initialization failed! \n";
+
+    ID3 tag(file.get());
+    ASSERT_TRUE(tag.isValid()) << "No valid ID3 tag found for " << path.c_str() << "\n";
+    size_t dataSize;
+    String8 mime;
+    const void *data = tag.getAlbumArt(&dataSize, &mime);
+
+    if (albumArtPresent) {
+        if (data) {
+            ALOGV("Found album art: size = %zu mime = %s \n", dataSize, mime.string());
+        }
+        ASSERT_NE(data, nullptr) << "Expected album art, found none!" << path;
+    } else {
+        ASSERT_EQ(data, nullptr) << "Found album art when expected none!";
+    }
+#if (LOG_NDEBUG == 0)
+    hexdump(data, dataSize > 128 ? 128 : dataSize);
+#endif
+}
+
+TEST_P(ID3multiAlbumArtTest, MultiAlbumArtTest) {
+    int numAlbumArt = GetParam().second;
+    string path = gEnv->getRes() + GetParam().first;
+    sp<android::FileSource> file = new FileSource(path.c_str());
+    ASSERT_EQ(file->initCheck(), (status_t)OK) << "File initialization failed! \n";
+
+    ID3 tag(file.get());
+    ASSERT_TRUE(tag.isValid()) << "No valid ID3 tag found for " << path.c_str() << "\n";
+    int count = 0;
+    ID3::Iterator it(tag, nullptr);
+    while (!it.done()) {
+        String8 id;
+        it.getID(&id);
+        ASSERT_GT(id.length(), 0);
+        // Check if the tag is an "APIC/PIC" tag.
+        if (String8(id) == "APIC" || String8(id) == "PIC") {
+            count++;
+            size_t dataSize;
+            String8 mime;
+            const void *data = tag.getAlbumArt(&dataSize, &mime);
+            if (data) {
+                ALOGV("Found album art: size = %zu mime = %s \n", dataSize, mime.string());
+#if (LOG_NDEBUG == 0)
+                hexdump(data, dataSize > 128 ? 128 : dataSize);
+#endif
+            }
+            ASSERT_NE(data, nullptr) << "Expected album art, found none!" << path;
+        }
+        it.next();
+    }
+    ASSERT_EQ(count, numAlbumArt) << "Found " << count << " album arts, expected " << numAlbumArt
+                                  << " album arts! \n";
+}
+
+INSTANTIATE_TEST_SUITE_P(id3TestAll, ID3tagTest,
+                         ::testing::Values("bbb_44100hz_2ch_128kbps_mp3_30sec.mp3",
+                                           "bbb_44100hz_2ch_128kbps_mp3_30sec_1_image.mp3",
+                                           "bbb_44100hz_2ch_128kbps_mp3_30sec_2_image.mp3",
+                                           "bbb_44100hz_2ch_128kbps_mp3_5mins.mp3",
+                                           "bbb_44100hz_2ch_128kbps_mp3_5mins_1_image.mp3",
+                                           "bbb_44100hz_2ch_128kbps_mp3_5mins_2_image.mp3",
+                                           "bbb_44100hz_2ch_128kbps_mp3_5mins_largeSize.mp3",
+                                           "bbb_44100hz_2ch_128kbps_mp3_30sec_moreTextFrames.mp3"));
+
+INSTANTIATE_TEST_SUITE_P(
+        id3TestAll, ID3versionTest,
+        ::testing::Values(make_pair("bbb_44100hz_2ch_128kbps_mp3_30sec.mp3", 4),
+                          make_pair("bbb_44100hz_2ch_128kbps_mp3_30sec_1_image.mp3", 4),
+                          make_pair("bbb_44100hz_2ch_128kbps_mp3_30sec_2_image.mp3", 4),
+                          make_pair("bbb_44100hz_2ch_128kbps_mp3_5mins.mp3", 4),
+                          make_pair("bbb_44100hz_2ch_128kbps_mp3_5mins_1_image.mp3", 4),
+                          make_pair("bbb_44100hz_2ch_128kbps_mp3_5mins_2_image.mp3", 4),
+                          make_pair("bbb_44100hz_2ch_128kbps_mp3_5mins_largeSize.mp3", 4),
+                          make_pair("bbb_44100hz_2ch_128kbps_mp3_30sec_moreTextFrames.mp3", 4)));
+
+INSTANTIATE_TEST_SUITE_P(
+        id3TestAll, ID3textTagTest,
+        ::testing::Values(make_pair("bbb_44100hz_2ch_128kbps_mp3_30sec.mp3", 1),
+                          make_pair("bbb_44100hz_2ch_128kbps_mp3_30sec_1_image.mp3", 1),
+                          make_pair("bbb_44100hz_2ch_128kbps_mp3_30sec_2_image.mp3", 1),
+                          make_pair("bbb_44100hz_2ch_128kbps_mp3_5mins.mp3", 1),
+                          make_pair("bbb_44100hz_2ch_128kbps_mp3_5mins_1_image.mp3", 1),
+                          make_pair("bbb_44100hz_2ch_128kbps_mp3_5mins_2_image.mp3", 1),
+                          make_pair("bbb_44100hz_2ch_128kbps_mp3_5mins_largeSize.mp3", 1),
+                          make_pair("bbb_44100hz_2ch_128kbps_mp3_30sec_moreTextFrames.mp3", 5)));
+
+INSTANTIATE_TEST_SUITE_P(
+        id3TestAll, ID3albumArtTest,
+        ::testing::Values(make_pair("bbb_44100hz_2ch_128kbps_mp3_30sec.mp3", false),
+                          make_pair("bbb_44100hz_2ch_128kbps_mp3_30sec_1_image.mp3", true),
+                          make_pair("bbb_44100hz_2ch_128kbps_mp3_30sec_2_image.mp3", true),
+                          make_pair("bbb_44100hz_2ch_128kbps_mp3_5mins.mp3", false),
+                          make_pair("bbb_44100hz_2ch_128kbps_mp3_5mins_1_image.mp3", true),
+                          make_pair("bbb_44100hz_2ch_128kbps_mp3_5mins_2_image.mp3", true),
+                          make_pair("bbb_44100hz_2ch_128kbps_mp3_5mins_largeSize.mp3", true)));
+
+INSTANTIATE_TEST_SUITE_P(
+        id3TestAll, ID3multiAlbumArtTest,
+        ::testing::Values(make_pair("bbb_44100hz_2ch_128kbps_mp3_30sec.mp3", 0),
+                          make_pair("bbb_44100hz_2ch_128kbps_mp3_5mins.mp3", 0),
+                          make_pair("bbb_44100hz_2ch_128kbps_mp3_30sec_1_image.mp3", 1),
+                          make_pair("bbb_44100hz_2ch_128kbps_mp3_5mins_1_image.mp3", 1),
+                          make_pair("bbb_44100hz_2ch_128kbps_mp3_30sec_2_image.mp3", 2),
+                          make_pair("bbb_44100hz_2ch_128kbps_mp3_5mins_2_image.mp3", 2),
+                          make_pair("bbb_44100hz_2ch_128kbps_mp3_5mins_largeSize.mp3", 3)));
+
+int main(int argc, char **argv) {
+    gEnv = new ID3TestEnvironment();
+    ::testing::AddGlobalTestEnvironment(gEnv);
+    ::testing::InitGoogleTest(&argc, argv);
+    int status = gEnv->initFromOptions(argc, argv);
+    if (status == 0) {
+        status = RUN_ALL_TESTS();
+        ALOGI("ID3 Test result = %d\n", status);
+    }
+    return status;
+}
diff --git a/media/libstagefright/id3/test/ID3TestEnvironment.h b/media/libstagefright/id3/test/ID3TestEnvironment.h
new file mode 100644
index 0000000..2229718
--- /dev/null
+++ b/media/libstagefright/id3/test/ID3TestEnvironment.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __ID3_TEST_ENVIRONMENT_H__
+#define __ID3_TEST_ENVIRONMENT_H__
+
+#include <gtest/gtest.h>
+
+#include <getopt.h>
+
+using namespace std;
+
+class ID3TestEnvironment : public::testing::Environment {
+  public:
+    ID3TestEnvironment() : res("/data/local/tmp/") {}
+
+    // Parses the command line arguments
+    int initFromOptions(int argc, char **argv);
+
+    void setRes(const char *_res) { res = _res; }
+
+    const string getRes() const { return res; }
+
+  private:
+    string res;
+};
+
+int ID3TestEnvironment::initFromOptions(int argc, char **argv) {
+    static struct option options[] = {{"path", required_argument, 0, 'P'}, {0, 0, 0, 0}};
+
+    while (true) {
+        int index = 0;
+        int c = getopt_long(argc, argv, "P:", options, &index);
+        if (c == -1) {
+            break;
+        }
+
+        switch (c) {
+            case 'P': {
+                setRes(optarg);
+                break;
+            }
+            default:
+                break;
+        }
+    }
+
+    if (optind < argc) {
+        fprintf(stderr,
+                "unrecognized option: %s\n\n"
+                "usage: %s <gtest options> <test options>\n\n"
+                "test options are:\n\n"
+                "-P, --path: Resource files directory location\n",
+                argv[optind ?: 1], argv[0]);
+        return 2;
+    }
+    return 0;
+}
+
+#endif  // __ID3_TEST_ENVIRONMENT_H__
diff --git a/media/libstagefright/id3/test/README.md b/media/libstagefright/id3/test/README.md
new file mode 100644
index 0000000..72f730c
--- /dev/null
+++ b/media/libstagefright/id3/test/README.md
@@ -0,0 +1,34 @@
+## Media Testing ##
+---
+#### ID3 Test :
+The ID3 Test Suite validates the ID3 parser available in libstagefright.
+
+Run the following command in the id3 folder to build the test suite:
+```
+m ID3Test
+```
+
+The 32-bit binaries will be created in the following path : ${OUT}/data/nativetest/
+
+The 64-bit binaries will be created in the following path : ${OUT}/data/nativetest64/
+
+To test 64-bit binary push binaries from nativetest64.
+```
+adb push ${OUT}/data/nativetest64/ID3Test/ID3Test /data/local/tmp/
+```
+
+To test 32-bit binary push binaries from nativetest.
+```
+adb push ${OUT}/data/nativetest/ID3Test/ID3Test /data/local/tmp/
+```
+
+The resource file for the tests is taken from [here](https://drive.google.com/drive/folders/1pt5HFVSysbqfyqY1sVJ9MTupZKCdqjYZ). Push these files into device for testing.
+Download ID3 folder and push all the files in this folder to /data/local/tmp/ID3 on the device.
+```
+adb push ID3/. /data/local/tmp/ID3
+```
+
+usage: ID3Test -P \<path_to_folder\>
+```
+adb shell /data/local/tmp/ID3Test -P /data/local/tmp/ID3/
+```
diff --git a/media/libstagefright/include/media/stagefright/MediaCodec.h b/media/libstagefright/include/media/stagefright/MediaCodec.h
index cd30347..01d0325 100644
--- a/media/libstagefright/include/media/stagefright/MediaCodec.h
+++ b/media/libstagefright/include/media/stagefright/MediaCodec.h
@@ -25,7 +25,7 @@
 #include <media/hardware/CryptoAPI.h>
 #include <media/MediaCodecInfo.h>
 #include <media/MediaResource.h>
-#include <media/MediaAnalyticsItem.h>
+#include <media/MediaMetrics.h>
 #include <media/stagefright/foundation/AHandler.h>
 #include <media/stagefright/FrameRenderTracker.h>
 #include <utils/Vector.h>
@@ -189,7 +189,7 @@
 
     status_t getCodecInfo(sp<MediaCodecInfo> *codecInfo) const;
 
-    status_t getMetrics(MediaAnalyticsItem * &reply);
+    status_t getMetrics(mediametrics_handle_t &reply);
 
     status_t setParameters(const sp<AMessage> &params);
 
@@ -328,11 +328,11 @@
     sp<Surface> mSurface;
     SoftwareRenderer *mSoftRenderer;
 
-    MediaAnalyticsItem *mAnalyticsItem;
-    void initAnalyticsItem();
-    void updateAnalyticsItem();
-    void flushAnalyticsItem();
-    void updateEphemeralAnalytics(MediaAnalyticsItem *item);
+    mediametrics_handle_t mMetricsHandle;
+    void initMediametrics();
+    void updateMediametrics();
+    void flushMediametrics();
+    void updateEphemeralMediametrics(mediametrics_handle_t item);
 
     sp<AMessage> mOutputFormat;
     sp<AMessage> mInputFormat;
diff --git a/media/tests/benchmark/MediaBenchmarkTest/Android.bp b/media/tests/benchmark/MediaBenchmarkTest/Android.bp
index 91b03f1..489ab04 100644
--- a/media/tests/benchmark/MediaBenchmarkTest/Android.bp
+++ b/media/tests/benchmark/MediaBenchmarkTest/Android.bp
@@ -29,6 +29,10 @@
         "android.test.base",
     ],
 
+    jni_libs: [
+        "libmediabenchmark_jni",
+    ],
+
     static_libs: [
         "libMediaBenchmark",
         "junit",
diff --git a/media/tests/benchmark/MediaBenchmarkTest/build.gradle b/media/tests/benchmark/MediaBenchmarkTest/build.gradle
index b0ee692..b2aee1a 100644
--- a/media/tests/benchmark/MediaBenchmarkTest/build.gradle
+++ b/media/tests/benchmark/MediaBenchmarkTest/build.gradle
@@ -30,7 +30,7 @@
     compileSdkVersion 29
     defaultConfig {
         applicationId "com.android.media.benchmark"
-        minSdkVersion 21
+        minSdkVersion 28
         targetSdkVersion 29
         versionCode 1
         versionName "1.0"
@@ -48,6 +48,18 @@
             manifest.srcFile 'AndroidManifest.xml'
         }
     }
+    buildTypes {
+        release {
+            minifyEnabled false
+            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+        }
+    }
+    externalNativeBuild {
+        cmake {
+            path "src/main/cpp/CMakeLists.txt"
+            version "3.10.2"
+        }
+    }
 }
 
 repositories {
diff --git a/media/tests/benchmark/MediaBenchmarkTest/src/androidTest/java/com/android/media/benchmark/tests/DecoderTest.java b/media/tests/benchmark/MediaBenchmarkTest/src/androidTest/java/com/android/media/benchmark/tests/DecoderTest.java
index be2633d..07d414d 100644
--- a/media/tests/benchmark/MediaBenchmarkTest/src/androidTest/java/com/android/media/benchmark/tests/DecoderTest.java
+++ b/media/tests/benchmark/MediaBenchmarkTest/src/androidTest/java/com/android/media/benchmark/tests/DecoderTest.java
@@ -27,7 +27,10 @@
 import com.android.media.benchmark.library.CodecUtils;
 import com.android.media.benchmark.library.Decoder;
 import com.android.media.benchmark.library.Extractor;
+import com.android.media.benchmark.library.Native;
+import com.android.media.benchmark.library.Stats;
 
+import org.junit.BeforeClass;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
@@ -44,12 +47,17 @@
 import java.util.Arrays;
 import java.util.Collection;
 
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertEquals;
+
 @RunWith(Parameterized.class)
 public class DecoderTest {
     private static final Context mContext =
             InstrumentationRegistry.getInstrumentation().getTargetContext();
     private static final String mInputFilePath = mContext.getString(R.string.input_file_path);
     private static final String mOutputFilePath = mContext.getString(R.string.output_file_path);
+    private static final String mStatsFile =
+            mContext.getFilesDir() + "/Decoder." + System.currentTimeMillis() + ".csv";
     private static final String TAG = "DecoderTest";
     private static final long PER_TEST_TIMEOUT_MS = 60000;
     private static final boolean DEBUG = false;
@@ -101,97 +109,111 @@
                 {"crowd_1920x1080_25fps_4000kbps_h265.mkv", true}});
     }
 
+    @BeforeClass
+    public static void writeStatsHeaderToFile() throws IOException {
+        Stats mStats = new Stats();
+        boolean status = mStats.writeStatsHeader(mStatsFile);
+        assertTrue("Unable to open stats file for writing!", status);
+    }
+
     @Test(timeout = PER_TEST_TIMEOUT_MS)
     public void testDecoder() throws IOException {
         File inputFile = new File(mInputFilePath + mInputFile);
-        if (inputFile.exists()) {
-            FileInputStream fileInput = new FileInputStream(inputFile);
-            FileDescriptor fileDescriptor = fileInput.getFD();
-            Extractor extractor = new Extractor();
-            int trackCount = extractor.setUpExtractor(fileDescriptor);
-            ArrayList<ByteBuffer> inputBuffer = new ArrayList<>();
-            ArrayList<MediaCodec.BufferInfo> frameInfo = new ArrayList<>();
-            if (trackCount <= 0) {
-                Log.e(TAG, "Extraction failed. No tracks for file: " + mInputFile);
-                return;
-            }
-            for (int currentTrack = 0; currentTrack < trackCount; currentTrack++) {
-                extractor.selectExtractorTrack(currentTrack);
-                MediaFormat format = extractor.getFormat(currentTrack);
-                String mime = format.getString(MediaFormat.KEY_MIME);
-                ArrayList<String> mediaCodecs = CodecUtils.selectCodecs(mime, false);
-                if (mediaCodecs.size() <= 0) {
-                    Log.e(TAG,
-                            "No suitable codecs found for file: " + mInputFile
-                                    + " track : " + currentTrack + " mime: " + mime);
-                    continue;
+        assertTrue("Cannot find " + mInputFile + " in directory " + mInputFilePath,
+                inputFile.exists());
+        FileInputStream fileInput = new FileInputStream(inputFile);
+        FileDescriptor fileDescriptor = fileInput.getFD();
+        Extractor extractor = new Extractor();
+        int trackCount = extractor.setUpExtractor(fileDescriptor);
+        assertTrue("Extraction failed. No tracks for file: " + mInputFile, (trackCount > 0));
+        ArrayList<ByteBuffer> inputBuffer = new ArrayList<>();
+        ArrayList<MediaCodec.BufferInfo> frameInfo = new ArrayList<>();
+        for (int currentTrack = 0; currentTrack < trackCount; currentTrack++) {
+            extractor.selectExtractorTrack(currentTrack);
+            MediaFormat format = extractor.getFormat(currentTrack);
+            String mime = format.getString(MediaFormat.KEY_MIME);
+            ArrayList<String> mediaCodecs = CodecUtils.selectCodecs(mime, false);
+            assertTrue("No suitable codecs found for file: " + mInputFile + " track : " +
+                    currentTrack + " mime: " + mime, (mediaCodecs.size() > 0));
+
+            // Get samples from extractor
+            int sampleSize;
+            do {
+                sampleSize = extractor.getFrameSample();
+                MediaCodec.BufferInfo bufInfo = new MediaCodec.BufferInfo();
+                MediaCodec.BufferInfo info = extractor.getBufferInfo();
+                ByteBuffer dataBuffer = ByteBuffer.allocate(info.size);
+                dataBuffer.put(extractor.getFrameBuffer().array(), 0, info.size);
+                bufInfo.set(info.offset, info.size, info.presentationTimeUs, info.flags);
+                inputBuffer.add(dataBuffer);
+                frameInfo.add(bufInfo);
+                if (DEBUG) {
+                    Log.d(TAG, "Extracted bufInfo: flag = " + bufInfo.flags + " timestamp = " +
+                            bufInfo.presentationTimeUs + " size = " + bufInfo.size);
                 }
-                // Get samples from extractor
-                int sampleSize;
-                do {
-                    sampleSize = extractor.getFrameSample();
-                    MediaCodec.BufferInfo bufInfo = new MediaCodec.BufferInfo();
-                    MediaCodec.BufferInfo info = extractor.getBufferInfo();
-                    ByteBuffer dataBuffer = ByteBuffer.allocate(info.size);
-                    dataBuffer.put(extractor.getFrameBuffer().array(), 0, info.size);
-                    bufInfo.set(info.offset, info.size, info.presentationTimeUs, info.flags);
-                    inputBuffer.add(dataBuffer);
-                    frameInfo.add(bufInfo);
-                    if (DEBUG) {
-                        Log.d(TAG,
-                                "Extracted bufInfo: flag = " + bufInfo.flags + " timestamp = "
-                                        + bufInfo.presentationTimeUs + " size = " + bufInfo.size);
+            } while (sampleSize > 0);
+            for (String codecName : mediaCodecs) {
+                FileOutputStream decodeOutputStream = null;
+                if (WRITE_OUTPUT) {
+                    if (!Paths.get(mOutputFilePath).toFile().exists()) {
+                        Files.createDirectories(Paths.get(mOutputFilePath));
                     }
-                } while (sampleSize > 0);
-                for (String codecName : mediaCodecs) {
-                    FileOutputStream decodeOutputStream = null;
-                    if (WRITE_OUTPUT) {
-                        if (!Paths.get(mOutputFilePath).toFile().exists()) {
-                            Files.createDirectories(Paths.get(mOutputFilePath));
-                        }
-                        File outFile = new File(mOutputFilePath + "decoder.out");
-                        if (outFile.exists()) {
-                            if (!outFile.delete()) {
-                                Log.e(TAG, " Unable to delete existing file" + outFile.toString());
-                            }
-                        }
-                        if (outFile.createNewFile()) {
-                            decodeOutputStream = new FileOutputStream(outFile);
-                        } else {
-                            Log.e(TAG, "Unable to create file: " + outFile.toString());
-                        }
+                    File outFile = new File(mOutputFilePath + "decoder.out");
+                    if (outFile.exists()) {
+                        assertTrue(" Unable to delete existing file" + outFile.toString(),
+                                outFile.delete());
                     }
-                    Decoder decoder = new Decoder();
-                    decoder.setupDecoder(decodeOutputStream);
-                    int status =
-                            decoder.decode(inputBuffer, frameInfo, mAsyncMode, format, codecName);
-                    decoder.deInitCodec();
-                    if (status == 0) {
-                        decoder.dumpStatistics(
-                                mInputFile + " " + codecName, extractor.getClipDuration());
-                        Log.i(TAG,
-                                "Decoding Successful for file: " + mInputFile
-                                        + " with codec: " + codecName);
-                    } else {
-                        Log.e(TAG,
-                                "Decoder returned error " + status + " for file: " + mInputFile
-                                        + " with codec: " + codecName);
-                    }
-                    decoder.resetDecoder();
-                    if (decodeOutputStream != null) {
-                        decodeOutputStream.close();
-                    }
+                    assertTrue("Unable to create file: " + outFile.toString(),
+                            outFile.createNewFile());
+                    decodeOutputStream = new FileOutputStream(outFile);
                 }
-                extractor.unselectExtractorTrack(currentTrack);
-                inputBuffer.clear();
-                frameInfo.clear();
+                Decoder decoder = new Decoder();
+                decoder.setupDecoder(decodeOutputStream);
+                int status = decoder.decode(inputBuffer, frameInfo, mAsyncMode, format, codecName);
+                decoder.deInitCodec();
+                assertEquals("Decoder returned error " + status + " for file: " + mInputFile +
+                        " with codec: " + codecName, 0, status);
+                decoder.dumpStatistics(mInputFile, codecName, (mAsyncMode ? "async" : "sync"),
+                        extractor.getClipDuration(), mStatsFile);
+                Log.i(TAG, "Decoding Successful for file: " + mInputFile + " with codec: " +
+                        codecName);
+                decoder.resetDecoder();
+                if (decodeOutputStream != null) {
+                    decodeOutputStream.close();
+                }
             }
-            extractor.deinitExtractor();
-            fileInput.close();
-        } else {
-            Log.w(TAG,
-                    "Warning: Test Skipped. Cannot find " + mInputFile + " in directory "
-                            + mInputFilePath);
+            extractor.unselectExtractorTrack(currentTrack);
+            inputBuffer.clear();
+            frameInfo.clear();
         }
+        extractor.deinitExtractor();
+        fileInput.close();
+    }
+
+    @Test
+    public void testNativeDecoder() throws IOException {
+        File inputFile = new File(mInputFilePath + mInputFile);
+        assertTrue("Cannot find " + mInputFile + " in directory " + mInputFilePath,
+                inputFile.exists());
+        FileInputStream fileInput = new FileInputStream(inputFile);
+        FileDescriptor fileDescriptor = fileInput.getFD();
+        Extractor extractor = new Extractor();
+        int trackCount = extractor.setUpExtractor(fileDescriptor);
+        assertTrue("Extraction failed. No tracks for file: ", trackCount > 0);
+        for (int currentTrack = 0; currentTrack < trackCount; currentTrack++) {
+            extractor.selectExtractorTrack(currentTrack);
+            MediaFormat format = extractor.getFormat(currentTrack);
+            String mime = format.getString(MediaFormat.KEY_MIME);
+            ArrayList<String> mediaCodecs = CodecUtils.selectCodecs(mime, false);
+            for (String codecName : mediaCodecs) {
+                Log.i("Test: %s\n", mInputFile);
+                Native nativeDecoder = new Native();
+                int status = nativeDecoder.Decode(
+                        mInputFilePath, mInputFile, mStatsFile, codecName, mAsyncMode);
+                assertEquals("Decoder returned error " + status + " for file: " + mInputFile, 0,
+                        status);
+            }
+        }
+        fileInput.close();
     }
 }
diff --git a/media/tests/benchmark/MediaBenchmarkTest/src/androidTest/java/com/android/media/benchmark/tests/EncoderTest.java b/media/tests/benchmark/MediaBenchmarkTest/src/androidTest/java/com/android/media/benchmark/tests/EncoderTest.java
index 9db9c84..00e5e21 100644
--- a/media/tests/benchmark/MediaBenchmarkTest/src/androidTest/java/com/android/media/benchmark/tests/EncoderTest.java
+++ b/media/tests/benchmark/MediaBenchmarkTest/src/androidTest/java/com/android/media/benchmark/tests/EncoderTest.java
@@ -28,7 +28,10 @@
 import com.android.media.benchmark.library.Decoder;
 import com.android.media.benchmark.library.Encoder;
 import com.android.media.benchmark.library.Extractor;
+import com.android.media.benchmark.library.Native;
+import com.android.media.benchmark.library.Stats;
 
+import org.junit.BeforeClass;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
@@ -37,18 +40,24 @@
 import java.io.FileDescriptor;
 import java.io.FileInputStream;
 import java.io.FileOutputStream;
+import java.io.IOException;
 import java.nio.ByteBuffer;
 
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
 
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertEquals;
+
 @RunWith(Parameterized.class)
 public class EncoderTest {
     private static final Context mContext =
             InstrumentationRegistry.getInstrumentation().getTargetContext();
     private static final String mInputFilePath = mContext.getString(R.string.input_file_path);
     private static final String mOutputFilePath = mContext.getString(R.string.output_file_path);
+    private static final String mStatsFile =
+            mContext.getFilesDir() + "/Encoder." + System.currentTimeMillis() + ".csv";
     private static final String TAG = "EncoderTest";
     private static final long PER_TEST_TIMEOUT_MS = 120000;
     private static final boolean DEBUG = false;
@@ -56,7 +65,6 @@
     private static final int ENCODE_DEFAULT_FRAME_RATE = 25;
     private static final int ENCODE_DEFAULT_BIT_RATE = 8000000 /* 8 Mbps */;
     private static final int ENCODE_MIN_BIT_RATE = 600000 /* 600 Kbps */;
-
     private String mInputFile;
 
     @Parameterized.Parameters
@@ -81,192 +89,204 @@
         this.mInputFile = inputFileName;
     }
 
+    @BeforeClass
+    public static void writeStatsHeaderToFile() throws IOException {
+        Stats mStats = new Stats();
+        boolean status = mStats.writeStatsHeader(mStatsFile);
+        assertTrue("Unable to open stats file for writing!", status);
+    }
+
     @Test(timeout = PER_TEST_TIMEOUT_MS)
     public void sampleEncoderTest() throws Exception {
         int status;
         int frameSize;
-
         //Parameters for video
         int width = 0;
         int height = 0;
         int profile = 0;
         int level = 0;
         int frameRate = 0;
-
         //Parameters for audio
         int bitRate = 0;
         int sampleRate = 0;
         int numChannels = 0;
-
         File inputFile = new File(mInputFilePath + mInputFile);
-        if (inputFile.exists()) {
-            FileInputStream fileInput = new FileInputStream(inputFile);
-            FileDescriptor fileDescriptor = fileInput.getFD();
-            Extractor extractor = new Extractor();
-            int trackCount = extractor.setUpExtractor(fileDescriptor);
-            if (trackCount <= 0) {
-                Log.e(TAG, "Extraction failed. No tracks for file: " + mInputFile);
-                return;
+        assertTrue("Cannot find " + mInputFile + " in directory " + mInputFilePath,
+                inputFile.exists());
+        FileInputStream fileInput = new FileInputStream(inputFile);
+        FileDescriptor fileDescriptor = fileInput.getFD();
+        Extractor extractor = new Extractor();
+        int trackCount = extractor.setUpExtractor(fileDescriptor);
+        assertTrue("Extraction failed. No tracks for file: " + mInputFile, (trackCount > 0));
+        ArrayList<ByteBuffer> inputBuffer = new ArrayList<>();
+        ArrayList<MediaCodec.BufferInfo> frameInfo = new ArrayList<>();
+        for (int currentTrack = 0; currentTrack < trackCount; currentTrack++) {
+            extractor.selectExtractorTrack(currentTrack);
+            MediaFormat format = extractor.getFormat(currentTrack);
+            // Get samples from extractor
+            int sampleSize;
+            do {
+                sampleSize = extractor.getFrameSample();
+                MediaCodec.BufferInfo bufInfo = new MediaCodec.BufferInfo();
+                MediaCodec.BufferInfo info = extractor.getBufferInfo();
+                ByteBuffer dataBuffer = ByteBuffer.allocate(info.size);
+                dataBuffer.put(extractor.getFrameBuffer().array(), 0, info.size);
+                bufInfo.set(info.offset, info.size, info.presentationTimeUs, info.flags);
+                inputBuffer.add(dataBuffer);
+                frameInfo.add(bufInfo);
+                if (DEBUG) {
+                    Log.d(TAG, "Extracted bufInfo: flag = " + bufInfo.flags + " timestamp = " +
+                            bufInfo.presentationTimeUs + " size = " + bufInfo.size);
+                }
+            } while (sampleSize > 0);
+            int tid = android.os.Process.myTid();
+            File decodedFile = new File(mContext.getFilesDir() + "/decoder_" + tid + ".out");
+            FileOutputStream decodeOutputStream = new FileOutputStream(decodedFile);
+            Decoder decoder = new Decoder();
+            decoder.setupDecoder(decodeOutputStream);
+            status = decoder.decode(inputBuffer, frameInfo, false, format, "");
+            assertEquals("Decoder returned error " + status + " for file: " + mInputFile, 0,
+                    status);
+            decoder.deInitCodec();
+            extractor.unselectExtractorTrack(currentTrack);
+            inputBuffer.clear();
+            frameInfo.clear();
+            if (decodeOutputStream != null) {
+                decodeOutputStream.close();
             }
-            ArrayList<ByteBuffer> inputBuffer = new ArrayList<>();
-            ArrayList<MediaCodec.BufferInfo> frameInfo = new ArrayList<>();
-            for (int currentTrack = 0; currentTrack < trackCount; currentTrack++) {
-                extractor.selectExtractorTrack(currentTrack);
-                MediaFormat format = extractor.getFormat(currentTrack);
-                // Get samples from extractor
-                int sampleSize;
-                do {
-                    sampleSize = extractor.getFrameSample();
-                    MediaCodec.BufferInfo bufInfo = new MediaCodec.BufferInfo();
-                    MediaCodec.BufferInfo info = extractor.getBufferInfo();
-                    ByteBuffer dataBuffer = ByteBuffer.allocate(info.size);
-                    dataBuffer.put(extractor.getFrameBuffer().array(), 0, info.size);
-                    bufInfo.set(info.offset, info.size, info.presentationTimeUs, info.flags);
-                    inputBuffer.add(dataBuffer);
-                    frameInfo.add(bufInfo);
+            String mime = format.getString(MediaFormat.KEY_MIME);
+            ArrayList<String> mediaCodecs = CodecUtils.selectCodecs(mime, true);
+            assertTrue("No suitable codecs found for file: " + mInputFile + " track : " +
+                    currentTrack + " mime: " + mime, (mediaCodecs.size() > 0));
+            Boolean[] encodeMode = {true, false};
+            /* Encoding the decoder's output */
+            for (Boolean asyncMode : encodeMode) {
+                for (String codecName : mediaCodecs) {
+                    FileOutputStream encodeOutputStream = null;
+                    if (WRITE_OUTPUT) {
+                        File outEncodeFile = new File(mOutputFilePath + "encoder.out");
+                        if (outEncodeFile.exists()) {
+                            assertTrue(" Unable to delete existing file" + outEncodeFile.toString(),
+                                    outEncodeFile.delete());
+                        }
+                        assertTrue("Unable to create file to write encoder output: " +
+                                outEncodeFile.toString(), outEncodeFile.createNewFile());
+                        encodeOutputStream = new FileOutputStream(outEncodeFile);
+                    }
+                    File rawFile = new File(mContext.getFilesDir() + "/decoder_" + tid + ".out");
+                    assertTrue("Cannot open file to write decoded output", rawFile.exists());
                     if (DEBUG) {
-                        Log.d(TAG, "Extracted bufInfo: flag = " + bufInfo.flags + " timestamp = " +
-                                bufInfo.presentationTimeUs + " size = " + bufInfo.size);
+                        Log.i(TAG, "Path of decoded input file: " + rawFile.toString());
                     }
-                } while (sampleSize > 0);
-
-                int tid = android.os.Process.myTid();
-                File decodedFile = new File(mContext.getFilesDir() + "/decoder_" + tid + ".out");
-                FileOutputStream decodeOutputStream = new FileOutputStream(decodedFile);
-                Decoder decoder = new Decoder();
-                decoder.setupDecoder(decodeOutputStream);
-                status = decoder.decode(inputBuffer, frameInfo, false, format, "");
-                if (status == 0) {
-                    Log.i(TAG, "Decoding complete.");
-                } else {
-                    Log.e(TAG, "Decode returned error. Encoding did not take place." + status);
-                    return;
-                }
-                decoder.deInitCodec();
-                extractor.unselectExtractorTrack(currentTrack);
-                inputBuffer.clear();
-                frameInfo.clear();
-                if (decodeOutputStream != null) {
-                    decodeOutputStream.close();
-                }
-                String mime = format.getString(MediaFormat.KEY_MIME);
-                ArrayList<String> mediaCodecs = CodecUtils.selectCodecs(mime, true);
-                if (mediaCodecs.size() <= 0) {
-                    Log.e(TAG, "No suitable codecs found for file: " + mInputFile + " track : " +
-                            currentTrack + " mime: " + mime);
-                    return;
-                }
-                Boolean[] encodeMode = {true, false};
-                /* Encoding the decoder's output */
-                for (Boolean asyncMode : encodeMode) {
-                    for (String codecName : mediaCodecs) {
-                        FileOutputStream encodeOutputStream = null;
-                        if (WRITE_OUTPUT) {
-                            File outEncodeFile = new File(mOutputFilePath + "encoder.out");
-                            if (outEncodeFile.exists()) {
-                                if (!outEncodeFile.delete()) {
-                                    Log.e(TAG, "Unable to delete existing file" +
-                                            decodedFile.toString());
-                                }
-                            }
-                            if (outEncodeFile.createNewFile()) {
-                                encodeOutputStream = new FileOutputStream(outEncodeFile);
+                    FileInputStream eleStream = new FileInputStream(rawFile);
+                    if (mime.startsWith("video/")) {
+                        width = format.getInteger(MediaFormat.KEY_WIDTH);
+                        height = format.getInteger(MediaFormat.KEY_HEIGHT);
+                        if (format.containsKey(MediaFormat.KEY_FRAME_RATE)) {
+                            frameRate = format.getInteger(MediaFormat.KEY_FRAME_RATE);
+                        } else if (frameRate <= 0) {
+                            frameRate = ENCODE_DEFAULT_FRAME_RATE;
+                        }
+                        if (format.containsKey(MediaFormat.KEY_BIT_RATE)) {
+                            bitRate = format.getInteger(MediaFormat.KEY_BIT_RATE);
+                        } else if (bitRate <= 0) {
+                            if (mime.contains("video/3gpp") || mime.contains("video/mp4v-es")) {
+                                bitRate = ENCODE_MIN_BIT_RATE;
                             } else {
-                                Log.e(TAG, "Unable to create file to write encoder output: " +
-                                        outEncodeFile.toString());
+                                bitRate = ENCODE_DEFAULT_BIT_RATE;
                             }
                         }
-                        File rawFile =
-                                new File(mContext.getFilesDir() + "/decoder_" + tid + ".out");
-                        if (rawFile.exists()) {
-                            if (DEBUG) {
-                                Log.i(TAG, "Path of decoded input file: " + rawFile.toString());
-                            }
-                            FileInputStream eleStream = new FileInputStream(rawFile);
-                            if (mime.startsWith("video/")) {
-                                width = format.getInteger(MediaFormat.KEY_WIDTH);
-                                height = format.getInteger(MediaFormat.KEY_HEIGHT);
-                                if (format.containsKey(MediaFormat.KEY_FRAME_RATE)) {
-                                    frameRate = format.getInteger(MediaFormat.KEY_FRAME_RATE);
-                                } else if (frameRate <= 0) {
-                                    frameRate = ENCODE_DEFAULT_FRAME_RATE;
-                                }
-                                if (format.containsKey(MediaFormat.KEY_BIT_RATE)) {
-                                    bitRate = format.getInteger(MediaFormat.KEY_BIT_RATE);
-                                } else if (bitRate <= 0) {
-                                    if (mime.contains("video/3gpp") ||
-                                            mime.contains("video/mp4v-es")) {
-                                        bitRate = ENCODE_MIN_BIT_RATE;
-                                    } else {
-                                        bitRate = ENCODE_DEFAULT_BIT_RATE;
-                                    }
-                                }
-                                if (format.containsKey(MediaFormat.KEY_PROFILE)) {
-                                    profile = format.getInteger(MediaFormat.KEY_PROFILE);
-                                }
-                                if (format.containsKey(MediaFormat.KEY_PROFILE)) {
-                                    level = format.getInteger(MediaFormat.KEY_LEVEL);
-                                }
-                            } else {
-                                sampleRate = format.getInteger(MediaFormat.KEY_SAMPLE_RATE);
-                                numChannels = format.getInteger(MediaFormat.KEY_CHANNEL_COUNT);
-                                bitRate = sampleRate * numChannels * 16;
-                            }
-                            /*Setup Encode Format*/
-                            MediaFormat encodeFormat;
-                            if (mime.startsWith("video/")) {
-                                frameSize = width * height * 3 / 2;
-                                encodeFormat = MediaFormat.createVideoFormat(mime, width, height);
-                                encodeFormat.setInteger(MediaFormat.KEY_FRAME_RATE, frameRate);
-                                encodeFormat.setInteger(MediaFormat.KEY_BIT_RATE, bitRate);
-                                encodeFormat.setInteger(MediaFormat.KEY_PROFILE, profile);
-                                encodeFormat.setInteger(MediaFormat.KEY_LEVEL, level);
-                                encodeFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 1);
-                                encodeFormat.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, frameSize);
-                            } else {
-                                encodeFormat = MediaFormat
-                                        .createAudioFormat(mime, sampleRate, numChannels);
-                                encodeFormat.setInteger(MediaFormat.KEY_BIT_RATE, bitRate);
-                                frameSize = 4096;
-                            }
-                            Encoder encoder = new Encoder();
-                            encoder.setupEncoder(encodeOutputStream, eleStream);
-                            status = encoder.encode(codecName, encodeFormat, mime, frameRate,
-                                    sampleRate, frameSize, asyncMode);
-                            encoder.deInitEncoder();
-                            if (status == 0) {
-                                encoder.dumpStatistics(mInputFile + "with " + codecName + " for " +
-                                        "aSyncMode = " + asyncMode, extractor.getClipDuration());
-                                Log.i(TAG, "Encoding complete for file: " + mInputFile +
-                                        " with codec: " + codecName + " for aSyncMode = " +
-                                        asyncMode);
-                            } else {
-                                Log.e(TAG,
-                                        codecName + " encoder returned error " + status + " for " +
-                                                "file:" + " " + mInputFile);
-                            }
-                            encoder.resetEncoder();
-                            eleStream.close();
-                            if (encodeOutputStream != null) {
-                                encodeOutputStream.close();
-                            }
+                        if (format.containsKey(MediaFormat.KEY_PROFILE)) {
+                            profile = format.getInteger(MediaFormat.KEY_PROFILE);
                         }
-                    }
-                }
-                //Cleanup temporary input file
-                if (decodedFile.exists()) {
-                    if (decodedFile.delete()) {
-                        Log.i(TAG, "Successfully deleted decoded file");
+                        if (format.containsKey(MediaFormat.KEY_PROFILE)) {
+                            level = format.getInteger(MediaFormat.KEY_LEVEL);
+                        }
                     } else {
-                        Log.e(TAG, "Unable to delete decoded file");
+                        sampleRate = format.getInteger(MediaFormat.KEY_SAMPLE_RATE);
+                        numChannels = format.getInteger(MediaFormat.KEY_CHANNEL_COUNT);
+                        bitRate = sampleRate * numChannels * 16;
                     }
+                    /*Setup Encode Format*/
+                    MediaFormat encodeFormat;
+                    if (mime.startsWith("video/")) {
+                        frameSize = width * height * 3 / 2;
+                        encodeFormat = MediaFormat.createVideoFormat(mime, width, height);
+                        encodeFormat.setInteger(MediaFormat.KEY_FRAME_RATE, frameRate);
+                        encodeFormat.setInteger(MediaFormat.KEY_BIT_RATE, bitRate);
+                        encodeFormat.setInteger(MediaFormat.KEY_PROFILE, profile);
+                        encodeFormat.setInteger(MediaFormat.KEY_LEVEL, level);
+                        encodeFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 1);
+                        encodeFormat.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, frameSize);
+                    } else {
+                        encodeFormat = MediaFormat.createAudioFormat(mime, sampleRate, numChannels);
+                        encodeFormat.setInteger(MediaFormat.KEY_BIT_RATE, bitRate);
+                        frameSize = 4096;
+                    }
+                    Encoder encoder = new Encoder();
+                    encoder.setupEncoder(encodeOutputStream, eleStream);
+                    status = encoder.encode(codecName, encodeFormat, mime, frameRate, sampleRate,
+                            frameSize, asyncMode);
+                    encoder.deInitEncoder();
+                    assertEquals(
+                            codecName + " encoder returned error " + status + " for " + "file:" +
+                                    " " + mInputFile, 0, status);
+                    encoder.dumpStatistics(mInputFile, codecName, (asyncMode ? "async" : "sync"),
+                            extractor.getClipDuration(), mStatsFile);
+                    Log.i(TAG, "Encoding complete for file: " + mInputFile + " with codec: " +
+                            codecName + " for aSyncMode = " + asyncMode);
+                    encoder.resetEncoder();
+                    eleStream.close();
+                    if (encodeOutputStream != null) {
+                        encodeOutputStream.close();
+                    }
+
                 }
             }
-            extractor.deinitExtractor();
-            fileInput.close();
-        } else {
-            Log.w(TAG, "Warning: Test Skipped. Cannot find " + mInputFile + " in directory " +
-                    mInputFilePath);
+            //Cleanup temporary input file
+            if (decodedFile.exists()) {
+                assertTrue(" Unable to delete decoded file" + decodedFile.toString(),
+                        decodedFile.delete());
+                Log.i(TAG, "Successfully deleted decoded file");
+            }
         }
+        extractor.deinitExtractor();
+        fileInput.close();
+    }
+
+    @Test
+    public void testNativeEncoder() throws Exception {
+        File inputFile = new File(mInputFilePath + mInputFile);
+        assertTrue("Cannot find " + mInputFile + " in directory " + mInputFilePath,
+                inputFile.exists());
+        int tid = android.os.Process.myTid();
+        final String mDecodedFile = mContext.getFilesDir() + "/decoder_" + tid + ".out";
+        FileInputStream fileInput = new FileInputStream(inputFile);
+        FileDescriptor fileDescriptor = fileInput.getFD();
+        Extractor extractor = new Extractor();
+        int trackCount = extractor.setUpExtractor(fileDescriptor);
+        assertTrue("Extraction failed. No tracks for file: ", trackCount > 0);
+        for (int currentTrack = 0; currentTrack < trackCount; currentTrack++) {
+            extractor.selectExtractorTrack(currentTrack);
+            MediaFormat format = extractor.getFormat(currentTrack);
+            String mime = format.getString(MediaFormat.KEY_MIME);
+            ArrayList<String> mediaCodecs = CodecUtils.selectCodecs(mime, true);
+            // Encoding the decoder's output
+            for (String codecName : mediaCodecs) {
+                Native nativeEncoder = new Native();
+                int status = nativeEncoder.Encode(
+                        mInputFilePath, mInputFile, mDecodedFile, mStatsFile, codecName);
+                assertEquals(
+                        codecName + " encoder returned error " + status + " for " + "file:" + " " +
+                                mInputFile, 0, status);
+            }
+        }
+        File decodedFile = new File(mDecodedFile);
+        // Cleanup temporary input file
+        if (decodedFile.exists()) {
+            assertTrue("Unable to delete - " + mDecodedFile, decodedFile.delete());
+            Log.i(TAG, "Successfully deleted - " + mDecodedFile);
+        }
+        fileInput.close();
     }
 }
diff --git a/media/tests/benchmark/MediaBenchmarkTest/src/androidTest/java/com/android/media/benchmark/tests/ExtractorTest.java b/media/tests/benchmark/MediaBenchmarkTest/src/androidTest/java/com/android/media/benchmark/tests/ExtractorTest.java
index a02011c..a33ecfe 100644
--- a/media/tests/benchmark/MediaBenchmarkTest/src/androidTest/java/com/android/media/benchmark/tests/ExtractorTest.java
+++ b/media/tests/benchmark/MediaBenchmarkTest/src/androidTest/java/com/android/media/benchmark/tests/ExtractorTest.java
@@ -18,12 +18,16 @@
 
 import com.android.media.benchmark.R;
 import com.android.media.benchmark.library.Extractor;
+import com.android.media.benchmark.library.Native;
+import com.android.media.benchmark.library.Stats;
 
 import android.content.Context;
+import android.media.MediaFormat;
 import android.util.Log;
 
 import androidx.test.platform.app.InstrumentationRegistry;
 
+import org.junit.BeforeClass;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
@@ -31,19 +35,23 @@
 import java.io.File;
 import java.io.FileDescriptor;
 import java.io.FileInputStream;
+import java.io.FileOutputStream;
 import java.io.IOException;
 import java.util.Arrays;
 import java.util.Collection;
 
-import static org.hamcrest.CoreMatchers.equalTo;
-import static org.hamcrest.CoreMatchers.is;
-import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertEquals;
+
+import static org.junit.Assert.assertTrue;
 
 @RunWith(Parameterized.class)
 public class ExtractorTest {
     private static Context mContext =
             InstrumentationRegistry.getInstrumentation().getTargetContext();
     private static final String mInputFilePath = mContext.getString(R.string.input_file_path);
+    private static final String mStatsFile =
+            mContext.getFilesDir() + "/Extractor." + System.currentTimeMillis() + ".csv";
     private static final String TAG = "ExtractorTest";
     private String mInputFileName;
     private int mTrackId;
@@ -71,22 +79,42 @@
         this.mTrackId = track;
     }
 
+    @BeforeClass
+    public static void writeStatsHeaderToFile() throws IOException {
+        Stats mStats = new Stats();
+        boolean status = mStats.writeStatsHeader(mStatsFile);
+        assertTrue("Unable to open stats file for writing!", status);
+    }
+
     @Test
     public void sampleExtractTest() throws IOException {
-        int status = -1;
         File inputFile = new File(mInputFilePath + mInputFileName);
-        if (inputFile.exists()) {
-            FileInputStream fileInput = new FileInputStream(inputFile);
-            FileDescriptor fileDescriptor = fileInput.getFD();
-            Extractor extractor = new Extractor();
-            extractor.setUpExtractor(fileDescriptor);
-            status = extractor.extractSample(mTrackId);
-            extractor.deinitExtractor();
-            extractor.dumpStatistics(mInputFileName);
-            fileInput.close();
-        } else {
-            Log.e(TAG, "Cannot find " + mInputFileName + " in directory " + mInputFilePath);
-        }
-        assertThat(status, is(equalTo(0)));
+        assertTrue("Cannot find " + mInputFileName + " in directory " + mInputFilePath,
+                inputFile.exists());
+        FileInputStream fileInput = new FileInputStream(inputFile);
+        FileDescriptor fileDescriptor = fileInput.getFD();
+        Extractor extractor = new Extractor();
+        extractor.setUpExtractor(fileDescriptor);
+        MediaFormat format = extractor.getFormat(mTrackId);
+        String mime = format.getString(MediaFormat.KEY_MIME);
+        int status = extractor.extractSample(mTrackId);
+        assertEquals("Extraction failed for " + mInputFileName, 0, status);
+        Log.i(TAG, "Extracted " + mInputFileName + " successfully.");
+        extractor.deinitExtractor();
+        extractor.dumpStatistics(mInputFileName, mime, mStatsFile);
+        fileInput.close();
+    }
+
+    @Test
+    public void sampleExtractNativeTest() throws IOException {
+        Native nativeExtractor = new Native();
+        File inputFile = new File(mInputFilePath + mInputFileName);
+        assertTrue("Cannot find " + mInputFileName + " in directory " + mInputFilePath,
+                inputFile.exists());
+        FileInputStream fileInput = new FileInputStream(inputFile);
+        int status = nativeExtractor.Extract(mInputFilePath, mInputFileName, mStatsFile);
+        fileInput.close();
+        assertEquals("Extraction failed for " + mInputFileName, 0, status);
+        Log.i(TAG, "Extracted " + mInputFileName + " successfully.");
     }
 }
diff --git a/media/tests/benchmark/MediaBenchmarkTest/src/androidTest/java/com/android/media/benchmark/tests/MuxerTest.java b/media/tests/benchmark/MediaBenchmarkTest/src/androidTest/java/com/android/media/benchmark/tests/MuxerTest.java
index 8c3080c..b69c57b 100644
--- a/media/tests/benchmark/MediaBenchmarkTest/src/androidTest/java/com/android/media/benchmark/tests/MuxerTest.java
+++ b/media/tests/benchmark/MediaBenchmarkTest/src/androidTest/java/com/android/media/benchmark/tests/MuxerTest.java
@@ -18,6 +18,8 @@
 import com.android.media.benchmark.R;
 import com.android.media.benchmark.library.Extractor;
 import com.android.media.benchmark.library.Muxer;
+import com.android.media.benchmark.library.Native;
+import com.android.media.benchmark.library.Stats;
 
 import androidx.test.platform.app.InstrumentationRegistry;
 
@@ -27,6 +29,7 @@
 import android.media.MediaMuxer;
 import android.util.Log;
 
+import org.junit.BeforeClass;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
@@ -34,6 +37,7 @@
 import java.io.File;
 import java.io.FileDescriptor;
 import java.io.FileInputStream;
+import java.io.FileOutputStream;
 import java.io.IOException;
 import java.nio.ByteBuffer;
 import java.util.ArrayList;
@@ -42,15 +46,19 @@
 import java.util.Hashtable;
 import java.util.Map;
 
-import static org.hamcrest.CoreMatchers.equalTo;
-import static org.hamcrest.CoreMatchers.is;
-import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+
+import static org.junit.Assert.assertTrue;
 
 @RunWith(Parameterized.class)
 public class MuxerTest {
     private static Context mContext =
             InstrumentationRegistry.getInstrumentation().getTargetContext();
     private static final String mInputFilePath = mContext.getString(R.string.input_file_path);
+    private static final String mStatsFile =
+            mContext.getFilesDir() + "/Muxer." + System.currentTimeMillis() + ".csv";
     private static final String TAG = "MuxerTest";
     private static final Map<String, Integer> mMapFormat = new Hashtable<String, Integer>() {
         {
@@ -93,60 +101,79 @@
         this.mFormat = outputFormat;
     }
 
+    @BeforeClass
+    public static void writeStatsHeaderToFile() throws IOException {
+        Stats mStats = new Stats();
+        boolean status = mStats.writeStatsHeader(mStatsFile);
+        assertTrue("Unable to open stats file for writing!", status);
+    }
+
     @Test
     public void sampleMuxerTest() throws IOException {
-        int status = -1;
         File inputFile = new File(mInputFilePath + mInputFileName);
-        if (inputFile.exists()) {
-            FileInputStream fileInput = new FileInputStream(inputFile);
-            FileDescriptor fileDescriptor = fileInput.getFD();
-            ArrayList<ByteBuffer> inputBuffer = new ArrayList<>();
-            ArrayList<MediaCodec.BufferInfo> inputBufferInfo = new ArrayList<>();
-            Extractor extractor = new Extractor();
-            int trackCount = extractor.setUpExtractor(fileDescriptor);
-            for (int currentTrack = 0; currentTrack < trackCount; currentTrack++) {
-                extractor.selectExtractorTrack(currentTrack);
-                while (true) {
-                    int sampleSize = extractor.getFrameSample();
-                    MediaCodec.BufferInfo bufferInfo = extractor.getBufferInfo();
-                    MediaCodec.BufferInfo tempBufferInfo = new MediaCodec.BufferInfo();
-                    tempBufferInfo
-                            .set(bufferInfo.offset, bufferInfo.size, bufferInfo.presentationTimeUs,
-                                    bufferInfo.flags);
-                    inputBufferInfo.add(tempBufferInfo);
-                    ByteBuffer tempSampleBuffer = ByteBuffer.allocate(tempBufferInfo.size);
-                    tempSampleBuffer.put(extractor.getFrameBuffer().array(), 0, bufferInfo.size);
-                    inputBuffer.add(tempSampleBuffer);
-                    if (sampleSize < 0) {
-                        break;
-                    }
-                }
-                MediaFormat format = extractor.getFormat(currentTrack);
-                int outputFormat = mMapFormat.getOrDefault(mFormat, -1);
-                if (outputFormat != -1) {
-                    Muxer muxer = new Muxer();
-                    int trackIndex = muxer.setUpMuxer(mContext, outputFormat, format);
-                    status = muxer.mux(trackIndex, inputBuffer, inputBufferInfo);
-                    if (status != 0) {
-                        Log.e(TAG, "Cannot perform write operation for " + mInputFileName);
-                    }
-                    muxer.deInitMuxer();
-                    muxer.dumpStatistics(mInputFileName, extractor.getClipDuration());
-                    muxer.resetMuxer();
-                    extractor.unselectExtractorTrack(currentTrack);
-                    inputBufferInfo.clear();
-                    inputBuffer.clear();
-                } else {
-                    Log.e(TAG, "Test failed for " + mInputFileName + ". Returned invalid " +
-                            "output format for given " + mFormat + " format.");
+        assertTrue("Cannot find " + mInputFileName + " in directory " + mInputFilePath,
+                inputFile.exists());
+        FileInputStream fileInput = new FileInputStream(inputFile);
+        FileDescriptor fileDescriptor = fileInput.getFD();
+        ArrayList<ByteBuffer> inputBuffer = new ArrayList<>();
+        ArrayList<MediaCodec.BufferInfo> inputBufferInfo = new ArrayList<>();
+        Extractor extractor = new Extractor();
+        int trackCount = extractor.setUpExtractor(fileDescriptor);
+        for (int currentTrack = 0; currentTrack < trackCount; currentTrack++) {
+            extractor.selectExtractorTrack(currentTrack);
+            while (true) {
+                int sampleSize = extractor.getFrameSample();
+                MediaCodec.BufferInfo bufferInfo = extractor.getBufferInfo();
+                MediaCodec.BufferInfo tempBufferInfo = new MediaCodec.BufferInfo();
+                tempBufferInfo
+                        .set(bufferInfo.offset, bufferInfo.size, bufferInfo.presentationTimeUs,
+                                bufferInfo.flags);
+                inputBufferInfo.add(tempBufferInfo);
+                ByteBuffer tempSampleBuffer = ByteBuffer.allocate(tempBufferInfo.size);
+                tempSampleBuffer.put(extractor.getFrameBuffer().array(), 0, bufferInfo.size);
+                inputBuffer.add(tempSampleBuffer);
+                if (sampleSize < 0) {
+                    break;
                 }
             }
-            extractor.deinitExtractor();
-            fileInput.close();
-        } else {
-            Log.w(TAG, "Warning: Test Skipped. Cannot find " + mInputFileName + " in directory " +
-                    mInputFilePath);
+            MediaFormat format = extractor.getFormat(currentTrack);
+            int outputFormat = mMapFormat.getOrDefault(mFormat, -1);
+            assertNotEquals("Test failed for " + mInputFileName + ". Returned invalid " +
+                    "output format for given " + mFormat + " format.", -1, outputFormat);
+            Muxer muxer = new Muxer();
+            int trackIndex = muxer.setUpMuxer(mContext, outputFormat, format);
+            int status = muxer.mux(trackIndex, inputBuffer, inputBufferInfo);
+            assertEquals("Cannot perform write operation for " + mInputFileName, 0, status);
+            Log.i(TAG, "Muxed " + mInputFileName + " successfully.");
+            muxer.deInitMuxer();
+            muxer.dumpStatistics(mInputFileName, mFormat, extractor.getClipDuration(), mStatsFile);
+            muxer.resetMuxer();
+            extractor.unselectExtractorTrack(currentTrack);
+            inputBufferInfo.clear();
+            inputBuffer.clear();
+
         }
-        assertThat(status, is(equalTo(0)));
+        extractor.deinitExtractor();
+        fileInput.close();
+    }
+
+    @Test
+    public void sampleMuxerNativeTest() {
+        Native nativeMuxer = new Native();
+        File inputFile = new File(mInputFilePath + mInputFileName);
+        assertTrue("Cannot find " + mInputFileName + " in directory " + mInputFilePath,
+                inputFile.exists());
+        int tid = android.os.Process.myTid();
+        String mMuxOutputFile = (mContext.getFilesDir() + "/mux_" + tid + ".out");
+        int status = nativeMuxer.Mux(
+                mInputFilePath, mInputFileName, mMuxOutputFile, mStatsFile, mFormat);
+        assertEquals("Cannot perform write operation for " + mInputFileName, 0, status);
+        Log.i(TAG, "Muxed " + mInputFileName + " successfully.");
+        File muxedFile = new File(mMuxOutputFile);
+        // Cleanup temporary output file
+        if (muxedFile.exists()) {
+            assertTrue("Unable to delete" + mMuxOutputFile + " file.",
+                    muxedFile.delete());
+        }
     }
 }
diff --git a/media/tests/benchmark/MediaBenchmarkTest/src/main/cpp/Android.bp b/media/tests/benchmark/MediaBenchmarkTest/src/main/cpp/Android.bp
new file mode 100644
index 0000000..c72eb55
--- /dev/null
+++ b/media/tests/benchmark/MediaBenchmarkTest/src/main/cpp/Android.bp
@@ -0,0 +1,32 @@
+cc_test_library {
+    name: "libmediabenchmark_jni",
+
+    defaults: [
+        "libmediabenchmark_common-defaults",
+        "libmediabenchmark_soft_sanitize_all-defaults",
+    ],
+
+    srcs: [
+        "NativeExtractor.cpp",
+        "NativeMuxer.cpp",
+        "NativeEncoder.cpp",
+        "NativeDecoder.cpp",
+    ],
+
+    shared_libs: [
+        "liblog",
+    ],
+
+    static_libs: [
+        "libmediabenchmark_common",
+        "libmediabenchmark_extractor",
+        "libmediabenchmark_muxer",
+        "libmediabenchmark_decoder",
+        "libmediabenchmark_encoder",
+    ],
+
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+}
diff --git a/media/tests/benchmark/MediaBenchmarkTest/src/main/cpp/CMakeLists.txt b/media/tests/benchmark/MediaBenchmarkTest/src/main/cpp/CMakeLists.txt
new file mode 100644
index 0000000..5823883
--- /dev/null
+++ b/media/tests/benchmark/MediaBenchmarkTest/src/main/cpp/CMakeLists.txt
@@ -0,0 +1,44 @@
+#
+# Copyright (C) 2019 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+#
+
+cmake_minimum_required(VERSION 3.4.1)
+
+set(native_source_path "../../../../src/native")
+set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Werror")
+
+add_library(
+  mediabenchmark_jni SHARED
+  NativeExtractor.cpp
+  NativeMuxer.cpp
+  NativeDecoder.cpp
+  NativeEncoder.cpp
+  ${native_source_path}/common/BenchmarkCommon.cpp
+  ${native_source_path}/common/Stats.cpp
+  ${native_source_path}/common/utils/Timers.cpp
+  ${native_source_path}/extractor/Extractor.cpp
+  ${native_source_path}/muxer/Muxer.cpp
+  ${native_source_path}/decoder/Decoder.cpp
+  ${native_source_path}/encoder/Encoder.cpp)
+
+include_directories(${native_source_path}/common)
+include_directories(${native_source_path}/extractor)
+include_directories(${native_source_path}/muxer)
+include_directories(${native_source_path}/decoder)
+include_directories(${native_source_path}/encoder)
+
+find_library(log-lib log)
+
+target_link_libraries(mediabenchmark_jni mediandk ${log-lib})
diff --git a/media/tests/benchmark/MediaBenchmarkTest/src/main/cpp/NativeDecoder.cpp b/media/tests/benchmark/MediaBenchmarkTest/src/main/cpp/NativeDecoder.cpp
new file mode 100644
index 0000000..043bc9e
--- /dev/null
+++ b/media/tests/benchmark/MediaBenchmarkTest/src/main/cpp/NativeDecoder.cpp
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "NativeDecoder"
+
+#include <jni.h>
+#include <fstream>
+#include <stdio.h>
+#include <string.h>
+#include <sys/stat.h>
+
+#include <android/log.h>
+
+#include "Decoder.h"
+
+extern "C" JNIEXPORT int JNICALL Java_com_android_media_benchmark_library_Native_Decode(
+        JNIEnv *env, jobject thiz, jstring jFilePath, jstring jFileName, jstring jStatsFile,
+        jstring jCodecName, jboolean asyncMode) {
+    const char *filePath = env->GetStringUTFChars(jFilePath, nullptr);
+    const char *fileName = env->GetStringUTFChars(jFileName, nullptr);
+    string sFilePath = string(filePath) + string(fileName);
+    UNUSED(thiz);
+    FILE *inputFp = fopen(sFilePath.c_str(), "rb");
+    env->ReleaseStringUTFChars(jFileName, fileName);
+    env->ReleaseStringUTFChars(jFilePath, filePath);
+    if (!inputFp) {
+        ALOGE("Unable to open input file for reading");
+        return -1;
+    }
+
+    Decoder *decoder = new Decoder();
+    Extractor *extractor = decoder->getExtractor();
+    if (!extractor) {
+        ALOGE("Extractor creation failed");
+        return -1;
+    }
+
+    // Read file properties
+    struct stat buf;
+    stat(sFilePath.c_str(), &buf);
+    size_t fileSize = buf.st_size;
+    if (fileSize > kMaxBufferSize) {
+        ALOGE("File size greater than maximum buffer size");
+        return -1;
+    }
+    int32_t fd = fileno(inputFp);
+    int32_t trackCount = extractor->initExtractor(fd, fileSize);
+    if (trackCount <= 0) {
+        ALOGE("initExtractor failed");
+        return -1;
+    }
+    for (int curTrack = 0; curTrack < trackCount; curTrack++) {
+        int32_t status = extractor->setupTrackFormat(curTrack);
+        if (status != 0) {
+            ALOGE("Track Format invalid");
+            return -1;
+        }
+
+        uint8_t *inputBuffer = (uint8_t *) malloc(fileSize);
+        if (!inputBuffer) {
+            ALOGE("Insufficient memory");
+            return -1;
+        }
+
+        vector<AMediaCodecBufferInfo> frameInfo;
+        AMediaCodecBufferInfo info;
+        uint32_t inputBufferOffset = 0;
+
+        // Get frame data
+        while (1) {
+            status = extractor->getFrameSample(info);
+            if (status || !info.size) break;
+            // copy the meta data and buffer to be passed to decoder
+            if (inputBufferOffset + info.size > kMaxBufferSize) {
+                ALOGE("Memory allocated not sufficient");
+                free(inputBuffer);
+                return -1;
+            }
+            memcpy(inputBuffer + inputBufferOffset, extractor->getFrameBuf(), info.size);
+            frameInfo.push_back(info);
+            inputBufferOffset += info.size;
+        }
+
+        const char *codecName = env->GetStringUTFChars(jCodecName, nullptr);
+        string sCodecName = string(codecName);
+        decoder->setupDecoder();
+        status = decoder->decode(inputBuffer, frameInfo, sCodecName, asyncMode);
+        if (status != AMEDIA_OK) {
+            ALOGE("Decode returned error");
+            free(inputBuffer);
+            env->ReleaseStringUTFChars(jCodecName, codecName);
+            return -1;
+        }
+        decoder->deInitCodec();
+        const char *inputReference = env->GetStringUTFChars(jFileName, nullptr);
+        const char *statsFile = env->GetStringUTFChars(jStatsFile, nullptr);
+        string sInputReference = string(inputReference);
+        decoder->dumpStatistics(sInputReference, sCodecName, (asyncMode ? "async" : "sync"),
+                                statsFile);
+        env->ReleaseStringUTFChars(jCodecName, codecName);
+        env->ReleaseStringUTFChars(jStatsFile, statsFile);
+        env->ReleaseStringUTFChars(jFileName, inputReference);
+        if (inputBuffer) {
+            free(inputBuffer);
+            inputBuffer = nullptr;
+        }
+        decoder->resetDecoder();
+    }
+    if (inputFp) {
+        fclose(inputFp);
+        inputFp = nullptr;
+    }
+    extractor->deInitExtractor();
+    delete decoder;
+    return 0;
+}
diff --git a/media/tests/benchmark/MediaBenchmarkTest/src/main/cpp/NativeEncoder.cpp b/media/tests/benchmark/MediaBenchmarkTest/src/main/cpp/NativeEncoder.cpp
new file mode 100644
index 0000000..271b852
--- /dev/null
+++ b/media/tests/benchmark/MediaBenchmarkTest/src/main/cpp/NativeEncoder.cpp
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "NativeEncoder"
+
+#include <jni.h>
+#include <fstream>
+#include <iostream>
+#include <sys/stat.h>
+
+#include <android/log.h>
+
+#include "Decoder.h"
+#include "Encoder.h"
+
+#include <stdio.h>
+
+extern "C" JNIEXPORT int JNICALL Java_com_android_media_benchmark_library_Native_Encode(
+        JNIEnv *env, jobject thiz, jstring jFilePath, jstring jFileName, jstring jOutFilePath,
+        jstring jStatsFile, jstring jCodecName) {
+    const char *filePath = env->GetStringUTFChars(jFilePath, nullptr);
+    const char *fileName = env->GetStringUTFChars(jFileName, nullptr);
+    string sFilePath = string(filePath) + string(fileName);
+    UNUSED(thiz);
+    FILE *inputFp = fopen(sFilePath.c_str(), "rb");
+    env->ReleaseStringUTFChars(jFileName, fileName);
+    env->ReleaseStringUTFChars(jFilePath, filePath);
+    if (!inputFp) {
+        ALOGE("Unable to open input file for reading");
+        return -1;
+    }
+
+    Decoder *decoder = new Decoder();
+    Extractor *extractor = decoder->getExtractor();
+    if (!extractor) {
+        ALOGE("Extractor creation failed");
+        return -1;
+    }
+
+    // Read file properties
+    struct stat buf;
+    stat(sFilePath.c_str(), &buf);
+    size_t fileSize = buf.st_size;
+    if (fileSize > kMaxBufferSize) {
+        ALOGE("File size greater than maximum buffer size");
+        return -1;
+    }
+    int32_t fd = fileno(inputFp);
+    int32_t trackCount = extractor->initExtractor(fd, fileSize);
+    if (trackCount <= 0) {
+        ALOGE("initExtractor failed");
+        return -1;
+    }
+
+    for (int curTrack = 0; curTrack < trackCount; curTrack++) {
+        int32_t status = extractor->setupTrackFormat(curTrack);
+        if (status != 0) {
+            ALOGE("Track Format invalid");
+            return -1;
+        }
+        uint8_t *inputBuffer = (uint8_t *) malloc(fileSize);
+        if (!inputBuffer) {
+            ALOGE("Insufficient memory");
+            return -1;
+        }
+        vector<AMediaCodecBufferInfo> frameInfo;
+        AMediaCodecBufferInfo info;
+        uint32_t inputBufferOffset = 0;
+
+        // Get frame data
+        while (1) {
+            status = extractor->getFrameSample(info);
+            if (status || !info.size) break;
+            // copy the meta data and buffer to be passed to decoder
+            if (inputBufferOffset + info.size > kMaxBufferSize) {
+                ALOGE("Memory allocated not sufficient");
+                free(inputBuffer);
+                return -1;
+            }
+            memcpy(inputBuffer + inputBufferOffset, extractor->getFrameBuf(), info.size);
+            frameInfo.push_back(info);
+            inputBufferOffset += info.size;
+        }
+        string decName = "";
+        const char *outputFilePath = env->GetStringUTFChars(jOutFilePath, nullptr);
+        FILE *outFp = fopen(outputFilePath, "wb");
+        if (outFp == nullptr) {
+            ALOGE("%s - File failed to open for writing!", outputFilePath);
+            free(inputBuffer);
+            return -1;
+        }
+        decoder->setupDecoder();
+        status = decoder->decode(inputBuffer, frameInfo, decName, false /*asyncMode */, outFp);
+        if (status != AMEDIA_OK) {
+            ALOGE("Decode returned error");
+            free(inputBuffer);
+            return -1;
+        }
+        AMediaFormat *format = extractor->getFormat();
+        if (inputBuffer) {
+            free(inputBuffer);
+            inputBuffer = nullptr;
+        }
+        const char *mime = nullptr;
+        AMediaFormat_getString(format, AMEDIAFORMAT_KEY_MIME, &mime);
+        if (!mime) {
+            ALOGE("Error in AMediaFormat_getString");
+            return -1;
+        }
+        ifstream eleStream;
+        eleStream.open(outputFilePath, ifstream::binary | ifstream::ate);
+        if (!eleStream.is_open()) {
+            ALOGE("%s - File failed to open for reading!", outputFilePath);
+            env->ReleaseStringUTFChars(jOutFilePath, outputFilePath);
+            return -1;
+        }
+        const char *codecName = env->GetStringUTFChars(jCodecName, NULL);
+        const char *inputReference = env->GetStringUTFChars(jFileName, nullptr);
+        string sCodecName = string(codecName);
+        string sInputReference = string(inputReference);
+
+        bool asyncMode[2] = {true, false};
+        for (int i = 0; i < 2; i++) {
+            size_t eleSize = eleStream.tellg();
+            eleStream.seekg(0, ifstream::beg);
+
+            // Get encoder params
+            encParameter encParams;
+            if (!strncmp(mime, "video/", 6)) {
+                AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_WIDTH, &encParams.width);
+                AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_HEIGHT, &encParams.height);
+                AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_FRAME_RATE, &encParams.frameRate);
+                AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_BIT_RATE, &encParams.bitrate);
+                if (encParams.bitrate <= 0 || encParams.frameRate <= 0) {
+                    encParams.frameRate = 25;
+                    if (!strcmp(mime, "video/3gpp") || !strcmp(mime, "video/mp4v-es")) {
+                        encParams.bitrate = 600000 /* 600 Kbps */;
+                    } else {
+                        encParams.bitrate = 8000000 /* 8 Mbps */;
+                    }
+                }
+                AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_PROFILE, &encParams.profile);
+                AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_LEVEL, &encParams.level);
+            } else {
+                AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_SAMPLE_RATE, &encParams.sampleRate);
+                AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_CHANNEL_COUNT,
+                                      &encParams.numChannels);
+                encParams.bitrate =
+                        encParams.sampleRate * encParams.numChannels * 16 /* bitsPerSample */;
+            }
+            Encoder *encoder = new Encoder();
+            encoder->setupEncoder();
+            status = encoder->encode(sCodecName, eleStream, eleSize, asyncMode[i], encParams,
+                                     (char *) mime);
+            encoder->deInitCodec();
+            cout << "codec : " << codecName << endl;
+            ALOGV(" asyncMode = %d \n", asyncMode[i]);
+            const char *statsFile = env->GetStringUTFChars(jStatsFile, nullptr);
+            encoder->dumpStatistics(sInputReference, extractor->getClipDuration(), sCodecName,
+                                    (asyncMode[i] ? "async" : "sync"), statsFile);
+            env->ReleaseStringUTFChars(jStatsFile, statsFile);
+            encoder->resetEncoder();
+            delete encoder;
+            encoder = nullptr;
+        }
+        eleStream.close();
+        if (outFp) {
+            fclose(outFp);
+            outFp = nullptr;
+        }
+        env->ReleaseStringUTFChars(jFileName, inputReference);
+        env->ReleaseStringUTFChars(jCodecName, codecName);
+        env->ReleaseStringUTFChars(jOutFilePath, outputFilePath);
+        if (format) {
+            AMediaFormat_delete(format);
+            format = nullptr;
+        }
+        decoder->deInitCodec();
+        decoder->resetDecoder();
+    }
+    if (inputFp) {
+        fclose(inputFp);
+        inputFp = nullptr;
+    }
+    extractor->deInitExtractor();
+    delete decoder;
+    return 0;
+}
diff --git a/media/tests/benchmark/MediaBenchmarkTest/src/main/cpp/NativeExtractor.cpp b/media/tests/benchmark/MediaBenchmarkTest/src/main/cpp/NativeExtractor.cpp
new file mode 100644
index 0000000..a762760
--- /dev/null
+++ b/media/tests/benchmark/MediaBenchmarkTest/src/main/cpp/NativeExtractor.cpp
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "NativeExtractor"
+
+#include <jni.h>
+#include <fstream>
+#include <string>
+#include <sys/stat.h>
+
+#include "Extractor.h"
+
+extern "C" JNIEXPORT int32_t JNICALL Java_com_android_media_benchmark_library_Native_Extract(
+        JNIEnv *env, jobject thiz, jstring jInputFilePath, jstring jInputFileName,
+        jstring jStatsFile) {
+    UNUSED(thiz);
+    const char *inputFilePath = env->GetStringUTFChars(jInputFilePath, nullptr);
+    const char *inputFileName = env->GetStringUTFChars(jInputFileName, nullptr);
+    string sFilePath = string(inputFilePath) + string(inputFileName);
+    FILE *inputFp = fopen(sFilePath.c_str(), "rb");
+
+    // Read file properties
+    struct stat buf;
+    stat(sFilePath.c_str(), &buf);
+    size_t fileSize = buf.st_size;
+    int32_t fd = fileno(inputFp);
+
+    Extractor *extractObj = new Extractor();
+    int32_t trackCount = extractObj->initExtractor((long) fd, fileSize);
+    if (trackCount <= 0) {
+        ALOGE("initExtractor failed");
+        return -1;
+    }
+
+    int32_t trackID = 0;
+    const char *mime = nullptr;
+    int32_t status = extractObj->extract(trackID);
+    if (status != AMEDIA_OK) {
+        ALOGE("Extraction failed");
+        return -1;
+    }
+
+    if (inputFp) {
+        fclose(inputFp);
+        inputFp = nullptr;
+    }
+    status = extractObj->setupTrackFormat(trackID);
+    AMediaFormat *format = extractObj->getFormat();
+    if (!format) {
+        ALOGE("format is null!");
+        return -1;
+    }
+    AMediaFormat_getString(format, AMEDIAFORMAT_KEY_MIME, &mime);
+    if (!mime) {
+        ALOGE("mime is null!");
+        return -1;
+    }
+    extractObj->deInitExtractor();
+    const char *statsFile = env->GetStringUTFChars(jStatsFile, nullptr);
+    extractObj->dumpStatistics(string(inputFileName), string(mime), statsFile);
+    env->ReleaseStringUTFChars(jStatsFile, statsFile);
+    env->ReleaseStringUTFChars(jInputFilePath, inputFilePath);
+    env->ReleaseStringUTFChars(jInputFileName, inputFileName);
+
+    delete extractObj;
+    return status;
+}
diff --git a/media/tests/benchmark/MediaBenchmarkTest/src/main/cpp/NativeMuxer.cpp b/media/tests/benchmark/MediaBenchmarkTest/src/main/cpp/NativeMuxer.cpp
new file mode 100644
index 0000000..a5ef5b8
--- /dev/null
+++ b/media/tests/benchmark/MediaBenchmarkTest/src/main/cpp/NativeMuxer.cpp
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "NativeMuxer"
+
+#include <jni.h>
+#include <fstream>
+#include <string>
+#include <sys/stat.h>
+
+#include "Muxer.h"
+
+MUXER_OUTPUT_T getMuxerOutFormat(const char *fmt);
+
+extern "C" JNIEXPORT int32_t JNICALL Java_com_android_media_benchmark_library_Native_Mux(
+        JNIEnv *env, jobject thiz, jstring jInputFilePath, jstring jInputFileName,
+        jstring jOutputFilePath, jstring jStatsFile, jstring jFormat) {
+    UNUSED(thiz);
+    ALOGV("Mux the samples given by extractor");
+    const char *inputFilePath = env->GetStringUTFChars(jInputFilePath, nullptr);
+    const char *inputFileName = env->GetStringUTFChars(jInputFileName, nullptr);
+    string sInputFile = string(inputFilePath) + string(inputFileName);
+    FILE *inputFp = fopen(sInputFile.c_str(), "rb");
+    if (!inputFp) {
+        ALOGE("Unable to open input file for reading");
+        return -1;
+    }
+
+    const char *fmt = env->GetStringUTFChars(jFormat, nullptr);
+    MUXER_OUTPUT_T outputFormat = getMuxerOutFormat(fmt);
+    if (outputFormat == MUXER_OUTPUT_FORMAT_INVALID) {
+        ALOGE("output format is MUXER_OUTPUT_FORMAT_INVALID");
+        return MUXER_OUTPUT_FORMAT_INVALID;
+    }
+
+    Muxer *muxerObj = new Muxer();
+    Extractor *extractor = muxerObj->getExtractor();
+    if (!extractor) {
+        ALOGE("Extractor creation failed");
+        return -1;
+    }
+
+    // Read file properties
+    struct stat buf;
+    stat(sInputFile.c_str(), &buf);
+    size_t fileSize = buf.st_size;
+    int32_t fd = fileno(inputFp);
+
+    int32_t trackCount = extractor->initExtractor(fd, fileSize);
+    if (trackCount <= 0) {
+        ALOGE("initExtractor failed");
+        return -1;
+    }
+
+    for (int curTrack = 0; curTrack < trackCount; curTrack++) {
+        int32_t status = extractor->setupTrackFormat(curTrack);
+        if (status != 0) {
+            ALOGE("Track Format invalid");
+            return -1;
+        }
+
+        uint8_t *inputBuffer = (uint8_t *) malloc(fileSize);
+        if (!inputBuffer) {
+            ALOGE("Allocation Failed");
+            return -1;
+        }
+        vector<AMediaCodecBufferInfo> frameInfos;
+        AMediaCodecBufferInfo info;
+        uint32_t inputBufferOffset = 0;
+
+        // Get Frame Data
+        while (1) {
+            status = extractor->getFrameSample(info);
+            if (status || !info.size) break;
+            // copy the meta data and buffer to be passed to muxer
+            if (inputBufferOffset + info.size > fileSize) {
+                ALOGE("Memory allocated not sufficient");
+                if (inputBuffer) {
+                    free(inputBuffer);
+                    inputBuffer = nullptr;
+                }
+                return -1;
+            }
+            memcpy(inputBuffer + inputBufferOffset, extractor->getFrameBuf(),
+                   static_cast<size_t>(info.size));
+            info.offset = inputBufferOffset;
+            frameInfos.push_back(info);
+            inputBufferOffset += info.size;
+        }
+
+        const char *outputFilePath = env->GetStringUTFChars(jOutputFilePath, nullptr);
+        FILE *outputFp = fopen(((string) outputFilePath).c_str(), "w+b");
+        env->ReleaseStringUTFChars(jOutputFilePath, outputFilePath);
+
+        if (!outputFp) {
+            ALOGE("Unable to open output file for writing");
+            if (inputBuffer) {
+                free(inputBuffer);
+                inputBuffer = nullptr;
+            }
+            return -1;
+        }
+        int32_t outFd = fileno(outputFp);
+
+        status = muxerObj->initMuxer(outFd, (MUXER_OUTPUT_T) outputFormat);
+        if (status != 0) {
+            ALOGE("initMuxer failed");
+            if (inputBuffer) {
+                free(inputBuffer);
+                inputBuffer = nullptr;
+            }
+            return -1;
+        }
+
+        status = muxerObj->mux(inputBuffer, frameInfos);
+        if (status != 0) {
+            ALOGE("Mux failed");
+            if (inputBuffer) {
+                free(inputBuffer);
+                inputBuffer = nullptr;
+            }
+            return -1;
+        }
+        muxerObj->deInitMuxer();
+        const char *statsFile = env->GetStringUTFChars(jStatsFile, nullptr);
+        string muxFormat(fmt);
+        muxerObj->dumpStatistics(string(inputFileName), muxFormat, statsFile);
+        env->ReleaseStringUTFChars(jStatsFile, statsFile);
+        env->ReleaseStringUTFChars(jInputFilePath, inputFilePath);
+        env->ReleaseStringUTFChars(jInputFileName, inputFileName);
+
+        if (inputBuffer) {
+            free(inputBuffer);
+            inputBuffer = nullptr;
+        }
+        if (outputFp) {
+            fclose(outputFp);
+            outputFp = nullptr;
+        }
+        muxerObj->resetMuxer();
+    }
+    if (inputFp) {
+        fclose(inputFp);
+        inputFp = nullptr;
+    }
+    env->ReleaseStringUTFChars(jFormat, fmt);
+    extractor->deInitExtractor();
+    delete muxerObj;
+
+    return 0;
+}
+
+MUXER_OUTPUT_T getMuxerOutFormat(const char *fmt) {
+    static const struct {
+        const char *name;
+        int value;
+    } kFormatMaps[] = {{"mp4",  MUXER_OUTPUT_FORMAT_MPEG_4},
+                       {"webm", MUXER_OUTPUT_FORMAT_WEBM},
+                       {"3gpp", MUXER_OUTPUT_FORMAT_3GPP},
+                       {"ogg",  MUXER_OUTPUT_FORMAT_OGG}};
+
+    int32_t muxOutputFormat = MUXER_OUTPUT_FORMAT_INVALID;
+    for (auto kFormatMap : kFormatMaps) {
+        if (!strcmp(fmt, kFormatMap.name)) {
+            muxOutputFormat = kFormatMap.value;
+            break;
+        }
+    }
+    return (MUXER_OUTPUT_T) muxOutputFormat;
+}
diff --git a/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Decoder.java b/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Decoder.java
index 2cd27c2..3b1eed4 100644
--- a/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Decoder.java
+++ b/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Decoder.java
@@ -239,11 +239,16 @@
      * Prints out the statistics in the information log
      *
      * @param inputReference The operation being performed, in this case decode
+     * @param componentName  Name of the component/codec
+     * @param mode           The operating mode: Sync/Async
      * @param durationUs     Duration of the clip in microseconds
+     * @param statsFile      The output file where the stats data is written
      */
-    public void dumpStatistics(String inputReference, long durationUs) {
+    public void dumpStatistics(String inputReference, String componentName, String mode,
+            long durationUs, String statsFile) throws IOException {
         String operation = "decode";
-        mStats.dumpStatistics(operation, inputReference, durationUs);
+        mStats.dumpStatistics(
+                inputReference, operation, componentName, mode, durationUs, statsFile);
     }
 
     /**
diff --git a/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Encoder.java b/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Encoder.java
index 03db294..40cf8bd 100644
--- a/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Encoder.java
+++ b/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Encoder.java
@@ -326,12 +326,17 @@
     /**
      * Prints out the statistics in the information log
      *
-     * @param inputReference The operation being performed, in this case encode
+     * @param inputReference The operation being performed, in this case decode
+     * @param componentName  Name of the component/codec
+     * @param mode           The operating mode: Sync/Async
      * @param durationUs     Duration of the clip in microseconds
+     * @param statsFile      The output file where the stats data is written
      */
-    public void dumpStatistics(String inputReference, long durationUs) {
+    public void dumpStatistics(String inputReference, String componentName, String mode,
+                               long durationUs, String statsFile) throws IOException {
         String operation = "encode";
-        mStats.dumpStatistics(operation, inputReference, durationUs);
+        mStats.dumpStatistics(
+                inputReference, operation, componentName, mode, durationUs, statsFile);
     }
 
     /**
diff --git a/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Extractor.java b/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Extractor.java
index 459e2a9..f3024e7 100644
--- a/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Extractor.java
+++ b/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Extractor.java
@@ -167,9 +167,12 @@
      * Write the benchmark logs for the given input file
      *
      * @param inputReference Name of the input file
+     * @param mimeType       Mime type of the muxed file
+     * @param statsFile      The output file where the stats data is written
      */
-    public void dumpStatistics(String inputReference) {
+    public void dumpStatistics(String inputReference, String mimeType, String statsFile)
+            throws IOException {
         String operation = "extract";
-        mStats.dumpStatistics(operation, inputReference, mDurationUs);
+        mStats.dumpStatistics(inputReference, operation, mimeType, "", mDurationUs, statsFile);
     }
 }
diff --git a/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Muxer.java b/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Muxer.java
index 49eaa1c..340b539 100644
--- a/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Muxer.java
+++ b/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Muxer.java
@@ -101,10 +101,13 @@
      * Write the benchmark logs for the given input file
      *
      * @param inputReference Name of the input file
+     * @param muxFormat      Format of the muxed output
      * @param clipDuration   Duration of the given inputReference file
+     * @param statsFile      The output file where the stats data is written
      */
-    public void dumpStatistics(String inputReference, long clipDuration) {
+    public void dumpStatistics(String inputReference, String muxFormat, long clipDuration,
+                               String statsFile) throws IOException {
         String operation = "mux";
-        mStats.dumpStatistics(operation, inputReference, clipDuration);
+        mStats.dumpStatistics(inputReference, operation, muxFormat, "", clipDuration, statsFile);
     }
 }
diff --git a/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Native.java b/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Native.java
new file mode 100644
index 0000000..38b608a
--- /dev/null
+++ b/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Native.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.media.benchmark.library;
+
+public class Native {
+    static { System.loadLibrary("mediabenchmark_jni"); }
+
+    public native int Extract(String inputFilePath, String inputFileName, String statsFile);
+
+    public native int Mux(String inputFilePath, String inputFileName, String outputFilePath,
+            String statsFile, String format);
+
+    public native int Decode(String inputFilePath, String inputFileName, String statsFile,
+            String codecName, boolean asyncMode);
+
+    public native int Encode(String inputFilePath, String inputFileName, String outputFilePath,
+            String statsFile, String codecName);
+}
diff --git a/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Stats.java b/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Stats.java
index 18ab5be..7245a3a 100644
--- a/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Stats.java
+++ b/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Stats.java
@@ -18,6 +18,10 @@
 
 import android.util.Log;
 
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileOutputStream;
+import java.io.IOException;
 import java.util.ArrayList;
 
 /**
@@ -91,14 +95,38 @@
     }
 
     /**
+     * Writes the stats header to a file
+     * <p>
+     * \param statsFile    file where the stats data is to be written
+     **/
+    public boolean writeStatsHeader(String statsFile) throws IOException {
+        File outputFile = new File(statsFile);
+        FileOutputStream out = new FileOutputStream(outputFile, true);
+        if (!outputFile.exists())
+            return false;
+        String statsHeader =
+                "currentTime, fileName, operation, componentName, NDK/SDK, sync/async, setupTime, "
+                        + "destroyTime, minimumTime, maximumTime, "
+                        + "averageTime, timeToProcess1SecContent, totalBytesProcessedPerSec, "
+                        + "timeToFirstFrame, totalSizeInBytes, totalTime\n";
+        out.write(statsHeader.getBytes());
+        out.close();
+        return true;
+    }
+
+    /**
      * Dumps the stats of the operation for a given input media.
      * <p>
+     * \param inputReference input media
      * \param operation      describes the operation performed on the input media
      * (i.e. extract/mux/decode/encode)
-     * \param inputReference input media
-     * \param durationUs    is a duration of the input media in microseconds.
+     * \param componentName  name of the codec/muxFormat/mime
+     * \param mode           the operating mode: sync/async.
+     * \param durationUs     is a duration of the input media in microseconds.
+     * \param statsFile      the file where the stats data is to be written.
      */
-    public void dumpStatistics(String operation, String inputReference, long durationUs) {
+    public void dumpStatistics(String inputReference, String operation, String componentName,
+            String mode, long durationUs, String statsFile) throws IOException {
         if (mOutputTimer.size() == 0) {
             Log.e(TAG, "No output produced");
             return;
@@ -121,18 +149,30 @@
                 maxTimeTakenNs = intervalNs;
             }
         }
-        // Print the Stats
-        Log.i(TAG, "Input Reference : " + inputReference);
-        Log.i(TAG, "Setup Time in nano sec : " + mInitTimeNs);
-        Log.i(TAG, "Average Time in nano sec : " + totalTimeTakenNs / mOutputTimer.size());
-        Log.i(TAG, "Time to first frame in nano sec : " + timeToFirstFrameNs);
-        Log.i(TAG, "Time taken (in nano sec) to " + operation + " 1 sec of content : " +
-                timeTakenPerSec);
-        Log.i(TAG, "Total bytes " + operation + "ed : " + size);
-        Log.i(TAG, "Number of bytes " + operation + "ed per second : " +
-                (size * 1000000000) / totalTimeTakenNs);
-        Log.i(TAG, "Minimum Time in nano sec : " + minTimeTakenNs);
-        Log.i(TAG, "Maximum Time in nano sec : " + maxTimeTakenNs);
-        Log.i(TAG, "Destroy Time in nano sec : " + mDeInitTimeNs);
+
+        // Write the stats row data to file
+        String rowData = "";
+        rowData += System.nanoTime() + ", ";
+        rowData += inputReference + ", ";
+        rowData += operation + ", ";
+        rowData += componentName + ", ";
+        rowData += "SDK, ";
+        rowData += mode + ", ";
+        rowData += mInitTimeNs + ", ";
+        rowData += mDeInitTimeNs + ", ";
+        rowData += minTimeTakenNs + ", ";
+        rowData += maxTimeTakenNs + ", ";
+        rowData += totalTimeTakenNs / mOutputTimer.size() + ", ";
+        rowData += timeTakenPerSec + ", ";
+        rowData += (size * 1000000000) / totalTimeTakenNs + ", ";
+        rowData += timeToFirstFrameNs + ", ";
+        rowData += size + ", ";
+        rowData += totalTimeTakenNs + "\n";
+
+        File outputFile = new File(statsFile);
+        FileOutputStream out = new FileOutputStream(outputFile, true);
+        assert outputFile.exists() : "Failed to open the stats file for writing!";
+        out.write(rowData.getBytes());
+        out.close();
     }
-}
\ No newline at end of file
+}
diff --git a/media/tests/benchmark/README.md b/media/tests/benchmark/README.md
index 6627462..05fbe6f 100644
--- a/media/tests/benchmark/README.md
+++ b/media/tests/benchmark/README.md
@@ -145,3 +145,12 @@
 ```
 adb shell /data/local/tmp/C2DecoderTest -P /data/local/tmp/MediaBenchmark/res/
 ```
+## C2 Encoder
+
+The test encodes input stream and benchmarks the codec2 encoders available in device.
+
+Setup steps are same as [extractor](#extractor).
+
+```
+adb shell /data/local/tmp/C2EncoderTest -P /data/local/tmp/MediaBenchmark/res/
+```
diff --git a/media/tests/benchmark/src/native/common/Android.bp b/media/tests/benchmark/src/native/common/Android.bp
index babc329..f8ea25c 100644
--- a/media/tests/benchmark/src/native/common/Android.bp
+++ b/media/tests/benchmark/src/native/common/Android.bp
@@ -29,7 +29,7 @@
 
     export_include_dirs: ["."],
 
-    ldflags: ["-Wl,-Bsymbolic"]
+    ldflags: ["-Wl,-Bsymbolic"],
 }
 
 cc_defaults {
@@ -55,7 +55,7 @@
     cflags: [
         "-Wall",
         "-Werror",
-    ]
+    ],
 }
 
 cc_library_static {
@@ -66,18 +66,20 @@
 
     srcs: [
         "BenchmarkC2Common.cpp",
+        "BenchmarkCommon.cpp",
+        "Stats.cpp",
+        "utils/Timers.cpp",
     ],
 
     export_include_dirs: ["."],
 
-    ldflags: ["-Wl,-Bsymbolic"]
+    ldflags: ["-Wl,-Bsymbolic"],
 }
 
 cc_defaults {
     name: "libmediabenchmark_codec2_common-defaults",
 
     defaults: [
-        "libmediabenchmark_common-defaults",
         "libcodec2-hidl-client-defaults",
         "libmediabenchmark_soft_sanitize_all-defaults",
     ],
@@ -88,7 +90,14 @@
 
     shared_libs: [
         "libcodec2_client",
-    ]
+        "libmediandk",
+        "liblog",
+    ],
+
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
 }
 
 // public dependency for native implementation
@@ -102,5 +111,5 @@
             "signed-integer-overflow",
         ],
         cfi: true,
-    }
+    },
 }
diff --git a/media/tests/benchmark/src/native/common/BenchmarkCommon.cpp b/media/tests/benchmark/src/native/common/BenchmarkCommon.cpp
index ab74508..cb49b8e 100644
--- a/media/tests/benchmark/src/native/common/BenchmarkCommon.cpp
+++ b/media/tests/benchmark/src/native/common/BenchmarkCommon.cpp
@@ -56,6 +56,7 @@
 
 void OnErrorCB(AMediaCodec *codec, void *userdata, media_status_t err, int32_t actionCode,
                const char *detail) {
+    (void)codec;
     ALOGE("OnErrorCB: err(%d), actionCode(%d), detail(%s)", err, actionCode, detail);
     CallBackHandle *self = (CallBackHandle *)userdata;
     self->mSawError = true;
@@ -91,7 +92,7 @@
 
     /* Configure codec with the given format*/
     const char *s = AMediaFormat_toString(format);
-    ALOGV("Input format: %s\n", s);
+    ALOGI("Input format: %s\n", s);
 
     media_status_t status = AMediaCodec_configure(codec, format, nullptr, nullptr, isEncoder);
     if (status != AMEDIA_OK) {
@@ -99,4 +100,4 @@
         return nullptr;
     }
     return codec;
-}
\ No newline at end of file
+}
diff --git a/media/tests/benchmark/src/native/common/BenchmarkCommon.h b/media/tests/benchmark/src/native/common/BenchmarkCommon.h
index 8153a86..c11fe36 100644
--- a/media/tests/benchmark/src/native/common/BenchmarkCommon.h
+++ b/media/tests/benchmark/src/native/common/BenchmarkCommon.h
@@ -17,15 +17,18 @@
 #ifndef __BENCHMARK_COMMON_H__
 #define __BENCHMARK_COMMON_H__
 
+#include <sys/stat.h>
 #include <inttypes.h>
 #include <mutex>
 #include <queue>
 #include <thread>
+#include <iostream>
 
 #include <media/NdkMediaCodec.h>
 #include <media/NdkMediaError.h>
 
 #include "Stats.h"
+#define UNUSED(x) (void)(x)
 
 using namespace std;
 
diff --git a/media/tests/benchmark/src/native/common/Stats.cpp b/media/tests/benchmark/src/native/common/Stats.cpp
index 2d9bb31..bfde125 100644
--- a/media/tests/benchmark/src/native/common/Stats.cpp
+++ b/media/tests/benchmark/src/native/common/Stats.cpp
@@ -17,8 +17,10 @@
 //#define LOG_NDEBUG 0
 #define LOG_TAG "Stats"
 
+#include <ctime>
 #include <iostream>
 #include <stdint.h>
+#include <fstream>
 
 #include "Stats.h"
 
@@ -28,16 +30,20 @@
  * \param operation      describes the operation performed on the input media
  *                       (i.e. extract/mux/decode/encode)
  * \param inputReference input media
- * \param duarationUs    is a duration of the input media in microseconds.
+ * \param durationUs     is a duration of the input media in microseconds.
+ * \param componentName  describes the codecName/muxFormat/mimeType.
+ * \param mode           the operating mode: sync/async.
+ * \param statsFile      the file where the stats data is to be written.
  */
-void Stats::dumpStatistics(std::string operation, std::string inputReference, int64_t duarationUs) {
+void Stats::dumpStatistics(string operation, string inputReference, int64_t durationUs,
+                           string componentName, string mode, string statsFile) {
     ALOGV("In %s", __func__);
     if (!mOutputTimer.size()) {
         ALOGE("No output produced");
         return;
     }
     nsecs_t totalTimeTakenNs = getTotalTime();
-    nsecs_t timeTakenPerSec = (totalTimeTakenNs * 1000000) / duarationUs;
+    nsecs_t timeTakenPerSec = (totalTimeTakenNs * 1000000) / durationUs;
     nsecs_t timeToFirstFrameNs = *mOutputTimer.begin() - mStartTimeNs;
     int32_t size = std::accumulate(mFrameSizes.begin(), mFrameSizes.end(), 0);
     // get min and max output intervals.
@@ -52,15 +58,32 @@
         else if (maxTimeTakenNs < intervalNs) maxTimeTakenNs = intervalNs;
     }
 
-    // Print the Stats
-    std::cout << "Input Reference : " << inputReference << endl;
-    std::cout << "Setup Time in nano sec : " << mInitTimeNs << endl;
-    std::cout << "Average Time in nano sec : " << totalTimeTakenNs / mOutputTimer.size() << endl;
-    std::cout << "Time to first frame in nano sec : " << timeToFirstFrameNs << endl;
-    std::cout << "Time taken (in nano sec) to " << operation
-              << " 1 sec of content : " << timeTakenPerSec << endl;
-    std::cout << "Total bytes " << operation << "ed : " << size << endl;
-    std::cout << "Minimum Time in nano sec : " << minTimeTakenNs << endl;
-    std::cout << "Maximum Time in nano sec : " << maxTimeTakenNs << endl;
-    std::cout << "Destroy Time in nano sec : " << mDeInitTimeNs << endl;
+    // Write the stats data to file.
+    int64_t dataSize = size;
+    int64_t bytesPerSec = ((int64_t)dataSize * 1000000000) / totalTimeTakenNs;
+    string rowData = "";
+    rowData.append(to_string(systemTime(CLOCK_MONOTONIC)) + ", ");
+    rowData.append(inputReference + ", ");
+    rowData.append(operation + ", ");
+    rowData.append(componentName + ", ");
+    rowData.append("NDK, ");
+    rowData.append(mode + ", ");
+    rowData.append(to_string(mInitTimeNs) + ", ");
+    rowData.append(to_string(mDeInitTimeNs) + ", ");
+    rowData.append(to_string(minTimeTakenNs) + ", ");
+    rowData.append(to_string(maxTimeTakenNs) + ", ");
+    rowData.append(to_string(totalTimeTakenNs / mOutputTimer.size()) + ", ");
+    rowData.append(to_string(timeTakenPerSec) + ", ");
+    rowData.append(to_string(bytesPerSec) + ", ");
+    rowData.append(to_string(timeToFirstFrameNs) + ", ");
+    rowData.append(to_string(size) + ",");
+    rowData.append(to_string(totalTimeTakenNs) + ",\n");
+
+    ofstream out(statsFile, ios::out | ios::app);
+    if(out.bad()) {
+        ALOGE("Failed to open stats file for writing!");
+        return;
+    }
+    out << rowData;
+    out.close();
 }
diff --git a/media/tests/benchmark/src/native/common/Stats.h b/media/tests/benchmark/src/native/common/Stats.h
index 2f556ee..18e4b06 100644
--- a/media/tests/benchmark/src/native/common/Stats.h
+++ b/media/tests/benchmark/src/native/common/Stats.h
@@ -18,6 +18,7 @@
 #define __STATS_H__
 
 #include <android/log.h>
+#include <inttypes.h>
 
 #ifndef ALOG
 #define ALOG(priority, tag, ...) ((void)__android_log_print(ANDROID_##priority, tag, __VA_ARGS__))
@@ -25,6 +26,12 @@
 #define ALOGI(...) ALOG(LOG_INFO, LOG_TAG, __VA_ARGS__)
 #define ALOGE(...) ALOG(LOG_ERROR, LOG_TAG, __VA_ARGS__)
 #define ALOGD(...) ALOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__)
+#define ALOGW(...) ALOG(LOG_WARN, LOG_TAG, __VA_ARGS__)
+
+#ifndef LOG_NDEBUG
+#define LOG_NDEBUG 1
+#endif
+
 #if LOG_NDEBUG
 #define ALOGV(cond, ...)   ((void)0)
 #else
@@ -95,7 +102,8 @@
         return (*(mOutputTimer.end() - 1) - mStartTimeNs);
     }
 
-    void dumpStatistics(std::string operation, std::string inputReference, int64_t duarationUs);
+    void dumpStatistics(string operation, string inputReference, int64_t duarationUs,
+                        string codecName = "", string mode = "", string statsFile = "");
 };
 
 #endif  // __STATS_H__
diff --git a/media/tests/benchmark/src/native/decoder/Android.bp b/media/tests/benchmark/src/native/decoder/Android.bp
index b5072ab..9791c11 100644
--- a/media/tests/benchmark/src/native/decoder/Android.bp
+++ b/media/tests/benchmark/src/native/decoder/Android.bp
@@ -27,24 +27,26 @@
 
     export_include_dirs: ["."],
 
-    ldflags: ["-Wl,-Bsymbolic"]
+    ldflags: ["-Wl,-Bsymbolic"],
 }
 
 cc_library_static {
     name: "libmediabenchmark_codec2_decoder",
     defaults: [
-        "libmediabenchmark_common-defaults",
         "libmediabenchmark_codec2_common-defaults",
     ],
 
-    srcs: ["C2Decoder.cpp"],
+    srcs: [
+        "C2Decoder.cpp",
+        "Decoder.cpp",
+    ],
 
     static_libs: [
         "libmediabenchmark_codec2_common",
-        "libmediabenchmark_extractor",
+        "libmediabenchmark_codec2_extractor",
     ],
 
     export_include_dirs: ["."],
 
-    ldflags: ["-Wl,-Bsymbolic"]
+    ldflags: ["-Wl,-Bsymbolic"],
 }
diff --git a/media/tests/benchmark/src/native/decoder/Decoder.cpp b/media/tests/benchmark/src/native/decoder/Decoder.cpp
index ac0d525..b797cc9 100644
--- a/media/tests/benchmark/src/native/decoder/Decoder.cpp
+++ b/media/tests/benchmark/src/native/decoder/Decoder.cpp
@@ -238,10 +238,11 @@
     mStats->setDeInitTime(timeTaken);
 }
 
-void Decoder::dumpStatistics(string inputReference) {
+void Decoder::dumpStatistics(string inputReference, string componentName, string mode,
+                             string statsFile) {
     int64_t durationUs = mExtractor->getClipDuration();
     string operation = "decode";
-    mStats->dumpStatistics(operation, inputReference, durationUs);
+    mStats->dumpStatistics(operation, inputReference, durationUs, componentName, mode, statsFile);
 }
 
 void Decoder::resetDecoder() {
diff --git a/media/tests/benchmark/src/native/decoder/Decoder.h b/media/tests/benchmark/src/native/decoder/Decoder.h
index aeda080..f3fa6a1 100644
--- a/media/tests/benchmark/src/native/decoder/Decoder.h
+++ b/media/tests/benchmark/src/native/decoder/Decoder.h
@@ -71,7 +71,8 @@
     int32_t decode(uint8_t *inputBuffer, vector<AMediaCodecBufferInfo> &frameInfo,
                    string &codecName, bool asyncMode, FILE *outFp = nullptr);
 
-    void dumpStatistics(string inputReference);
+    void dumpStatistics(string inputReference, string componentName = "", string mode = "",
+                        string statsFile = "");
 
   private:
     AMediaCodec *mCodec;
diff --git a/media/tests/benchmark/src/native/encoder/Android.bp b/media/tests/benchmark/src/native/encoder/Android.bp
index 239f378..8de7823 100644
--- a/media/tests/benchmark/src/native/encoder/Android.bp
+++ b/media/tests/benchmark/src/native/encoder/Android.bp
@@ -23,11 +23,31 @@
 
     srcs: ["Encoder.cpp"],
 
-    static_libs: ["libmediabenchmark_extractor",
-                  "libmediabenchmark_decoder",
+    static_libs: [
+        "libmediabenchmark_extractor",
+        "libmediabenchmark_decoder",
     ],
 
     export_include_dirs: ["."],
 
-    ldflags: ["-Wl,-Bsymbolic"]
+    ldflags: ["-Wl,-Bsymbolic"],
+}
+
+cc_library_static {
+    name: "libmediabenchmark_codec2_encoder",
+    defaults: [
+        "libmediabenchmark_codec2_common-defaults",
+    ],
+
+    srcs: ["C2Encoder.cpp"],
+
+    static_libs: [
+        "libmediabenchmark_codec2_common",
+        "libmediabenchmark_codec2_extractor",
+        "libmediabenchmark_codec2_decoder",
+    ],
+
+    export_include_dirs: ["."],
+
+    ldflags: ["-Wl,-Bsymbolic"],
 }
diff --git a/media/tests/benchmark/src/native/encoder/C2Encoder.cpp b/media/tests/benchmark/src/native/encoder/C2Encoder.cpp
new file mode 100644
index 0000000..33429ef
--- /dev/null
+++ b/media/tests/benchmark/src/native/encoder/C2Encoder.cpp
@@ -0,0 +1,264 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "C2Encoder"
+
+#include "C2Encoder.h"
+
+int32_t C2Encoder::createCodec2Component(string compName, AMediaFormat *format) {
+    ALOGV("In %s", __func__);
+    mListener.reset(new CodecListener(
+            [this](std::list<std::unique_ptr<C2Work>> &workItems) { handleWorkDone(workItems); }));
+    if (!mListener) return -1;
+
+    const char *mime = nullptr;
+    AMediaFormat_getString(format, AMEDIAFORMAT_KEY_MIME, &mime);
+    if (!mime) {
+        ALOGE("Error in AMediaFormat_getString");
+        return -1;
+    }
+    // Configure the plugin with Input properties
+    std::vector<C2Param *> configParam;
+    if (!strncmp(mime, "audio/", 6)) {
+        mIsAudioEncoder = true;
+        int32_t numChannels;
+        if (!AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_SAMPLE_RATE, &mSampleRate)) {
+            ALOGE("AMEDIAFORMAT_KEY_SAMPLE_RATE not set");
+            return -1;
+        }
+        if (!AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_CHANNEL_COUNT, &numChannels)) {
+            ALOGE("AMEDIAFORMAT_KEY_CHANNEL_COUNT not set");
+            return -1;
+        }
+        C2StreamSampleRateInfo::input sampleRateInfo(0u, mSampleRate);
+        C2StreamChannelCountInfo::input channelCountInfo(0u, numChannels);
+        configParam.push_back(&sampleRateInfo);
+        configParam.push_back(&channelCountInfo);
+    } else {
+        mIsAudioEncoder = false;
+        if (!AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_WIDTH, &mWidth)) {
+            ALOGE("AMEDIAFORMAT_KEY_WIDTH not set");
+            return -1;
+        }
+        if (!AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_HEIGHT, &mHeight)) {
+            ALOGE("AMEDIAFORMAT_KEY_HEIGHT not set");
+            return -1;
+        }
+        C2StreamPictureSizeInfo::input inputSize(0u, mWidth, mHeight);
+        configParam.push_back(&inputSize);
+
+        if (!AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_FRAME_RATE, &mFrameRate) ||
+            (mFrameRate <= 0)) {
+            mFrameRate = KDefaultFrameRate;
+        }
+    }
+
+    int64_t sTime = mStats->getCurTime();
+    mComponent = mClient->CreateComponentByName(compName.c_str(), mListener, &mClient);
+    if (mComponent == nullptr) {
+        ALOGE("Create component failed for %s", compName.c_str());
+        return -1;
+    }
+    std::vector<std::unique_ptr<C2SettingResult>> failures;
+    int32_t status = mComponent->config(configParam, C2_DONT_BLOCK, &failures);
+    if (failures.size() != 0) {
+        ALOGE("Invalid Configuration");
+        return -1;
+    }
+
+    status |= mComponent->start();
+    int64_t eTime = mStats->getCurTime();
+    int64_t timeTaken = mStats->getTimeDiff(sTime, eTime);
+    mStats->setInitTime(timeTaken);
+    return status;
+}
+
+// In encoder components, fetch the size of input buffer allocated
+int32_t C2Encoder::getInputMaxBufSize() {
+    int32_t bitStreamInfo[1] = {0};
+    std::vector<std::unique_ptr<C2Param>> inParams;
+    c2_status_t status = mComponent->query({}, {C2StreamMaxBufferSizeInfo::input::PARAM_TYPE},
+                                           C2_DONT_BLOCK, &inParams);
+    if (status != C2_OK && inParams.size() == 0) {
+        ALOGE("Query MaxBufferSizeInfo failed => %d", status);
+        return status;
+    } else {
+        size_t offset = sizeof(C2Param);
+        for (size_t i = 0; i < inParams.size(); ++i) {
+            C2Param *param = inParams[i].get();
+            bitStreamInfo[i] = *(int32_t *)((uint8_t *)param + offset);
+        }
+    }
+    mInputMaxBufSize = bitStreamInfo[0];
+    if (mInputMaxBufSize < 0) {
+        ALOGE("Invalid mInputMaxBufSize %d\n", mInputMaxBufSize);
+        return -1;
+    }
+    return status;
+}
+
+int32_t C2Encoder::encodeFrames(ifstream &eleStream, size_t inputBufferSize) {
+    ALOGV("In %s", __func__);
+    int32_t frameSize = 0;
+    if (!mIsAudioEncoder) {
+        frameSize = mWidth * mHeight * 3 / 2;
+    } else {
+        frameSize = DEFAULT_AUDIO_FRAME_SIZE;
+        if (getInputMaxBufSize() != 0) return -1;
+        if (frameSize > mInputMaxBufSize) {
+            frameSize = mInputMaxBufSize;
+        }
+    }
+    int32_t numFrames = (inputBufferSize + frameSize - 1) / frameSize;
+    // Temporary buffer to read data from the input file
+    char *data = (char *)malloc(frameSize);
+    if (!data) {
+        ALOGE("Insufficient memory to read from input file");
+        return -1;
+    }
+
+    typedef std::unique_lock<std::mutex> ULock;
+    uint64_t presentationTimeUs = 0;
+    size_t offset = 0;
+    c2_status_t status = C2_OK;
+
+    mStats->setStartTime();
+    while (numFrames > 0) {
+        std::unique_ptr<C2Work> work;
+        // Prepare C2Work
+        {
+            ULock l(mQueueLock);
+            if (mWorkQueue.empty()) mQueueCondition.wait_for(l, MAX_RETRY * TIME_OUT);
+            if (!mWorkQueue.empty()) {
+                mStats->addInputTime();
+                work.swap(mWorkQueue.front());
+                mWorkQueue.pop_front();
+            } else {
+                cout << "Wait for generating C2Work exceeded timeout" << endl;
+                return -1;
+            }
+        }
+
+        if (mIsAudioEncoder) {
+            presentationTimeUs = mNumInputFrame * frameSize * (1000000 / mSampleRate);
+        } else {
+            presentationTimeUs = mNumInputFrame * (1000000 / mFrameRate);
+        }
+        uint32_t flags = 0;
+        if (numFrames == 1) flags |= C2FrameData::FLAG_END_OF_STREAM;
+
+        work->input.flags = (C2FrameData::flags_t)flags;
+        work->input.ordinal.timestamp = presentationTimeUs;
+        work->input.ordinal.frameIndex = mNumInputFrame;
+        work->input.buffers.clear();
+
+        if (inputBufferSize - offset < frameSize) {
+            frameSize = inputBufferSize - offset;
+        }
+        eleStream.read(data, frameSize);
+        if (eleStream.gcount() != frameSize) {
+            ALOGE("read() from file failed. Incorrect bytes read");
+            return -1;
+        }
+        offset += frameSize;
+
+        if (frameSize) {
+            if (mIsAudioEncoder) {
+                std::shared_ptr<C2LinearBlock> block;
+                status = mLinearPool->fetchLinearBlock(
+                        frameSize, {C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE}, &block);
+                if (status != C2_OK || !block) {
+                    cout << "fetchLinearBlock failed : " << status << endl;
+                    return status;
+                }
+                C2WriteView view = block->map().get();
+                if (view.error() != C2_OK) {
+                    cout << "C2LinearBlock::map() failed : " << view.error() << endl;
+                    return view.error();
+                }
+
+                memcpy(view.base(), data, frameSize);
+                work->input.buffers.emplace_back(new LinearBuffer(block));
+            } else {
+                std::shared_ptr<C2GraphicBlock> block;
+                status = mGraphicPool->fetchGraphicBlock(
+                        mWidth, mHeight, HAL_PIXEL_FORMAT_YV12,
+                        {C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE}, &block);
+                if (status != C2_OK || !block) {
+                    cout << "fetchGraphicBlock failed : " << status << endl;
+                    return status;
+                }
+                C2GraphicView view = block->map().get();
+                if (view.error() != C2_OK) {
+                    cout << "C2GraphicBlock::map() failed : " << view.error() << endl;
+                    return view.error();
+                }
+
+                uint8_t *pY = view.data()[C2PlanarLayout::PLANE_Y];
+                uint8_t *pU = view.data()[C2PlanarLayout::PLANE_U];
+                uint8_t *pV = view.data()[C2PlanarLayout::PLANE_V];
+                memcpy(pY, data, mWidth * mHeight);
+                memcpy(pU, data + mWidth * mHeight, (mWidth * mHeight >> 2));
+                memcpy(pV, data + (mWidth * mHeight * 5 >> 2), mWidth * mHeight >> 2);
+                work->input.buffers.emplace_back(new GraphicBuffer(block));
+            }
+            mStats->addFrameSize(frameSize);
+        }
+
+        work->worklets.clear();
+        work->worklets.emplace_back(new C2Worklet);
+
+        std::list<std::unique_ptr<C2Work>> items;
+        items.push_back(std::move(work));
+        // queue() invokes process() function of C2 Plugin.
+        status = mComponent->queue(&items);
+        if (status != C2_OK) {
+            ALOGE("queue failed");
+            return status;
+        }
+        ALOGV("Frame #%d size = %d queued", mNumInputFrame, frameSize);
+        numFrames--;
+        mNumInputFrame++;
+    }
+    free(data);
+    return status;
+}
+
+void C2Encoder::deInitCodec() {
+    ALOGV("In %s", __func__);
+    if (!mComponent) return;
+
+    int64_t sTime = mStats->getCurTime();
+    mComponent->stop();
+    mComponent->release();
+    mComponent = nullptr;
+    int64_t eTime = mStats->getCurTime();
+    int64_t timeTaken = mStats->getTimeDiff(sTime, eTime);
+    mStats->setDeInitTime(timeTaken);
+}
+
+void C2Encoder::dumpStatistics(string inputReference, int64_t durationUs) {
+    string operation = "c2encode";
+    mStats->dumpStatistics(operation, inputReference, durationUs);
+}
+
+void C2Encoder::resetEncoder() {
+    mIsAudioEncoder = false;
+    mNumInputFrame = 0;
+    mEos = false;
+    if (mStats) mStats->reset();
+}
diff --git a/media/tests/benchmark/src/native/encoder/C2Encoder.h b/media/tests/benchmark/src/native/encoder/C2Encoder.h
new file mode 100644
index 0000000..a4ca097
--- /dev/null
+++ b/media/tests/benchmark/src/native/encoder/C2Encoder.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __C2_ENCODER_H__
+#define __C2_ENCODER_H__
+
+#include <stdio.h>
+#include <algorithm>
+#include <fstream>
+
+#include "BenchmarkC2Common.h"
+
+#define DEFAULT_AUDIO_FRAME_SIZE 4096
+
+constexpr int32_t KDefaultFrameRate = 25;
+
+class C2Encoder : public BenchmarkC2Common {
+  public:
+    C2Encoder()
+        : mIsAudioEncoder(false),
+          mWidth(0),
+          mHeight(0),
+          mNumInputFrame(0),
+          mComponent(nullptr) {}
+
+    int32_t createCodec2Component(string codecName, AMediaFormat *format);
+
+    int32_t encodeFrames(ifstream &eleStream, size_t inputBufferSize);
+
+    int32_t getInputMaxBufSize();
+
+    void deInitCodec();
+
+    void dumpStatistics(string inputReference, int64_t durationUs);
+
+    void resetEncoder();
+
+  private:
+    bool mIsAudioEncoder;
+
+    int32_t mWidth;
+    int32_t mHeight;
+    int32_t mFrameRate;
+    int32_t mSampleRate;
+
+    int32_t mNumInputFrame;
+    int32_t mInputMaxBufSize;
+
+    std::shared_ptr<android::Codec2Client::Listener> mListener;
+    std::shared_ptr<android::Codec2Client::Component> mComponent;
+};
+
+#endif  // __C2_ENCODER_H__
diff --git a/media/tests/benchmark/src/native/encoder/Encoder.cpp b/media/tests/benchmark/src/native/encoder/Encoder.cpp
index a5605de..f119cf3 100644
--- a/media/tests/benchmark/src/native/encoder/Encoder.cpp
+++ b/media/tests/benchmark/src/native/encoder/Encoder.cpp
@@ -174,9 +174,10 @@
     memset(&mParams, 0, sizeof mParams);
 }
 
-void Encoder::dumpStatistics(string inputReference, int64_t durationUs) {
+void Encoder::dumpStatistics(string inputReference, int64_t durationUs, string componentName,
+                             string mode, string statsFile) {
     string operation = "encode";
-    mStats->dumpStatistics(operation, inputReference, durationUs);
+    mStats->dumpStatistics(operation, inputReference, durationUs, componentName, mode, statsFile);
 }
 
 int32_t Encoder::encode(string &codecName, ifstream &eleStream, size_t eleSize,
@@ -206,7 +207,7 @@
         AMediaFormat_setInt32(mFormat, AMEDIAFORMAT_KEY_BIT_RATE, mParams.bitrate);
     }
     const char *s = AMediaFormat_toString(mFormat);
-    ALOGV("Input format: %s\n", s);
+    ALOGI("Input format: %s\n", s);
 
     int64_t sTime = mStats->getCurTime();
     mCodec = createMediaCodec(mFormat, mMime, codecName, true /*isEncoder*/);
diff --git a/media/tests/benchmark/src/native/encoder/Encoder.h b/media/tests/benchmark/src/native/encoder/Encoder.h
index 6059c4a..3d12600 100644
--- a/media/tests/benchmark/src/native/encoder/Encoder.h
+++ b/media/tests/benchmark/src/native/encoder/Encoder.h
@@ -75,7 +75,8 @@
     int32_t encode(std::string &codecName, std::ifstream &eleStream, size_t eleSize, bool asyncMode,
                    encParameter encParams, char *mime);
 
-    void dumpStatistics(string inputReference, int64_t durationUs);
+    void dumpStatistics(string inputReference, int64_t durationUs, string codecName = "",
+                        string mode = "", string statsFile = "");
 
   private:
     AMediaCodec *mCodec;
diff --git a/media/tests/benchmark/src/native/extractor/Android.bp b/media/tests/benchmark/src/native/extractor/Android.bp
index dfd0d49..7ed9476 100644
--- a/media/tests/benchmark/src/native/extractor/Android.bp
+++ b/media/tests/benchmark/src/native/extractor/Android.bp
@@ -27,3 +27,20 @@
 
     ldflags: ["-Wl,-Bsymbolic"]
 }
+
+cc_library_static {
+    name: "libmediabenchmark_codec2_extractor",
+    defaults: [
+        "libmediabenchmark_codec2_common-defaults",
+    ],
+
+    srcs: ["Extractor.cpp"],
+
+    static_libs: [
+        "libmediabenchmark_codec2_common",
+    ],
+
+    export_include_dirs: ["."],
+
+    ldflags: ["-Wl,-Bsymbolic"]
+}
diff --git a/media/tests/benchmark/src/native/extractor/Extractor.cpp b/media/tests/benchmark/src/native/extractor/Extractor.cpp
index b4cad0b..f0bb3b9 100644
--- a/media/tests/benchmark/src/native/extractor/Extractor.cpp
+++ b/media/tests/benchmark/src/native/extractor/Extractor.cpp
@@ -111,9 +111,9 @@
     return AMEDIA_OK;
 }
 
-void Extractor::dumpStatistics(string inputReference) {
+void Extractor::dumpStatistics(string inputReference, string componentName, string statsFile) {
     string operation = "extract";
-    mStats->dumpStatistics(operation, inputReference, mDurationUs);
+    mStats->dumpStatistics(operation, inputReference, mDurationUs, componentName, "", statsFile);
 }
 
 void Extractor::deInitExtractor() {
diff --git a/media/tests/benchmark/src/native/extractor/Extractor.h b/media/tests/benchmark/src/native/extractor/Extractor.h
index 4c39a72..1694fc7 100644
--- a/media/tests/benchmark/src/native/extractor/Extractor.h
+++ b/media/tests/benchmark/src/native/extractor/Extractor.h
@@ -45,7 +45,7 @@
 
     int32_t extract(int32_t trackId);
 
-    void dumpStatistics(std::string inputReference);
+    void dumpStatistics(string inputReference, string componentName = "", string statsFile = "");
 
     void deInitExtractor();
 
diff --git a/media/tests/benchmark/src/native/muxer/Muxer.cpp b/media/tests/benchmark/src/native/muxer/Muxer.cpp
index b297a66..f8627cb 100644
--- a/media/tests/benchmark/src/native/muxer/Muxer.cpp
+++ b/media/tests/benchmark/src/native/muxer/Muxer.cpp
@@ -29,7 +29,7 @@
     int64_t sTime = mStats->getCurTime();
     mMuxer = AMediaMuxer_new(fd, (OutputFormat)outputFormat);
     if (!mMuxer) {
-        cout << "[   WARN   ] Test Skipped. Unable to create muxer \n";
+        ALOGV("Unable to create muxer");
         return AMEDIA_ERROR_INVALID_OBJECT;
     }
     /*
@@ -38,7 +38,7 @@
      */
     ssize_t index = AMediaMuxer_addTrack(mMuxer, mFormat);
     if (index < 0) {
-        cout << "[   WARN   ] Test Skipped. Format not supported \n";
+        ALOGV("Format not supported");
         return index;
     }
     AMediaMuxer_start(mMuxer);
@@ -66,9 +66,10 @@
     if (mStats) mStats->reset();
 }
 
-void Muxer::dumpStatistics(string inputReference) {
+void Muxer::dumpStatistics(string inputReference, string componentName, string statsFile) {
     string operation = "mux";
-    mStats->dumpStatistics(operation, inputReference, mExtractor->getClipDuration());
+    mStats->dumpStatistics(operation, inputReference, mExtractor->getClipDuration(), componentName,
+                           "", statsFile);
 }
 
 int32_t Muxer::mux(uint8_t *inputBuffer, vector<AMediaCodecBufferInfo> &frameInfos) {
diff --git a/media/tests/benchmark/src/native/muxer/Muxer.h b/media/tests/benchmark/src/native/muxer/Muxer.h
index eee3146..860fdaf 100644
--- a/media/tests/benchmark/src/native/muxer/Muxer.h
+++ b/media/tests/benchmark/src/native/muxer/Muxer.h
@@ -51,7 +51,7 @@
     /* Process the frames and give Muxed output */
     int32_t mux(uint8_t *inputBuffer, vector<AMediaCodecBufferInfo> &frameSizes);
 
-    void dumpStatistics(string inputReference);
+    void dumpStatistics(string inputReference, string codecName = "", string statsFile = "");
 
   private:
     AMediaFormat *mFormat;
diff --git a/media/tests/benchmark/tests/Android.bp b/media/tests/benchmark/tests/Android.bp
index 128d055..f46fa4a 100644
--- a/media/tests/benchmark/tests/Android.bp
+++ b/media/tests/benchmark/tests/Android.bp
@@ -87,8 +87,25 @@
     srcs: ["C2DecoderTest.cpp"],
 
     static_libs: [
-        "libmediabenchmark_extractor",
+        "libmediabenchmark_codec2_extractor",
         "libmediabenchmark_codec2_common",
         "libmediabenchmark_codec2_decoder",
     ],
 }
+
+cc_test {
+    name: "C2EncoderTest",
+    gtest: true,
+    defaults: [
+        "libmediabenchmark_codec2_common-defaults",
+    ],
+
+    srcs: ["C2EncoderTest.cpp"],
+
+    static_libs: [
+        "libmediabenchmark_codec2_extractor",
+        "libmediabenchmark_codec2_decoder",
+        "libmediabenchmark_codec2_common",
+        "libmediabenchmark_codec2_encoder",
+    ],
+}
diff --git a/media/tests/benchmark/tests/C2DecoderTest.cpp b/media/tests/benchmark/tests/C2DecoderTest.cpp
index 3531d8a..ecd9759 100644
--- a/media/tests/benchmark/tests/C2DecoderTest.cpp
+++ b/media/tests/benchmark/tests/C2DecoderTest.cpp
@@ -29,82 +29,64 @@
 
 class C2DecoderTest : public ::testing::TestWithParam<pair<string, string>> {
   public:
-    C2DecoderTest() : mDecoder(nullptr), disableTest(false) { setupC2DecoderTest(); }
+    C2DecoderTest() : mDecoder(nullptr) {}
+
+    ~C2DecoderTest() {
+        if (!mCodecList.empty()) {
+            mCodecList.clear();
+        }
+        if (mDecoder) {
+            delete mDecoder;
+            mDecoder = nullptr;
+        }
+    }
+
+    virtual void SetUp() override { setupC2DecoderTest(); }
 
     void setupC2DecoderTest();
 
     vector<string> mCodecList;
     C2Decoder *mDecoder;
-    bool disableTest;
 };
 
 void C2DecoderTest::setupC2DecoderTest() {
     mDecoder = new C2Decoder();
-    if (!mDecoder) {
-        cout << "[   WARN   ] Test Skipped. C2Decoder creation failed\n";
-        disableTest = true;
-        return;
-    }
+    ASSERT_NE(mDecoder, nullptr) << "C2Decoder creation failed";
+
     int32_t status = mDecoder->setupCodec2();
-    if (status != 0) {
-        cout << "[   WARN   ] Test Skipped. Codec2 setup failed \n";
-        disableTest = true;
-        return;
-    }
+    ASSERT_EQ(status, 0) << "Codec2 setup failed";
+
     mCodecList = mDecoder->getSupportedComponentList(false /* isEncoder*/);
-    if (!mCodecList.size()) {
-        cout << "[   WARN   ] Test Skipped. Codec2 client didn't recognise any component \n";
-        disableTest = true;
-        return;
-    }
+    ASSERT_GT(mCodecList.size(), 0) << "Codec2 client didn't recognise any component";
 }
 
 TEST_P(C2DecoderTest, Codec2Decode) {
-    if (disableTest) return;
-
     ALOGV("Decode the samples given by extractor using codec2");
     string inputFile = gEnv->getRes() + GetParam().first;
     FILE *inputFp = fopen(inputFile.c_str(), "rb");
-    if (!inputFp) {
-        cout << "[   WARN   ] Test Skipped. Unable to open input file" << inputFile
-             << " for reading \n";
-        return;
-    }
+    ASSERT_NE(inputFp, nullptr) << "Unable to open " << inputFile << " file for reading";
 
     Extractor *extractor = new Extractor();
-    if (!extractor) {
-        cout << "[   WARN   ] Test Skipped. Extractor creation failed \n";
-        return;
-    }
+    ASSERT_NE(extractor, nullptr) << "Extractor creation failed";
 
     // Read file properties
-    fseek(inputFp, 0, SEEK_END);
-    size_t fileSize = ftell(inputFp);
-    fseek(inputFp, 0, SEEK_SET);
+    struct stat buf;
+    stat(inputFile.c_str(), &buf);
+    size_t fileSize = buf.st_size;
     int32_t fd = fileno(inputFp);
 
-    if (fileSize > kMaxBufferSize) {
-        cout << "[   WARN   ] Test Skipped. Input file size is greater than the threshold memory "
-                "dedicated to the test \n";
-    }
+    ASSERT_LE(fileSize, kMaxBufferSize)
+            << "Input file size is greater than the threshold memory dedicated to the test";
 
     int32_t trackCount = extractor->initExtractor(fd, fileSize);
-    if (trackCount <= 0) {
-        cout << "[   WARN   ] Test Skipped. initExtractor failed\n";
-        return;
-    }
+    ASSERT_GT(trackCount, 0) << "initExtractor failed";
+
     for (int32_t curTrack = 0; curTrack < trackCount; curTrack++) {
         int32_t status = extractor->setupTrackFormat(curTrack);
-        if (status != 0) {
-            cout << "[   WARN   ] Test Skipped. Track Format invalid \n";
-            return;
-        }
+        ASSERT_EQ(status, 0) << "Track Format invalid";
 
         uint8_t *inputBuffer = (uint8_t *)malloc(fileSize);
-        if (!inputBuffer) {
-            cout << "[   WARN   ] Test Skipped. Insufficient memory \n";
-            return;
-        }
+        ASSERT_NE(inputBuffer, nullptr) << "Insufficient memory";
 
         vector<AMediaCodecBufferInfo> frameInfo;
         AMediaCodecBufferInfo info;
@@ -116,11 +98,8 @@
             void *csdBuffer = extractor->getCSDSample(info, idx);
             if (!csdBuffer || !info.size) break;
             // copy the meta data and buffer to be passed to decoder
-            if (inputBufferOffset + info.size > fileSize) {
-                cout << "[   WARN   ] Test Skipped. Memory allocated not sufficient\n";
-                free(inputBuffer);
-                return;
-            }
+            ASSERT_LE(inputBufferOffset + info.size, fileSize) << "Memory allocated not sufficient";
+
             memcpy(inputBuffer + inputBufferOffset, csdBuffer, info.size);
             frameInfo.push_back(info);
             inputBufferOffset += info.size;
@@ -132,11 +111,8 @@
             status = extractor->getFrameSample(info);
             if (status || !info.size) break;
             // copy the meta data and buffer to be passed to decoder
-            if (inputBufferOffset + info.size > fileSize) {
-                cout << "[   WARN   ] Test Skipped. Memory allocated not sufficient\n";
-                free(inputBuffer);
-                return;
-            }
+            ASSERT_LE(inputBufferOffset + info.size, fileSize) << "Memory allocated not sufficient";
+
             memcpy(inputBuffer + inputBufferOffset, extractor->getFrameBuf(), info.size);
             frameInfo.push_back(info);
             inputBufferOffset += info.size;
@@ -148,21 +124,18 @@
             if (codecName.find(GetParam().second) != string::npos &&
                 codecName.find("secure") == string::npos) {
                 status = mDecoder->createCodec2Component(codecName, format);
-                if (status != 0) {
-                    cout << "[   WARN   ] Test Skipped. Create component failed for " << codecName
-                         << "\n";
-                    continue;
-                }
+                ASSERT_EQ(status, 0) << "Create component failed for " << codecName;
 
                 // Send the inputs to C2 Decoder and wait till all buffers are returned.
-                mDecoder->decodeFrames(inputBuffer, frameInfo);
+                status = mDecoder->decodeFrames(inputBuffer, frameInfo);
+                ASSERT_EQ(status, 0) << "Decoder failed for " << codecName;
+
                 mDecoder->waitOnInputConsumption();
-                if (!mDecoder->mEos) {
-                    cout << "[   WARN   ] Test Failed. Didn't receive EOS \n";
-                }
+                ASSERT_TRUE(mDecoder->mEos) << "Test Failed. Didn't receive EOS \n";
+
                 mDecoder->deInitCodec();
                 int64_t durationUs = extractor->getClipDuration();
-                cout << "codec: " << codecName << endl;
+                ALOGV("codec : %s", codecName.c_str());
                 mDecoder->dumpStatistics(GetParam().first, durationUs);
                 mDecoder->resetDecoder();
             }
@@ -172,6 +145,7 @@
         extractor->deInitExtractor();
         delete extractor;
         delete mDecoder;
+        mDecoder = nullptr;
     }
 }
 
@@ -179,26 +153,24 @@
 // Add wav files
 INSTANTIATE_TEST_SUITE_P(
         AudioDecoderTest, C2DecoderTest,
-        ::testing::Values(
-                make_pair("bbb_44100hz_2ch_128kbps_aac_30sec.mp4", "aac"),
-                make_pair("bbb_44100hz_2ch_128kbps_mp3_30sec.mp3", "mp3"),
-                make_pair("bbb_8000hz_1ch_8kbps_amrnb_30sec.3gp", "amrnb"),
-                make_pair("bbb_16000hz_1ch_9kbps_amrwb_30sec.3gp", "amrnb"),
-                make_pair("bbb_44100hz_2ch_80kbps_vorbis_30sec.mp4", "vorbis"),
-                make_pair("bbb_44100hz_2ch_600kbps_flac_30sec.mp4", "flac"),
-                make_pair("bbb_48000hz_2ch_100kbps_opus_30sec.webm", "opus")));
+        ::testing::Values(make_pair("bbb_44100hz_2ch_128kbps_aac_30sec.mp4", "aac"),
+                          make_pair("bbb_44100hz_2ch_128kbps_mp3_30sec.mp3", "mp3"),
+                          make_pair("bbb_8000hz_1ch_8kbps_amrnb_30sec.3gp", "amrnb"),
+                          make_pair("bbb_16000hz_1ch_9kbps_amrwb_30sec.3gp", "amrnb"),
+                          make_pair("bbb_44100hz_2ch_80kbps_vorbis_30sec.mp4", "vorbis"),
+                          make_pair("bbb_44100hz_2ch_600kbps_flac_30sec.mp4", "flac"),
+                          make_pair("bbb_48000hz_2ch_100kbps_opus_30sec.webm", "opus")));
 
 INSTANTIATE_TEST_SUITE_P(
         VideoDecoderTest, C2DecoderTest,
-        ::testing::Values(
-                make_pair("crowd_1920x1080_25fps_4000kbps_vp9.webm", "vp9"),
-                make_pair("crowd_1920x1080_25fps_4000kbps_vp8.webm", "vp8"),
-                make_pair("crowd_1920x1080_25fps_4000kbps_av1.webm", "av1"),
-                make_pair("crowd_1920x1080_25fps_7300kbps_mpeg2.mp4", "mpeg2"),
-                make_pair("crowd_1920x1080_25fps_6000kbps_mpeg4.mp4", "mpeg4"),
-                make_pair("crowd_352x288_25fps_6000kbps_h263.3gp", "h263"),
-                make_pair("crowd_1920x1080_25fps_6700kbps_h264.ts", "avc"),
-                make_pair("crowd_1920x1080_25fps_4000kbps_h265.mkv", "hevc")));
+        ::testing::Values(make_pair("crowd_1920x1080_25fps_4000kbps_vp9.webm", "vp9"),
+                          make_pair("crowd_1920x1080_25fps_4000kbps_vp8.webm", "vp8"),
+                          make_pair("crowd_1920x1080_25fps_4000kbps_av1.webm", "av1"),
+                          make_pair("crowd_1920x1080_25fps_7300kbps_mpeg2.mp4", "mpeg2"),
+                          make_pair("crowd_1920x1080_25fps_6000kbps_mpeg4.mp4", "mpeg4"),
+                          make_pair("crowd_352x288_25fps_6000kbps_h263.3gp", "h263"),
+                          make_pair("crowd_1920x1080_25fps_6700kbps_h264.ts", "avc"),
+                          make_pair("crowd_1920x1080_25fps_4000kbps_h265.mkv", "hevc")));
 
 int main(int argc, char **argv) {
     gEnv = new BenchmarkTestEnvironment();
diff --git a/media/tests/benchmark/tests/C2EncoderTest.cpp b/media/tests/benchmark/tests/C2EncoderTest.cpp
new file mode 100644
index 0000000..98eb17a
--- /dev/null
+++ b/media/tests/benchmark/tests/C2EncoderTest.cpp
@@ -0,0 +1,187 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "C2EncoderTest"
+
+#include <fstream>
+#include <iostream>
+#include <limits>
+
+#include "BenchmarkTestEnvironment.h"
+#include "C2Encoder.h"
+#include "Decoder.h"
+
+static BenchmarkTestEnvironment *gEnv = nullptr;
+
+class C2EncoderTest : public ::testing::TestWithParam<pair<string, string>> {
+  public:
+    C2EncoderTest() : mEncoder(nullptr) {}
+
+    ~C2EncoderTest() {
+        if (!mCodecList.empty()) {
+            mCodecList.clear();
+        }
+        if (mEncoder) {
+            delete mEncoder;
+            mEncoder = nullptr;
+        }
+    }
+
+    virtual void SetUp() override { setupC2EncoderTest(); }
+
+    void setupC2EncoderTest();
+
+    vector<string> mCodecList;
+    C2Encoder *mEncoder;
+};
+
+void C2EncoderTest::setupC2EncoderTest() {
+    mEncoder = new C2Encoder();
+    ASSERT_NE(mEncoder, nullptr) << "C2Encoder creation failed";
+
+    int32_t status = mEncoder->setupCodec2();
+    ASSERT_EQ(status, 0) << "Codec2 setup failed";
+
+    mCodecList = mEncoder->getSupportedComponentList(true /* isEncoder*/);
+    ASSERT_GT(mCodecList.size(), 0) << "Codec2 client didn't recognise any component";
+}
+
+TEST_P(C2EncoderTest, Codec2Encode) {
+    ALOGV("Encodes the input using codec2 framework");
+    string inputFile = gEnv->getRes() + GetParam().first;
+    FILE *inputFp = fopen(inputFile.c_str(), "rb");
+    ASSERT_NE(inputFp, nullptr) << "Unable to open input file for reading";
+
+    Decoder *decoder = new Decoder();
+    ASSERT_NE(decoder, nullptr) << "Decoder creation failed";
+
+    Extractor *extractor = decoder->getExtractor();
+    ASSERT_NE(extractor, nullptr) << "Extractor creation failed";
+
+    // Read file properties
+    struct stat buf;
+    stat(inputFile.c_str(), &buf);
+    size_t fileSize = buf.st_size;
+    int32_t fd = fileno(inputFp);
+
+    ASSERT_LE(fileSize, kMaxBufferSize)
+            << "Input file size is greater than the threshold memory dedicated to the test";
+
+    int32_t trackCount = extractor->initExtractor(fd, fileSize);
+    ASSERT_GT(trackCount, 0) << "initExtractor failed";
+
+    for (int curTrack = 0; curTrack < trackCount; curTrack++) {
+        int32_t status = extractor->setupTrackFormat(curTrack);
+        ASSERT_EQ(status, 0) << "Track Format invalid";
+
+        uint8_t *inputBuffer = (uint8_t *)malloc(fileSize);
+        ASSERT_NE(inputBuffer, nullptr) << "Insufficient memory";
+
+        vector<AMediaCodecBufferInfo> frameInfo;
+        AMediaCodecBufferInfo info;
+        uint32_t inputBufferOffset = 0;
+
+        // Get frame data
+        while (1) {
+            status = extractor->getFrameSample(info);
+            if (status || !info.size) break;
+            // copy the meta data and buffer to be passed to decoder
+            ASSERT_LE(inputBufferOffset + info.size, fileSize) << "Memory allocated not sufficient";
+
+            memcpy(inputBuffer + inputBufferOffset, extractor->getFrameBuf(), info.size);
+            frameInfo.push_back(info);
+            inputBufferOffset += info.size;
+        }
+
+        string decName = "";
+        string outputFileName = "decode.out";
+        FILE *outFp = fopen(outputFileName.c_str(), "wb");
+        ASSERT_NE(outFp, nullptr) << "Unable to open output file" << outputFileName
+                                  << " for dumping decoder's output";
+
+        decoder->setupDecoder();
+        status = decoder->decode(inputBuffer, frameInfo, decName, false /*asyncMode */, outFp);
+        ASSERT_EQ(status, AMEDIA_OK) << "Decode returned error : " << status;
+
+        // Encode the given input stream for all C2 codecs supported by device
+        AMediaFormat *format = extractor->getFormat();
+        ifstream eleStream;
+        eleStream.open(outputFileName.c_str(), ifstream::binary | ifstream::ate);
+        ASSERT_EQ(eleStream.is_open(), true) << outputFileName.c_str() << " - file not found";
+        size_t eleSize = eleStream.tellg();
+
+        for (string codecName : mCodecList) {
+            if (codecName.find(GetParam().second) != string::npos) {
+                status = mEncoder->createCodec2Component(codecName, format);
+                ASSERT_EQ(status, 0) << "Create component failed for " << codecName;
+
+                // Send the inputs to C2 Encoder and wait till all buffers are returned.
+                eleStream.seekg(0, ifstream::beg);
+                status = mEncoder->encodeFrames(eleStream, eleSize);
+                ASSERT_EQ(status, 0) << "Encoder failed for " << codecName;
+
+                mEncoder->waitOnInputConsumption();
+                ASSERT_TRUE(mEncoder->mEos) << "Test Failed. Didn't receive EOS \n";
+
+                mEncoder->deInitCodec();
+                int64_t durationUs = extractor->getClipDuration();
+                ALOGV("codec : %s", codecName.c_str());
+                mEncoder->dumpStatistics(GetParam().first, durationUs);
+                mEncoder->resetEncoder();
+            }
+        }
+
+        // Destroy the decoder for the given input
+        decoder->deInitCodec();
+        decoder->resetDecoder();
+        free(inputBuffer);
+    }
+    fclose(inputFp);
+    extractor->deInitExtractor();
+    delete decoder;
+    delete mEncoder;
+    mEncoder = nullptr;
+}
+
+INSTANTIATE_TEST_SUITE_P(
+        AudioEncoderTest, C2EncoderTest,
+        ::testing::Values(make_pair("bbb_44100hz_2ch_128kbps_aac_30sec.mp4", "aac"),
+                          make_pair("bbb_8000hz_1ch_8kbps_amrnb_30sec.3gp", "amrnb"),
+                          make_pair("bbb_16000hz_1ch_9kbps_amrwb_30sec.3gp", "amrwb"),
+                          make_pair("bbb_44100hz_2ch_600kbps_flac_30sec.mp4", "flac"),
+                          make_pair("bbb_48000hz_2ch_100kbps_opus_30sec.webm", "opus")));
+
+INSTANTIATE_TEST_SUITE_P(
+        VideoEncoderTest, C2EncoderTest,
+        ::testing::Values(make_pair("crowd_1920x1080_25fps_4000kbps_vp9.webm", "vp9"),
+                          make_pair("crowd_1920x1080_25fps_4000kbps_vp8.webm", "vp8"),
+                          make_pair("crowd_176x144_25fps_6000kbps_mpeg4.mp4", "mpeg4"),
+                          make_pair("crowd_176x144_25fps_6000kbps_h263.3gp", "h263"),
+                          make_pair("crowd_1920x1080_25fps_6700kbps_h264.ts", "avc"),
+                          make_pair("crowd_1920x1080_25fps_4000kbps_h265.mkv", "hevc")));
+
+int main(int argc, char **argv) {
+    gEnv = new BenchmarkTestEnvironment();
+    ::testing::AddGlobalTestEnvironment(gEnv);
+    ::testing::InitGoogleTest(&argc, argv);
+    int status = gEnv->initFromOptions(argc, argv);
+    if (status == 0) {
+        status = RUN_ALL_TESTS();
+        ALOGV("C2 Encoder Test result = %d\n", status);
+    }
+    return status;
+}
diff --git a/media/tests/benchmark/tests/DecoderTest.cpp b/media/tests/benchmark/tests/DecoderTest.cpp
index fa37435..5c6aa5b 100644
--- a/media/tests/benchmark/tests/DecoderTest.cpp
+++ b/media/tests/benchmark/tests/DecoderTest.cpp
@@ -21,8 +21,8 @@
 #include <iostream>
 #include <limits>
 
-#include "Decoder.h"
 #include "BenchmarkTestEnvironment.h"
+#include "Decoder.h"
 
 static BenchmarkTestEnvironment *gEnv = nullptr;
 
@@ -34,41 +34,30 @@
 
     string inputFile = gEnv->getRes() + get<0>(params);
     FILE *inputFp = fopen(inputFile.c_str(), "rb");
-    if (!inputFp) {
-        cout << "[   WARN   ] Test Skipped. Unable to open input file for reading \n";
-        return;
-    }
+    ASSERT_NE(inputFp, nullptr) << "Unable to open " << inputFile << " file for reading";
 
     Decoder *decoder = new Decoder();
+    ASSERT_NE(decoder, nullptr) << "Decoder creation failed";
+
     Extractor *extractor = decoder->getExtractor();
-    if (!extractor) {
-        cout << "[   WARN   ] Test Skipped. Extractor creation failed \n";
-        return;
-    }
+    ASSERT_NE(extractor, nullptr) << "Extractor creation failed";
 
     // Read file properties
-    fseek(inputFp, 0, SEEK_END);
-    size_t fileSize = ftell(inputFp);
-    fseek(inputFp, 0, SEEK_SET);
+    struct stat buf;
+    stat(inputFile.c_str(), &buf);
+    size_t fileSize = buf.st_size;
     int32_t fd = fileno(inputFp);
 
     int32_t trackCount = extractor->initExtractor(fd, fileSize);
-    if (trackCount <= 0) {
-        cout << "[   WARN   ] Test Skipped. initExtractor failed\n";
-        return;
-    }
+    ASSERT_GT(trackCount, 0) << "initExtractor failed";
+
     for (int curTrack = 0; curTrack < trackCount; curTrack++) {
         int32_t status = extractor->setupTrackFormat(curTrack);
-        if (status != 0) {
-            cout << "[   WARN   ] Test Skipped. Track Format invalid \n";
-            return;
-        }
+        ASSERT_EQ(status, 0) << "Track Format invalid";
 
         uint8_t *inputBuffer = (uint8_t *)malloc(kMaxBufferSize);
-        if (!inputBuffer) {
-            cout << "[   WARN   ] Test Skipped. Insufficient memory \n";
-            return;
-        }
+        ASSERT_NE(inputBuffer, nullptr) << "Insufficient memory";
+
         vector<AMediaCodecBufferInfo> frameInfo;
         AMediaCodecBufferInfo info;
         uint32_t inputBufferOffset = 0;
@@ -78,11 +67,9 @@
             status = extractor->getFrameSample(info);
             if (status || !info.size) break;
             // copy the meta data and buffer to be passed to decoder
-            if (inputBufferOffset + info.size > kMaxBufferSize) {
-                cout << "[   WARN   ] Test Skipped. Memory allocated not sufficient\n";
-                free(inputBuffer);
-                return;
-            }
+            ASSERT_LE(inputBufferOffset + info.size, kMaxBufferSize)
+                    << "Memory allocated not sufficient";
+
             memcpy(inputBuffer + inputBufferOffset, extractor->getFrameBuf(), info.size);
             frameInfo.push_back(info);
             inputBufferOffset += info.size;
@@ -92,13 +79,10 @@
         bool asyncMode = get<2>(params);
         decoder->setupDecoder();
         status = decoder->decode(inputBuffer, frameInfo, codecName, asyncMode);
-        if (status != AMEDIA_OK) {
-            cout << "[   WARN   ] Test Failed. Decode returned error " << status << endl;
-            free(inputBuffer);
-            return;
-        }
+        ASSERT_EQ(status, AMEDIA_OK) << "Decoder failed for " << codecName;
+
         decoder->deInitCodec();
-        cout << "codec : " << codecName << endl;
+        ALOGV("codec : %s", codecName.c_str());
         string inputReference = get<0>(params);
         decoder->dumpStatistics(inputReference);
         free(inputBuffer);
diff --git a/media/tests/benchmark/tests/EncoderTest.cpp b/media/tests/benchmark/tests/EncoderTest.cpp
index c3963f8..dc2a2dd 100644
--- a/media/tests/benchmark/tests/EncoderTest.cpp
+++ b/media/tests/benchmark/tests/EncoderTest.cpp
@@ -20,8 +20,8 @@
 #include <fstream>
 
 #include "BenchmarkTestEnvironment.h"
-#include "Encoder.h"
 #include "Decoder.h"
+#include "Encoder.h"
 
 static BenchmarkTestEnvironment *gEnv = nullptr;
 
@@ -33,42 +33,33 @@
 
     string inputFile = gEnv->getRes() + get<0>(params);
     FILE *inputFp = fopen(inputFile.c_str(), "rb");
-    if (!inputFp) {
-        cout << "[   WARN   ] Test Skipped. Unable to open input file for reading \n";
-        return;
-    }
+    ASSERT_NE(inputFp, nullptr) << "Unable to open " << inputFile << " file for reading";
 
     Decoder *decoder = new Decoder();
+    ASSERT_NE(decoder, nullptr) << "Decoder creation failed";
+
     Extractor *extractor = decoder->getExtractor();
-    if (!extractor) {
-        cout << "[   WARN   ] Test Skipped. Extractor creation failed \n";
-        return;
-    }
+    ASSERT_NE(extractor, nullptr) << "Extractor creation failed";
+
     // Read file properties
-    fseek(inputFp, 0, SEEK_END);
-    size_t fileSize = ftell(inputFp);
-    fseek(inputFp, 0, SEEK_SET);
+    struct stat buf;
+    stat(inputFile.c_str(), &buf);
+    size_t fileSize = buf.st_size;
     int32_t fd = fileno(inputFp);
 
     int32_t trackCount = extractor->initExtractor(fd, fileSize);
-    if (trackCount <= 0) {
-        cout << "[   WARN   ] Test Skipped. initExtractor failed\n";
-        return;
-    }
+    ASSERT_GT(trackCount, 0) << "initExtractor failed";
 
     Encoder *encoder = new Encoder();
+    ASSERT_NE(encoder, nullptr) << "Decoder creation failed";
+
     for (int curTrack = 0; curTrack < trackCount; curTrack++) {
         int32_t status = extractor->setupTrackFormat(curTrack);
-        if (status != 0) {
-            cout << "[   WARN   ] Test Skipped. Track Format invalid \n";
-            return;
-        }
+        ASSERT_EQ(status, 0) << "Track Format invalid";
 
         uint8_t *inputBuffer = (uint8_t *)malloc(kMaxBufferSize);
-        if (!inputBuffer) {
-            cout << "[   WARN   ] Test Skipped. Insufficient memory \n";
-            return;
-        }
+        ASSERT_NE(inputBuffer, nullptr) << "Insufficient memory";
+
         vector<AMediaCodecBufferInfo> frameInfo;
         AMediaCodecBufferInfo info;
         uint32_t inputBufferOffset = 0;
@@ -78,11 +69,9 @@
             status = extractor->getFrameSample(info);
             if (status || !info.size) break;
             // copy the meta data and buffer to be passed to decoder
-            if (inputBufferOffset + info.size > kMaxBufferSize) {
-                cout << "[   WARN   ] Test Skipped. Memory allocated not sufficient\n";
-                free(inputBuffer);
-                return;
-            }
+            ASSERT_LE(inputBufferOffset + info.size, kMaxBufferSize)
+                    << "Memory allocated not sufficient";
+
             memcpy(inputBuffer + inputBufferOffset, extractor->getFrameBuf(), info.size);
             frameInfo.push_back(info);
             inputBufferOffset += info.size;
@@ -91,16 +80,12 @@
         string decName = "";
         string outputFileName = "decode.out";
         FILE *outFp = fopen(outputFileName.c_str(), "wb");
-        if (outFp == nullptr) {
-            ALOGE("Unable to open output file for writing");
-            return;
-        }
+        ASSERT_NE(outFp, nullptr) << "Unable to open output file" << outputFileName
+                                  << " for dumping decoder's output";
+
         decoder->setupDecoder();
         status = decoder->decode(inputBuffer, frameInfo, decName, false /*asyncMode */, outFp);
-        if (status != AMEDIA_OK) {
-            cout << "[   WARN   ] Test Skipped. Decode returned error \n";
-            return;
-        }
+        ASSERT_EQ(status, AMEDIA_OK) << "Decode returned error : " << status;
 
         ifstream eleStream;
         eleStream.open(outputFileName.c_str(), ifstream::binary | ifstream::ate);
@@ -111,15 +96,13 @@
         AMediaFormat *format = extractor->getFormat();
         const char *mime = nullptr;
         AMediaFormat_getString(format, AMEDIAFORMAT_KEY_MIME, &mime);
-        if (!mime) {
-            ALOGE("Error in AMediaFormat_getString");
-            return;
-        }
+        ASSERT_NE(mime, nullptr) << "Invalid mime type";
+
         // Get encoder params
         encParameter encParams;
         if (!strncmp(mime, "video/", 6)) {
-            AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_WIDTH, &encParams.width);
-            AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_HEIGHT, &encParams.height);
+            ASSERT_TRUE(AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_WIDTH, &encParams.width));
+            ASSERT_TRUE(AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_HEIGHT, &encParams.height));
             AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_FRAME_RATE, &encParams.frameRate);
             AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_BIT_RATE, &encParams.bitrate);
             if (encParams.bitrate <= 0 || encParams.frameRate <= 0) {
@@ -133,8 +116,10 @@
             AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_PROFILE, &encParams.profile);
             AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_LEVEL, &encParams.level);
         } else {
-            AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_SAMPLE_RATE, &encParams.sampleRate);
-            AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_CHANNEL_COUNT, &encParams.numChannels);
+            ASSERT_TRUE(AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_SAMPLE_RATE,
+                                              &encParams.sampleRate));
+            ASSERT_TRUE(AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_CHANNEL_COUNT,
+                                              &encParams.numChannels));
             encParams.bitrate =
                     encParams.sampleRate * encParams.numChannels * 16 /* bitsPerSample */;
         }
@@ -143,13 +128,10 @@
         string codecName = get<1>(params);
         bool asyncMode = get<2>(params);
         status = encoder->encode(codecName, eleStream, eleSize, asyncMode, encParams, (char *)mime);
-        if (status != AMEDIA_OK) {
-            cout << "[   WARN   ] Test Failed. Encode returned error " << status << endl;
-            free(inputBuffer);
-            return;
-        }
+        ASSERT_EQ(status, 0) << "Encoder failed for " << codecName;
+
         encoder->deInitCodec();
-        cout << "codec : " << codecName << endl;
+        ALOGV("codec : %s", codecName.c_str());
         string inputReference = get<0>(params);
         encoder->dumpStatistics(inputReference, extractor->getClipDuration());
         eleStream.close();
diff --git a/media/tests/benchmark/tests/ExtractorTest.cpp b/media/tests/benchmark/tests/ExtractorTest.cpp
index dd0d711..c2d72ff 100644
--- a/media/tests/benchmark/tests/ExtractorTest.cpp
+++ b/media/tests/benchmark/tests/ExtractorTest.cpp
@@ -19,8 +19,8 @@
 
 #include <gtest/gtest.h>
 
-#include "Extractor.h"
 #include "BenchmarkTestEnvironment.h"
+#include "Extractor.h"
 
 static BenchmarkTestEnvironment *gEnv = nullptr;
 
@@ -28,33 +28,24 @@
 
 TEST_P(ExtractorTest, Extract) {
     Extractor *extractObj = new Extractor();
+    ASSERT_NE(extractObj, nullptr) << "Extractor creation failed";
 
     string inputFile = gEnv->getRes() + GetParam().first;
     FILE *inputFp = fopen(inputFile.c_str(), "rb");
-    if (!inputFp) {
-        cout << "[   WARN   ] Test Skipped. Unable to open input file for reading \n";
-        return;
-    }
+    ASSERT_NE(inputFp, nullptr) << "Unable to open " << inputFile << " file for reading";
 
     // Read file properties
-    size_t fileSize = 0;
-    fseek(inputFp, 0, SEEK_END);
-    fileSize = ftell(inputFp);
-    fseek(inputFp, 0, SEEK_SET);
+    struct stat buf;
+    stat(inputFile.c_str(), &buf);
+    size_t fileSize = buf.st_size;
     int32_t fd = fileno(inputFp);
 
     int32_t trackCount = extractObj->initExtractor(fd, fileSize);
-    if (trackCount <= 0) {
-        cout << "[   WARN   ] Test Skipped. initExtractor failed\n";
-        return;
-    }
+    ASSERT_GT(trackCount, 0) << "initExtractor failed";
 
     int32_t trackID = GetParam().second;
     int32_t status = extractObj->extract(trackID);
-    if (status != AMEDIA_OK) {
-        cout << "[   WARN   ] Test Skipped. Extraction failed \n";
-        return;
-    }
+    ASSERT_EQ(status, AMEDIA_OK) << "Extraction failed \n";
 
     extractObj->deInitExtractor();
 
@@ -79,7 +70,8 @@
                                            make_pair("bbb_8000hz_1ch_8kbps_amrnb_5mins.3gp", 0),
                                            make_pair("bbb_16000hz_1ch_9kbps_amrwb_5mins.3gp", 0),
                                            make_pair("bbb_44100hz_2ch_80kbps_vorbis_5mins.mp4", 0),
-                                           make_pair("bbb_48000hz_2ch_100kbps_opus_5mins.webm", 0)));
+                                           make_pair("bbb_48000hz_2ch_100kbps_opus_5mins.webm",
+                                                     0)));
 
 int main(int argc, char **argv) {
     gEnv = new BenchmarkTestEnvironment();
diff --git a/media/tests/benchmark/tests/MuxerTest.cpp b/media/tests/benchmark/tests/MuxerTest.cpp
index e814f90..7b01732 100644
--- a/media/tests/benchmark/tests/MuxerTest.cpp
+++ b/media/tests/benchmark/tests/MuxerTest.cpp
@@ -21,8 +21,8 @@
 #include <fstream>
 #include <iostream>
 
-#include "Muxer.h"
 #include "BenchmarkTestEnvironment.h"
+#include "Muxer.h"
 
 #define OUTPUT_FILE_NAME "/data/local/tmp/mux.out"
 
@@ -53,49 +53,34 @@
     ALOGV("Mux the samples given by extractor");
     string inputFile = gEnv->getRes() + GetParam().first;
     FILE *inputFp = fopen(inputFile.c_str(), "rb");
-    if (!inputFp) {
-        cout << "[   WARN   ] Test Skipped. Unable to open input file for reading \n";
-        return;
-    }
+    ASSERT_NE(inputFp, nullptr) << "Unable to open " << inputFile << " file for reading";
+
     string fmt = GetParam().second;
     MUXER_OUTPUT_T outputFormat = getMuxerOutFormat(fmt);
-    if (outputFormat == MUXER_OUTPUT_FORMAT_INVALID) {
-        ALOGE("output format is MUXER_OUTPUT_FORMAT_INVALID");
-        return;
-    }
+    ASSERT_NE(outputFormat, MUXER_OUTPUT_FORMAT_INVALID) << "Invalid muxer output format";
 
     Muxer *muxerObj = new Muxer();
+    ASSERT_NE(muxerObj, nullptr) << "Muxer creation failed";
+
     Extractor *extractor = muxerObj->getExtractor();
-    if (!extractor) {
-        cout << "[   WARN   ] Test Skipped. Extractor creation failed \n";
-        return;
-    }
+    ASSERT_NE(extractor, nullptr) << "Extractor creation failed";
 
     // Read file properties
-    size_t fileSize = 0;
-    fseek(inputFp, 0, SEEK_END);
-    fileSize = ftell(inputFp);
-    fseek(inputFp, 0, SEEK_SET);
+    struct stat buf;
+    stat(inputFile.c_str(), &buf);
+    size_t fileSize = buf.st_size;
     int32_t fd = fileno(inputFp);
 
     int32_t trackCount = extractor->initExtractor(fd, fileSize);
-    if (trackCount <= 0) {
-        cout << "[   WARN   ] Test Skipped. initExtractor failed\n";
-        return;
-    }
+    ASSERT_GT(trackCount, 0) << "initExtractor failed";
 
     for (int curTrack = 0; curTrack < trackCount; curTrack++) {
         int32_t status = extractor->setupTrackFormat(curTrack);
-        if (status != 0) {
-            cout << "[   WARN   ] Test Skipped. Track Format invalid \n";
-            return;
-        }
+        ASSERT_EQ(status, 0) << "Track Format invalid";
 
         uint8_t *inputBuffer = (uint8_t *)malloc(kMaxBufferSize);
-        if (!inputBuffer) {
-            std::cout << "[   WARN   ] Test Skipped. Insufficient memory \n";
-            return;
-        }
+        ASSERT_NE(inputBuffer, nullptr) << "Insufficient memory";
+
         // AMediaCodecBufferInfo : <size of frame> <flags> <presentationTimeUs> <offset>
         vector<AMediaCodecBufferInfo> frameInfos;
         AMediaCodecBufferInfo info;
@@ -106,11 +91,9 @@
             status = extractor->getFrameSample(info);
             if (status || !info.size) break;
             // copy the meta data and buffer to be passed to muxer
-            if (inputBufferOffset + info.size > kMaxBufferSize) {
-                cout << "[   WARN   ] Test Skipped. Memory allocated not sufficient\n";
-                free(inputBuffer);
-                return;
-            }
+            ASSERT_LE(inputBufferOffset + info.size, kMaxBufferSize)
+                    << "Memory allocated not sufficient";
+
             memcpy(inputBuffer + inputBufferOffset, extractor->getFrameBuf(), info.size);
             info.offset = inputBufferOffset;
             frameInfos.push_back(info);
@@ -119,22 +102,16 @@
 
         string outputFileName = OUTPUT_FILE_NAME;
         FILE *outputFp = fopen(outputFileName.c_str(), "w+b");
-        if (!outputFp) {
-            cout << "[   WARN   ] Test Skipped. Unable to open output file for writing \n";
-            return;
-        }
+        ASSERT_NE(outputFp, nullptr)
+                << "Unable to open output file" << outputFileName << " for writing";
+
         int32_t fd = fileno(outputFp);
         status = muxerObj->initMuxer(fd, outputFormat);
-        if (status != 0) {
-            cout << "[   WARN   ] Test Skipped. initMuxer failed\n";
-            return;
-        }
+        ASSERT_EQ(status, 0) << "initMuxer failed";
 
         status = muxerObj->mux(inputBuffer, frameInfos);
-        if (status != 0) {
-            cout << "[   WARN   ] Test Skipped. Mux failed \n";
-            return;
-        }
+        ASSERT_EQ(status, 0) << "Mux failed";
+
         muxerObj->deInitMuxer();
         muxerObj->dumpStatistics(GetParam().first + "." + fmt.c_str());
         free(inputBuffer);
diff --git a/media/utils/ProcessInfo.cpp b/media/utils/ProcessInfo.cpp
index 27f1a79..113e4a7 100644
--- a/media/utils/ProcessInfo.cpp
+++ b/media/utils/ProcessInfo.cpp
@@ -23,6 +23,7 @@
 #include <binder/IPCThreadState.h>
 #include <binder/IProcessInfoService.h>
 #include <binder/IServiceManager.h>
+#include <private/android_filesystem_config.h>
 
 namespace android {
 
@@ -55,8 +56,9 @@
 
 bool ProcessInfo::isValidPid(int pid) {
     int callingPid = IPCThreadState::self()->getCallingPid();
+    int callingUid = IPCThreadState::self()->getCallingUid();
     // Trust it if this is called from the same process otherwise pid has to match the calling pid.
-    return (callingPid == getpid()) || (callingPid == pid);
+    return (callingPid == getpid()) || (callingPid == pid) || (callingUid == AID_MEDIA);
 }
 
 ProcessInfo::~ProcessInfo() {}
diff --git a/services/audioflinger/Effects.cpp b/services/audioflinger/Effects.cpp
index ee7a6d6..bbd0d2c 100644
--- a/services/audioflinger/Effects.cpp
+++ b/services/audioflinger/Effects.cpp
@@ -622,7 +622,7 @@
     mConfig.outputCfg.format = EFFECT_BUFFER_FORMAT;
 
     // Don't use sample rate for thread if effect isn't offloadable.
-    if ((thread->type() == ThreadBase::OFFLOAD) && !isOffloaded()) {
+    if (isOffloadedOrDirect() && !isOffloaded()) {
         mConfig.inputCfg.samplingRate = DEFAULT_OUTPUT_SAMPLE_RATE;
         ALOGV("Overriding effect input as 48kHz");
     } else {
@@ -1856,12 +1856,13 @@
             }
 
             // copy to local memory in case of client corruption b/32220769
-            param = (effect_param_t *)realloc(param, size);
-            if (param == NULL) {
+            auto *newParam = (effect_param_t *)realloc(param, size);
+            if (newParam == NULL) {
                 ALOGW("command(): out of memory");
                 status = NO_MEMORY;
                 break;
             }
+            param = newParam;
             memcpy(param, p, size);
 
             int reply = 0;
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index 23c0a12..b427794 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -1355,6 +1355,14 @@
     // For encoded streams force direct flag to prevent downstream mixing.
     sinkConfig->flags.output = static_cast<audio_output_flags_t>(
             sinkConfig->flags.output | AUDIO_OUTPUT_FLAG_DIRECT);
+    if (audio_is_iec61937_compatible(sinkConfig->format)) {
+        // For formats compatible with IEC61937 encapsulation, assume that
+        // the record thread input from MSD is IEC61937 framed (for proportional buffer sizing).
+        // Add the AUDIO_OUTPUT_FLAG_IEC958_NONAUDIO flag so downstream HAL can distinguish between
+        // raw and IEC61937 framed streams.
+        sinkConfig->flags.output = static_cast<audio_output_flags_t>(
+                sinkConfig->flags.output | AUDIO_OUTPUT_FLAG_IEC958_NONAUDIO);
+    }
     sourceConfig->sample_rate = bestSinkConfig.sample_rate;
     // Specify exact channel mask to prevent guessing by bit count in PatchPanel.
     sourceConfig->channel_mask = audio_channel_mask_out_to_in(bestSinkConfig.channel_mask);
@@ -5283,7 +5291,8 @@
         auto attr = mEngine->getAllAttributesForProductStrategy(productStrategy).front();
 
         if ((hasVoiceStream(streams) &&
-             (isInCall() || mOutputs.isStrategyActiveOnSameModule(productStrategy, outputDesc))) ||
+             (isInCall() || mOutputs.isStrategyActiveOnSameModule(productStrategy, outputDesc)) &&
+             !isStreamActive(AUDIO_STREAM_ENFORCED_AUDIBLE, 0)) ||
              ((hasStream(streams, AUDIO_STREAM_ALARM) || hasStream(streams, AUDIO_STREAM_ENFORCED_AUDIBLE)) &&
                 mOutputs.isStrategyActiveOnSameModule(productStrategy, outputDesc)) ||
                 outputDesc->isStrategyActive(productStrategy)) {
diff --git a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
index 47a103b..c1190be 100644
--- a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
+++ b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
@@ -351,12 +351,17 @@
         return NO_INIT;
     }
 
+    audio_source_t inputSource = attr->source;
+    if (inputSource == AUDIO_SOURCE_DEFAULT) {
+        inputSource = AUDIO_SOURCE_MIC;
+    }
+
     // already checked by client, but double-check in case the client wrapper is bypassed
-    if ((attr->source < AUDIO_SOURCE_DEFAULT)
-            || (attr->source >= AUDIO_SOURCE_CNT
-                && attr->source != AUDIO_SOURCE_HOTWORD
-                && attr->source != AUDIO_SOURCE_FM_TUNER
-                && attr->source != AUDIO_SOURCE_ECHO_REFERENCE)) {
+    if ((inputSource < AUDIO_SOURCE_DEFAULT)
+            || (inputSource >= AUDIO_SOURCE_CNT
+                && inputSource != AUDIO_SOURCE_HOTWORD
+                && inputSource != AUDIO_SOURCE_FM_TUNER
+                && inputSource != AUDIO_SOURCE_ECHO_REFERENCE)) {
         return BAD_VALUE;
     }
 
@@ -385,16 +390,16 @@
     }
 
     bool canCaptureOutput = captureAudioOutputAllowed(pid, uid);
-    if ((attr->source == AUDIO_SOURCE_VOICE_UPLINK ||
-        attr->source == AUDIO_SOURCE_VOICE_DOWNLINK ||
-        attr->source == AUDIO_SOURCE_VOICE_CALL ||
-        attr->source == AUDIO_SOURCE_ECHO_REFERENCE) &&
+    if ((inputSource == AUDIO_SOURCE_VOICE_UPLINK ||
+        inputSource == AUDIO_SOURCE_VOICE_DOWNLINK ||
+        inputSource == AUDIO_SOURCE_VOICE_CALL ||
+        inputSource == AUDIO_SOURCE_ECHO_REFERENCE) &&
         !canCaptureOutput) {
         return PERMISSION_DENIED;
     }
 
     bool canCaptureHotword = captureHotwordAllowed(opPackageName, pid, uid);
-    if ((attr->source == AUDIO_SOURCE_HOTWORD) && !canCaptureHotword) {
+    if ((inputSource == AUDIO_SOURCE_HOTWORD) && !canCaptureHotword) {
         return BAD_VALUE;
     }
 
@@ -459,7 +464,7 @@
 
     if (audioPolicyEffects != 0) {
         // create audio pre processors according to input source
-        status_t status = audioPolicyEffects->addInputEffects(*input, attr->source, session);
+        status_t status = audioPolicyEffects->addInputEffects(*input, inputSource, session);
         if (status != NO_ERROR && status != ALREADY_EXISTS) {
             ALOGW("Failed to add effects on input %d", *input);
         }
diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp
index 4227a3b..bda35f3 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Device.cpp
@@ -2165,7 +2165,9 @@
 }
 
 void Camera3Device::pauseStateNotify(bool enable) {
-    Mutex::Autolock il(mInterfaceLock);
+    // We must not hold mInterfaceLock here since this function is called from
+    // RequestThread::threadLoop and holding mInterfaceLock could lead to
+    // deadlocks (http://b/143513518)
     Mutex::Autolock l(mLock);
 
     mPauseStateNotify = enable;
@@ -2742,7 +2744,9 @@
     ATRACE_CALL();
     bool ret = false;
 
-    Mutex::Autolock il(mInterfaceLock);
+    // We must not hold mInterfaceLock here since this function is called from
+    // RequestThread::threadLoop and holding mInterfaceLock could lead to
+    // deadlocks (http://b/143513518)
     nsecs_t maxExpectedDuration = getExpectedInFlightDuration();
 
     Mutex::Autolock l(mLock);
@@ -5371,6 +5375,9 @@
 bool Camera3Device::RequestThread::threadLoop() {
     ATRACE_CALL();
     status_t res;
+    // Any function called from threadLoop() must not hold mInterfaceLock since
+    // it could lead to deadlocks (disconnect() -> hold mInterfaceMutex -> wait for request thread
+    // to finish -> request thread waits on mInterfaceMutex) http://b/143513518
 
     // Handle paused state.
     if (waitIfPaused()) {
diff --git a/services/mediadrm/Android.mk b/services/mediadrm/Android.mk
index 9f03964..72d42ae 100644
--- a/services/mediadrm/Android.mk
+++ b/services/mediadrm/Android.mk
@@ -27,6 +27,7 @@
 LOCAL_SHARED_LIBRARIES:= \
     libbinder \
     liblog \
+    libmedia \
     libmediadrm \
     libutils \
     libhidlbase \
diff --git a/services/mediaresourcemanager/Android.bp b/services/mediaresourcemanager/Android.bp
index f3339a0..d468406 100644
--- a/services/mediaresourcemanager/Android.bp
+++ b/services/mediaresourcemanager/Android.bp
@@ -23,4 +23,6 @@
         "-Wall",
     ],
 
+    export_include_dirs: ["."],
+
 }
diff --git a/services/mediaresourcemanager/ResourceManagerService.cpp b/services/mediaresourcemanager/ResourceManagerService.cpp
index bdcd5e4..45eea0f 100644
--- a/services/mediaresourcemanager/ResourceManagerService.cpp
+++ b/services/mediaresourcemanager/ResourceManagerService.cpp
@@ -290,6 +290,18 @@
     }
 }
 
+void ResourceManagerService::mergeResources(
+        MediaResource& r1, const MediaResource& r2) {
+    if (r1.mType == MediaResource::kDrmSession) {
+        // This means we are using a session. Each session's mValue is initialized to UINT64_MAX.
+        // The oftener a session is used the lower it's mValue. During reclaim the session with
+        // the highest mValue/lowest usage would be closed.
+        r1.mValue -= (r1.mValue == 0 ? 0 : 1);
+    } else {
+        r1.mValue += r2.mValue;
+    }
+}
+
 void ResourceManagerService::addResource(
         int pid,
         int uid,
@@ -309,15 +321,16 @@
     ResourceInfo& info = getResourceInfoForEdit(uid, clientId, client, infos);
 
     for (size_t i = 0; i < resources.size(); ++i) {
-        const auto resType = std::make_pair(resources[i].mType, resources[i].mSubType);
+        const auto &res = resources[i];
+        const auto resType = std::tuple(res.mType, res.mSubType, res.mId);
         if (info.resources.find(resType) == info.resources.end()) {
-            onFirstAdded(resources[i], info);
-            info.resources[resType] = resources[i];
+            onFirstAdded(res, info);
+            info.resources[resType] = res;
         } else {
-            info.resources[resType].mValue += resources[i].mValue;
+            mergeResources(info.resources[resType], res);
         }
     }
-    if (info.deathNotifier == nullptr) {
+    if (info.deathNotifier == nullptr && client != nullptr) {
         info.deathNotifier = new DeathNotifier(this, pid, clientId);
         IInterface::asBinder(client)->linkToDeath(info.deathNotifier);
     }
@@ -351,14 +364,17 @@
     ResourceInfo &info = infos.editValueAt(index);
 
     for (size_t i = 0; i < resources.size(); ++i) {
-        const auto resType = std::make_pair(resources[i].mType, resources[i].mSubType);
+        const auto &res = resources[i];
+        const auto resType = std::tuple(res.mType, res.mSubType, res.mId);
         // ignore if we don't have it
         if (info.resources.find(resType) != info.resources.end()) {
             MediaResource &resource = info.resources[resType];
-            if (resource.mValue > resources[i].mValue) {
-                resource.mValue -= resources[i].mValue;
+            if (resource.mValue > res.mValue) {
+                resource.mValue -= res.mValue;
             } else {
-                onLastRemoved(resources[i], info);
+                // drm sessions always take this branch because res.mValue is set
+                // to UINT64_MAX
+                onLastRemoved(res, info);
                 info.resources.erase(resType);
             }
         }
@@ -430,6 +446,7 @@
         const MediaResource *secureCodec = NULL;
         const MediaResource *nonSecureCodec = NULL;
         const MediaResource *graphicMemory = NULL;
+        const MediaResource *drmSession = NULL;
         for (size_t i = 0; i < resources.size(); ++i) {
             MediaResource::Type type = resources[i].mType;
             if (resources[i].mType == MediaResource::kSecureCodec) {
@@ -438,6 +455,8 @@
                 nonSecureCodec = &resources[i];
             } else if (type == MediaResource::kGraphicMemory) {
                 graphicMemory = &resources[i];
+            } else if (type == MediaResource::kDrmSession) {
+                drmSession = &resources[i];
             }
         }
 
@@ -461,6 +480,12 @@
                 }
             }
         }
+        if (drmSession != NULL) {
+            getClientForResource_l(callingPid, drmSession, &clients);
+            if (clients.size() == 0) {
+                return false;
+            }
+        }
 
         if (clients.size() == 0) {
             // if no secure/non-secure codec conflict, run second pass to handle other resources.
diff --git a/services/mediaresourcemanager/ResourceManagerService.h b/services/mediaresourcemanager/ResourceManagerService.h
index f086dc3..44d0c28 100644
--- a/services/mediaresourcemanager/ResourceManagerService.h
+++ b/services/mediaresourcemanager/ResourceManagerService.h
@@ -33,7 +33,7 @@
 class ServiceLog;
 struct ProcessInfoInterface;
 
-typedef std::map<std::pair<MediaResource::Type, MediaResource::SubType>, MediaResource> ResourceList;
+typedef std::map<std::tuple<MediaResource::Type, MediaResource::SubType, std::vector<uint8_t>>, MediaResource> ResourceList;
 struct ResourceInfo {
     int64_t clientId;
     uid_t uid;
@@ -126,6 +126,9 @@
     void onFirstAdded(const MediaResource& res, const ResourceInfo& clientInfo);
     void onLastRemoved(const MediaResource& res, const ResourceInfo& clientInfo);
 
+    // Merge r2 into r1
+    void mergeResources(MediaResource& r1, const MediaResource& r2);
+
     mutable Mutex mLock;
     sp<ProcessInfoInterface> mProcessInfo;
     sp<SystemCallbackInterface> mSystemCB;
diff --git a/services/mediaresourcemanager/test/ResourceManagerService_test.cpp b/services/mediaresourcemanager/test/ResourceManagerService_test.cpp
index ae97ec8..9e14151 100644
--- a/services/mediaresourcemanager/test/ResourceManagerService_test.cpp
+++ b/services/mediaresourcemanager/test/ResourceManagerService_test.cpp
@@ -173,8 +173,9 @@
         // convert resource1 to ResourceList
         ResourceList r1;
         for (size_t i = 0; i < resources1.size(); ++i) {
-            const auto resType = std::make_pair(resources1[i].mType, resources1[i].mSubType);
-            r1[resType] = resources1[i];
+            const auto &res = resources1[i];
+            const auto resType = std::tuple(res.mType, res.mSubType, res.mId);
+            r1[resType] = res;
         }
         return r1 == resources2;
     }