Merge "Move C2HandleIon and C2AllocationIon to internal dir"
diff --git a/media/codec2/TEST_MAPPING b/media/codec2/TEST_MAPPING
index 49614cd..8afa1a8 100644
--- a/media/codec2/TEST_MAPPING
+++ b/media/codec2/TEST_MAPPING
@@ -1,7 +1,6 @@
 {
-  "postsubmit": [
+  "presubmit": [
     {
-      // TODO: move to presubmit once we verify the tests are not flaky
       "name": "CtsMediaTestCases",
       "options": [
         {
@@ -13,6 +12,9 @@
         // TODO: b/149314419
         {
           "exclude-filter": "android.media.cts.AudioPlaybackCaptureTest"
+        },
+        {
+          "exclude-filter": "android.media.cts.AudioRecordTest"
         }
       ]
     }
diff --git a/media/codec2/sfplugin/CCodec.cpp b/media/codec2/sfplugin/CCodec.cpp
index 39263f9..0d093da 100644
--- a/media/codec2/sfplugin/CCodec.cpp
+++ b/media/codec2/sfplugin/CCodec.cpp
@@ -668,7 +668,7 @@
     // initialize config here in case setParameters is called prior to configure
     Mutexed<std::unique_ptr<Config>>::Locked configLocked(mConfig);
     const std::unique_ptr<Config> &config = *configLocked;
-    status_t err = config->initialize(mClient, comp);
+    status_t err = config->initialize(mClient->getParamReflector(), comp);
     if (err != OK) {
         ALOGW("Failed to initialize configuration support");
         // TODO: report error once we complete implementation.
@@ -884,6 +884,13 @@
             }
         }
 
+        int32_t subscribeToAllVendorParams;
+        if (msg->findInt32("x-*", &subscribeToAllVendorParams) && subscribeToAllVendorParams) {
+            if (config->subscribeToAllVendorParams(comp, C2_MAY_BLOCK) != OK) {
+                ALOGD("[%s] Failed to subscribe to all vendor params", comp->getName().c_str());
+            }
+        }
+
         std::vector<std::unique_ptr<C2Param>> configUpdate;
         // NOTE: We used to ignore "video-bitrate" at configure; replicate
         //       the behavior here.
@@ -1192,7 +1199,7 @@
 
     // we are now using surface - apply default color aspects to input format - as well as
     // get dataspace
-    bool inputFormatChanged = config->updateFormats(config->IS_INPUT);
+    bool inputFormatChanged = config->updateFormats(Config::IS_INPUT);
     ALOGD("input format %s to %s",
             inputFormatChanged ? "changed" : "unchanged",
             config->mInputFormat->debugString().c_str());
@@ -1207,7 +1214,7 @@
     if (err != OK) {
         // undo input format update
         config->mUsingSurface = false;
-        (void)config->updateFormats(config->IS_INPUT);
+        (void)config->updateFormats(Config::IS_INPUT);
         return err;
     }
     config->mInputSurface = surface;
@@ -1323,6 +1330,8 @@
         mCallback->onError(err2, ACTION_CODE_FATAL);
         return;
     }
+    // We're not starting after flush.
+    (void)mSentConfigAfterResume.test_and_set();
     err2 = mChannel->start(inputFormat, outputFormat, buffersBoundToCodec);
     if (err2 != OK) {
         mCallback->onError(err2, ACTION_CODE_FATAL);
@@ -1555,18 +1564,27 @@
 }
 
 void CCodec::signalResume() {
-    auto setResuming = [this] {
+    std::shared_ptr<Codec2Client::Component> comp;
+    auto setResuming = [this, &comp] {
         Mutexed<State>::Locked state(mState);
         if (state->get() != FLUSHED) {
             return UNKNOWN_ERROR;
         }
         state->set(RESUMING);
+        comp = state->comp;
         return OK;
     };
     if (tryAndReportOnError(setResuming) != OK) {
         return;
     }
 
+    mSentConfigAfterResume.clear();
+    {
+        Mutexed<std::unique_ptr<Config>>::Locked configLocked(mConfig);
+        const std::unique_ptr<Config> &config = *configLocked;
+        config->queryConfiguration(comp);
+    }
+
     (void)mChannel->start(nullptr, nullptr, [&]{
         Mutexed<std::unique_ptr<Config>>::Locked configLocked(mConfig);
         const std::unique_ptr<Config> &config = *configLocked;
@@ -1617,7 +1635,8 @@
      * Handle input surface parameters
      */
     if ((config->mDomain & (Config::IS_VIDEO | Config::IS_IMAGE))
-            && (config->mDomain & Config::IS_ENCODER) && config->mInputSurface && config->mISConfig) {
+            && (config->mDomain & Config::IS_ENCODER)
+            && config->mInputSurface && config->mISConfig) {
         (void)params->findInt64(PARAMETER_KEY_OFFSET_TIME, &config->mISConfig->mTimeOffsetUs);
 
         if (params->findInt64("skip-frames-before", &config->mISConfig->mStartAtUs)) {
@@ -1770,7 +1789,7 @@
             // handle configuration changes in work done
             Mutexed<std::unique_ptr<Config>>::Locked configLocked(mConfig);
             const std::unique_ptr<Config> &config = *configLocked;
-            bool changed = false;
+            bool changed = !mSentConfigAfterResume.test_and_set();
             Config::Watcher<C2StreamInitDataInfo::output> initData =
                 config->watch<C2StreamInitDataInfo::output>();
             if (!work->worklets.empty()
@@ -1802,7 +1821,9 @@
                     ++stream;
                 }
 
-                changed = config->updateConfiguration(updates, config->mOutputDomain);
+                if (config->updateConfiguration(updates, config->mOutputDomain)) {
+                    changed = true;
+                }
 
                 // copy standard infos to graphic buffers if not already present (otherwise, we
                 // may overwrite the actual intermediate value with a final value)
diff --git a/media/codec2/sfplugin/CCodecBufferChannel.cpp b/media/codec2/sfplugin/CCodecBufferChannel.cpp
index 035dca8..b869bd9 100644
--- a/media/codec2/sfplugin/CCodecBufferChannel.cpp
+++ b/media/codec2/sfplugin/CCodecBufferChannel.cpp
@@ -1457,6 +1457,24 @@
         std::unique_ptr<C2Work> work,
         const sp<AMessage> &outputFormat,
         const C2StreamInitDataInfo::output *initData) {
+    if (outputFormat != nullptr) {
+        Mutexed<Output>::Locked output(mOutput);
+        ALOGD("[%s] onWorkDone: output format changed to %s",
+                mName, outputFormat->debugString().c_str());
+        output->buffers->setFormat(outputFormat);
+
+        AString mediaType;
+        if (outputFormat->findString(KEY_MIME, &mediaType)
+                && mediaType == MIMETYPE_AUDIO_RAW) {
+            int32_t channelCount;
+            int32_t sampleRate;
+            if (outputFormat->findInt32(KEY_CHANNEL_COUNT, &channelCount)
+                    && outputFormat->findInt32(KEY_SAMPLE_RATE, &sampleRate)) {
+                output->buffers->updateSkipCutBuffer(sampleRate, channelCount);
+            }
+        }
+    }
+
     if ((work->input.ordinal.frameIndex - mFirstValidFrameIndex.load()).peek() < 0) {
         // Discard frames from previous generation.
         ALOGD("[%s] Discard frames from previous generation.", mName);
@@ -1634,24 +1652,6 @@
         }
     }
 
-    if (outputFormat != nullptr) {
-        Mutexed<Output>::Locked output(mOutput);
-        ALOGD("[%s] onWorkDone: output format changed to %s",
-                mName, outputFormat->debugString().c_str());
-        output->buffers->setFormat(outputFormat);
-
-        AString mediaType;
-        if (outputFormat->findString(KEY_MIME, &mediaType)
-                && mediaType == MIMETYPE_AUDIO_RAW) {
-            int32_t channelCount;
-            int32_t sampleRate;
-            if (outputFormat->findInt32(KEY_CHANNEL_COUNT, &channelCount)
-                    && outputFormat->findInt32(KEY_SAMPLE_RATE, &sampleRate)) {
-                output->buffers->updateSkipCutBuffer(sampleRate, channelCount);
-            }
-        }
-    }
-
     int32_t flags = 0;
     if (worklet->output.flags & C2FrameData::FLAG_END_OF_STREAM) {
         flags |= MediaCodec::BUFFER_FLAG_EOS;
diff --git a/media/codec2/sfplugin/CCodecConfig.cpp b/media/codec2/sfplugin/CCodecConfig.cpp
index b019d0d..051f88a 100644
--- a/media/codec2/sfplugin/CCodecConfig.cpp
+++ b/media/codec2/sfplugin/CCodecConfig.cpp
@@ -20,7 +20,6 @@
 #include <log/log.h>
 
 #include <C2Component.h>
-#include <C2Debug.h>
 #include <C2Param.h>
 #include <util/C2InterfaceHelper.h>
 
@@ -51,6 +50,27 @@
 
 namespace {
 
+void C2ValueToMessageItem(const C2Value &value, AMessage::ItemData &item) {
+    int32_t int32Value;
+    uint32_t uint32Value;
+    int64_t int64Value;
+    uint64_t uint64Value;
+    float floatValue;
+    if (value.get(&int32Value)) {
+        item.set(int32Value);
+    } else if (value.get(&uint32Value) && uint32Value <= uint32_t(INT32_MAX)) {
+        // SDK does not support unsigned values
+        item.set((int32_t)uint32Value);
+    } else if (value.get(&int64Value)) {
+        item.set(int64Value);
+    } else if (value.get(&uint64Value) && uint64Value <= uint64_t(INT64_MAX)) {
+        // SDK does not support unsigned values
+        item.set((int64_t)uint64Value);
+    } else if (value.get(&floatValue)) {
+        item.set(floatValue);
+    }
+}
+
 /**
  * mapping between SDK and Codec 2.0 configurations.
  */
@@ -140,27 +160,10 @@
     /// Maps from a C2Value to an SDK value in an AMessage.
     AMessage::ItemData mapToMessage(C2Value value) const {
         AMessage::ItemData item;
-        int32_t int32Value;
-        uint32_t uint32Value;
-        int64_t int64Value;
-        uint64_t uint64Value;
-        float floatValue;
         if (value.type() != C2Value::NO_INIT && mReverse) {
             value = mReverse(value);
         }
-        if (value.get(&int32Value)) {
-            item.set(int32Value);
-        } else if (value.get(&uint32Value) && uint32Value <= uint32_t(INT32_MAX)) {
-            // SDK does not support unsigned values
-            item.set((int32_t)uint32Value);
-        } else if (value.get(&int64Value)) {
-            item.set(int64Value);
-        } else if (value.get(&uint64Value) && uint64Value <= uint64_t(INT64_MAX)) {
-            // SDK does not support unsigned values
-            item.set((int64_t)uint64Value);
-        } else if (value.get(&floatValue)) {
-            item.set(floatValue);
-        }
+        C2ValueToMessageItem(value, item);
         return item;
     }
 
@@ -181,10 +184,10 @@
 
 template <typename PORT, typename STREAM>
 AString QueryMediaTypeImpl(
-        const std::shared_ptr<Codec2Client::Component> &component) {
+        const std::shared_ptr<Codec2Client::Configurable> &configurable) {
     AString mediaType;
     std::vector<std::unique_ptr<C2Param>> queried;
-    c2_status_t c2err = component->query(
+    c2_status_t c2err = configurable->query(
             {}, { PORT::PARAM_TYPE, STREAM::PARAM_TYPE }, C2_DONT_BLOCK, &queried);
     if (c2err != C2_OK && queried.size() == 0) {
         ALOGD("Query media type failed => %s", asString(c2err));
@@ -209,13 +212,13 @@
 }
 
 AString QueryMediaType(
-        bool input, const std::shared_ptr<Codec2Client::Component> &component) {
+        bool input, const std::shared_ptr<Codec2Client::Configurable> &configurable) {
     typedef C2PortMediaTypeSetting P;
     typedef C2StreamMediaTypeSetting S;
     if (input) {
-        return QueryMediaTypeImpl<P::input, S::input>(component);
+        return QueryMediaTypeImpl<P::input, S::input>(configurable);
     } else {
-        return QueryMediaTypeImpl<P::output, S::output>(component);
+        return QueryMediaTypeImpl<P::output, S::output>(configurable);
     }
 }
 
@@ -912,27 +915,27 @@
 }
 
 status_t CCodecConfig::initialize(
-        const std::shared_ptr<Codec2Client> &client,
-        const std::shared_ptr<Codec2Client::Component> &component) {
+        const std::shared_ptr<C2ParamReflector> &reflector,
+        const std::shared_ptr<Codec2Client::Configurable> &configurable) {
     C2ComponentDomainSetting domain(C2Component::DOMAIN_OTHER);
     C2ComponentKindSetting kind(C2Component::KIND_OTHER);
 
     std::vector<std::unique_ptr<C2Param>> queried;
-    c2_status_t c2err = component->query({ &domain, &kind }, {}, C2_DONT_BLOCK, &queried);
+    c2_status_t c2err = configurable->query({ &domain, &kind }, {}, C2_DONT_BLOCK, &queried);
     if (c2err != C2_OK) {
         ALOGD("Query domain & kind failed => %s", asString(c2err));
         // TEMP: determine kind from component name
         if (kind.value == C2Component::KIND_OTHER) {
-            if (component->getName().find("encoder") != std::string::npos) {
+            if (configurable->getName().find("encoder") != std::string::npos) {
                 kind.value = C2Component::KIND_ENCODER;
-            } else if (component->getName().find("decoder") != std::string::npos) {
+            } else if (configurable->getName().find("decoder") != std::string::npos) {
                 kind.value = C2Component::KIND_DECODER;
             }
         }
 
         // TEMP: determine domain from media type (port (preferred) or stream #0)
         if (domain.value == C2Component::DOMAIN_OTHER) {
-            AString mediaType = QueryMediaType(true /* input */, component);
+            AString mediaType = QueryMediaType(true /* input */, configurable);
             if (mediaType.startsWith("audio/")) {
                 domain.value = C2Component::DOMAIN_AUDIO;
             } else if (mediaType.startsWith("video/")) {
@@ -957,16 +960,16 @@
     std::vector<C2Param::Index> paramIndices;
     switch (kind.value) {
     case C2Component::KIND_DECODER:
-        mCodingMediaType = QueryMediaType(true /* input */, component).c_str();
+        mCodingMediaType = QueryMediaType(true /* input */, configurable).c_str();
         break;
     case C2Component::KIND_ENCODER:
-        mCodingMediaType = QueryMediaType(false /* input */, component).c_str();
+        mCodingMediaType = QueryMediaType(false /* input */, configurable).c_str();
         break;
     default:
         mCodingMediaType = "";
     }
 
-    c2err = component->querySupportedParams(&mParamDescs);
+    c2err = configurable->querySupportedParams(&mParamDescs);
     if (c2err != C2_OK) {
         ALOGD("Query supported params failed after returning %zu values => %s",
                 mParamDescs.size(), asString(c2err));
@@ -976,9 +979,9 @@
         mSupportedIndices.emplace(desc->index());
     }
 
-    mReflector = client->getParamReflector();
+    mReflector = reflector;
     if (mReflector == nullptr) {
-        ALOGE("Failed to get param reflector");
+        ALOGE("Null param reflector");
         return UNKNOWN_ERROR;
     }
 
@@ -1032,11 +1035,21 @@
     // init data (CSD)
     mSubscribedIndices.emplace(C2StreamInitDataInfo::output::PARAM_TYPE);
 
+    for (const std::shared_ptr<C2ParamDescriptor> &desc : mParamDescs) {
+        if (desc->index().isVendor()) {
+            std::vector<std::string> keys;
+            mParamUpdater->getKeysForParamIndex(desc->index(), &keys);
+            for (const std::string &key : keys) {
+                mVendorParamIndices.insert_or_assign(key, desc->index());
+            }
+        }
+    }
+
     return OK;
 }
 
 status_t CCodecConfig::subscribeToConfigUpdate(
-        const std::shared_ptr<Codec2Client::Component> &component,
+        const std::shared_ptr<Codec2Client::Configurable> &configurable,
         const std::vector<C2Param::Index> &indices,
         c2_blocking_t blocking) {
     mSubscribedIndices.insert(indices.begin(), indices.end());
@@ -1049,7 +1062,7 @@
         std::unique_ptr<C2SubscribedParamIndicesTuning> subscribeTuning =
             C2SubscribedParamIndicesTuning::AllocUnique(indices);
         std::vector<std::unique_ptr<C2SettingResult>> results;
-        c2_status_t c2Err = component->config({ subscribeTuning.get() }, blocking, &results);
+        c2_status_t c2Err = configurable->config({ subscribeTuning.get() }, blocking, &results);
         if (c2Err != C2_OK && c2Err != C2_BAD_INDEX) {
             ALOGD("Failed to subscribe to parameters => %s", asString(c2Err));
             // TODO: error
@@ -1061,11 +1074,11 @@
 }
 
 status_t CCodecConfig::queryConfiguration(
-        const std::shared_ptr<Codec2Client::Component> &component) {
+        const std::shared_ptr<Codec2Client::Configurable> &configurable) {
     // query all subscribed parameters
     std::vector<C2Param::Index> indices(mSubscribedIndices.begin(), mSubscribedIndices.end());
     std::vector<std::unique_ptr<C2Param>> queried;
-    c2_status_t c2Err = component->query({}, indices, C2_MAY_BLOCK, &queried);
+    c2_status_t c2Err = configurable->query({}, indices, C2_MAY_BLOCK, &queried);
     if (c2Err != OK) {
         ALOGI("query failed after returning %zu values (%s)", queried.size(), asString(c2Err));
         // TODO: error
@@ -1135,7 +1148,7 @@
     if (domain & mInputDomain) {
         sp<AMessage> oldFormat = mInputFormat;
         mInputFormat = mInputFormat->dup(); // trigger format changed
-        mInputFormat->extend(getSdkFormatForDomain(reflected, mInputDomain));
+        mInputFormat->extend(getFormatForDomain(reflected, mInputDomain));
         if (mInputFormat->countEntries() != oldFormat->countEntries()
                 || mInputFormat->changesFrom(oldFormat)->countEntries() > 0) {
             changed = true;
@@ -1146,7 +1159,7 @@
     if (domain & mOutputDomain) {
         sp<AMessage> oldFormat = mOutputFormat;
         mOutputFormat = mOutputFormat->dup(); // trigger output format changed
-        mOutputFormat->extend(getSdkFormatForDomain(reflected, mOutputDomain));
+        mOutputFormat->extend(getFormatForDomain(reflected, mOutputDomain));
         if (mOutputFormat->countEntries() != oldFormat->countEntries()
                 || mOutputFormat->changesFrom(oldFormat)->countEntries() > 0) {
             changed = true;
@@ -1158,8 +1171,9 @@
     return changed;
 }
 
-sp<AMessage> CCodecConfig::getSdkFormatForDomain(
-        const ReflectedParamUpdater::Dict &reflected, Domain portDomain) const {
+sp<AMessage> CCodecConfig::getFormatForDomain(
+        const ReflectedParamUpdater::Dict &reflected,
+        Domain portDomain) const {
     sp<AMessage> msg = new AMessage;
     for (const std::pair<std::string, std::vector<ConfigMapper>> &el : mStandardParams->getKeys()) {
         for (const ConfigMapper &cm : el.second) {
@@ -1190,6 +1204,39 @@
         }
     }
 
+    bool input = (portDomain & Domain::IS_INPUT);
+    std::vector<std::string> vendorKeys;
+    for (const std::pair<std::string, ReflectedParamUpdater::Value> &entry : reflected) {
+        auto it = mVendorParamIndices.find(entry.first);
+        if (it == mVendorParamIndices.end()) {
+            continue;
+        }
+        if (mSubscribedIndices.count(it->second) == 0) {
+            continue;
+        }
+        // For vendor parameters, we only care about direction
+        if ((input && !it->second.forInput())
+                || (!input && !it->second.forOutput())) {
+            continue;
+        }
+        const ReflectedParamUpdater::Value &value = entry.second;
+        C2Value c2Value;
+        sp<ABuffer> bufValue;
+        AString strValue;
+        AMessage::ItemData item;
+        if (value.find(&c2Value)) {
+            C2ValueToMessageItem(c2Value, item);
+        } else if (value.find(&bufValue)) {
+            item.set(bufValue);
+        } else if (value.find(&strValue)) {
+            item.set(strValue);
+        } else {
+            ALOGD("unexpected untyped query value for key: %s", entry.first.c_str());
+            continue;
+        }
+        msg->setItem(entry.first.c_str(), item);
+    }
+
     { // convert from Codec 2.0 rect to MediaFormat rect and add crop rect if not present
         int32_t left, top, width, height;
         if (msg->findInt32("crop-left", &left) && msg->findInt32("crop-width", &width)
@@ -1597,7 +1644,7 @@
 }
 
 status_t CCodecConfig::getConfigUpdateFromSdkParams(
-        std::shared_ptr<Codec2Client::Component> component,
+        std::shared_ptr<Codec2Client::Configurable> configurable,
         const sp<AMessage> &sdkParams, Domain configDomain,
         c2_blocking_t blocking,
         std::vector<std::unique_ptr<C2Param>> *configUpdate) const {
@@ -1624,7 +1671,7 @@
         }
     }
 
-    c2_status_t err = component->query({ }, supportedIndices, blocking, configUpdate);
+    c2_status_t err = configurable->query({ }, supportedIndices, blocking, configUpdate);
     if (err != C2_OK) {
         ALOGD("query failed after returning %zu params => %s", configUpdate->size(), asString(err));
     }
@@ -1636,7 +1683,7 @@
 }
 
 status_t CCodecConfig::setParameters(
-        std::shared_ptr<Codec2Client::Component> component,
+        std::shared_ptr<Codec2Client::Configurable> configurable,
         std::vector<std::unique_ptr<C2Param>> &configUpdate,
         c2_blocking_t blocking) {
     status_t result = OK;
@@ -1672,10 +1719,10 @@
         }
     }
     // update subscribed param indices
-    subscribeToConfigUpdate(component, indices, blocking);
+    subscribeToConfigUpdate(configurable, indices, blocking);
 
     std::vector<std::unique_ptr<C2SettingResult>> failures;
-    c2_status_t err = component->config(paramVector, blocking, &failures);
+    c2_status_t err = configurable->config(paramVector, blocking, &failures);
     if (err != C2_OK) {
         ALOGD("config failed => %s", asString(err));
         // This is non-fatal.
@@ -1695,7 +1742,7 @@
     // Re-query parameter values in case config could not update them and update the current
     // configuration.
     configUpdate.clear();
-    err = component->query({}, indices, blocking, &configUpdate);
+    err = configurable->query({}, indices, blocking, &configUpdate);
     if (err != C2_OK) {
         ALOGD("query failed after returning %zu params => %s", configUpdate.size(), asString(err));
     }
@@ -1714,4 +1761,13 @@
     }
 }
 
+status_t CCodecConfig::subscribeToAllVendorParams(
+        const std::shared_ptr<Codec2Client::Configurable> &configurable,
+        c2_blocking_t blocking) {
+    for (const std::pair<std::string, C2Param::Index> &entry : mVendorParamIndices) {
+        mSubscribedIndices.insert(entry.second);
+    }
+    return subscribeToConfigUpdate(configurable, {}, blocking);
+}
+
 }  // namespace android
diff --git a/media/codec2/sfplugin/CCodecConfig.h b/media/codec2/sfplugin/CCodecConfig.h
index 093bfdd..2895746 100644
--- a/media/codec2/sfplugin/CCodecConfig.h
+++ b/media/codec2/sfplugin/CCodecConfig.h
@@ -23,8 +23,10 @@
 #include <vector>
 
 #include <C2Component.h>
-#include <codec2/hidl/client.h>
+#include <C2Config.h>
+#include <C2Debug.h>
 
+#include <codec2/hidl/client.h>
 #include <utils/RefBase.h>
 
 #include "InputSurfaceWrapper.h"
@@ -39,7 +41,6 @@
  * Struct managing the codec configuration for CCodec.
  */
 struct CCodecConfig {
-
     /**
      * Domain consists of a bitmask divided into fields, and specifiers work by excluding other
      * values in those domains.
@@ -135,6 +136,9 @@
     /// For now support a validation function.
     std::map<C2Param::Index, LocalParamValidator> mLocalParams;
 
+    /// Vendor field name -> index map.
+    std::map<std::string, C2Param::Index> mVendorParamIndices;
+
     std::set<std::string> mLastConfig;
 
     CCodecConfig();
@@ -143,9 +147,8 @@
     /// reflected param helper, domain, standard params, and subscribes to standard
     /// indices.
     status_t initialize(
-            const std::shared_ptr<Codec2Client> &client,
-            const std::shared_ptr<Codec2Client::Component> &component);
-
+            const std::shared_ptr<C2ParamReflector> &client,
+            const std::shared_ptr<Codec2Client::Configurable> &configurable);
 
     /**
      * Adds a locally maintained parameter. This is used for output configuration that can be
@@ -238,7 +241,7 @@
      * \param blocking blocking mode to use with the component
      */
     status_t getConfigUpdateFromSdkParams(
-            std::shared_ptr<Codec2Client::Component> component,
+            std::shared_ptr<Codec2Client::Configurable> configurable,
             const sp<AMessage> &sdkParams, Domain domain,
             c2_blocking_t blocking,
             std::vector<std::unique_ptr<C2Param>> *configUpdate) const;
@@ -250,19 +253,24 @@
      * \param blocking blocking mode to use with the component
      */
     status_t setParameters(
-            std::shared_ptr<Codec2Client::Component> component,
+            std::shared_ptr<Codec2Client::Configurable> configurable,
             std::vector<std::unique_ptr<C2Param>> &configUpdate,
             c2_blocking_t blocking);
 
     /// Queries subscribed indices (which contains all SDK-exposed values) and updates
     /// input/output formats.
     status_t queryConfiguration(
-            const std::shared_ptr<Codec2Client::Component> &component);
+            const std::shared_ptr<Codec2Client::Configurable> &configurable);
 
     /// Queries a configuration parameter value. Returns nullptr if the parameter is not
     /// part of the current configuration
     const C2Param *getConfigParameterValue(C2Param::Index index) const;
 
+    /// Subscribe to all vendor parameters.
+    status_t subscribeToAllVendorParams(
+            const std::shared_ptr<Codec2Client::Configurable> &configurable,
+            c2_blocking_t blocking);
+
     /**
      * Object that can be used to access configuration parameters and if they change.
      */
@@ -321,14 +329,15 @@
     /// Adds indices to the subscribed indices, and updated subscription to component
     /// \param blocking blocking mode to use with the component
     status_t subscribeToConfigUpdate(
-            const std::shared_ptr<Codec2Client::Component> &component,
+            const std::shared_ptr<Codec2Client::Configurable> &configurable,
             const std::vector<C2Param::Index> &indices,
             c2_blocking_t blocking = C2_DONT_BLOCK);
 
     /// Gets SDK format from codec 2.0 reflected configuration
     /// \param domain input/output bitmask
-    sp<AMessage> getSdkFormatForDomain(
-            const ReflectedParamUpdater::Dict &reflected, Domain domain) const;
+    sp<AMessage> getFormatForDomain(
+            const ReflectedParamUpdater::Dict &reflected,
+            Domain domain) const;
 
     /**
      * Converts a set of configuration parameters in an AMessage to a list of path-based Codec
diff --git a/media/codec2/sfplugin/ReflectedParamUpdater.cpp b/media/codec2/sfplugin/ReflectedParamUpdater.cpp
index 55b0ec9..f39051b 100644
--- a/media/codec2/sfplugin/ReflectedParamUpdater.cpp
+++ b/media/codec2/sfplugin/ReflectedParamUpdater.cpp
@@ -125,18 +125,6 @@
         }
         addParamDesc(desc, *structDesc, reflector, true /* markVendor */);
     }
-
-    // TEMP: also add vendor parameters as non-vendor
-    for (const std::shared_ptr<C2ParamDescriptor> &desc : paramDescs) {
-        if (!desc->index().isVendor()) {
-            continue;
-        }
-        std::unique_ptr<C2StructDescriptor> structDesc = reflector->describe(
-                desc->index().coreIndex());
-        if (structDesc) {
-            addParamDesc(desc, *structDesc, reflector, false /* markVendor */);
-        }
-    }
 }
 
 void ReflectedParamUpdater::addParamStructDesc(
@@ -286,6 +274,20 @@
     }
 }
 
+void ReflectedParamUpdater::getKeysForParamIndex(
+        const C2Param::Index &index,
+        std::vector<std::string> *keys /* nonnull */) const {
+    CHECK(keys != nullptr);
+    keys->clear();
+    for (const std::pair<const std::string, FieldDesc> &kv : mMap) {
+        const std::string &name = kv.first;
+        const FieldDesc &desc = kv.second;
+        if (desc.paramDesc->index() == index) {
+            keys->push_back(name);
+        }
+    }
+}
+
 void ReflectedParamUpdater::updateParamsFromMessage(
         const Dict &params,
         std::vector<std::unique_ptr<C2Param>> *vec /* nonnull */) const {
diff --git a/media/codec2/sfplugin/ReflectedParamUpdater.h b/media/codec2/sfplugin/ReflectedParamUpdater.h
index 5436ba5..752c7e4 100644
--- a/media/codec2/sfplugin/ReflectedParamUpdater.h
+++ b/media/codec2/sfplugin/ReflectedParamUpdater.h
@@ -166,6 +166,16 @@
             std::vector<C2Param::Index> *vec /* nonnull */) const;
 
     /**
+     * Get list of field names for the given param index.
+     *
+     * \param index[in]   param index
+     * \param keys[out]   vector to store the field names
+     */
+    void getKeysForParamIndex(
+            const C2Param::Index &index,
+            std::vector<std::string> *keys /* nonnull */) const;
+
+    /**
      * Update C2Param objects from field name and value in AMessage object.
      *
      * \param params[in]    Dict object with field name to value pairs.
diff --git a/media/codec2/sfplugin/include/media/stagefright/CCodec.h b/media/codec2/sfplugin/include/media/stagefright/CCodec.h
index 6ff2c4a..ecb2506 100644
--- a/media/codec2/sfplugin/include/media/stagefright/CCodec.h
+++ b/media/codec2/sfplugin/include/media/stagefright/CCodec.h
@@ -17,6 +17,7 @@
 #ifndef C_CODEC_H_
 #define C_CODEC_H_
 
+#include <atomic>
 #include <chrono>
 #include <list>
 #include <memory>
@@ -189,9 +190,10 @@
     struct ClientListener;
 
     Mutexed<NamedTimePoint> mDeadline;
-    typedef CCodecConfig Config;
+
     Mutexed<std::unique_ptr<CCodecConfig>> mConfig;
     Mutexed<std::list<std::unique_ptr<C2Work>>> mWorkDoneQueue;
+    std::atomic_flag mSentConfigAfterResume;
 
     friend class CCodecCallbackImpl;
 
diff --git a/media/codec2/sfplugin/tests/Android.bp b/media/codec2/sfplugin/tests/Android.bp
index c953f84..fe5fa68 100644
--- a/media/codec2/sfplugin/tests/Android.bp
+++ b/media/codec2/sfplugin/tests/Android.bp
@@ -2,16 +2,24 @@
     name: "ccodec_unit_test",
 
     srcs: [
+        "CCodecConfig_test.cpp",
         "ReflectedParamUpdater_test.cpp",
     ],
 
+    defaults: [
+        "libcodec2-hidl-defaults@1.0",
+        "libcodec2-internal-defaults",
+    ],
+
     include_dirs: [
         "frameworks/av/media/codec2/sfplugin",
     ],
 
     shared_libs: [
         "libcodec2",
+        "libcodec2_client",
         "libsfplugin_ccodec",
+        "libsfplugin_ccodec_utils",
         "libstagefright_foundation",
         "libutils",
     ],
diff --git a/media/codec2/sfplugin/tests/CCodecConfig_test.cpp b/media/codec2/sfplugin/tests/CCodecConfig_test.cpp
new file mode 100644
index 0000000..7b445a0
--- /dev/null
+++ b/media/codec2/sfplugin/tests/CCodecConfig_test.cpp
@@ -0,0 +1,311 @@
+/*
+ * Copyright 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.
+ */
+
+#include "CCodecConfig.h"
+
+#include <set>
+
+#include <gtest/gtest.h>
+
+#include <codec2/hidl/1.0/Configurable.h>
+#include <codec2/hidl/client.h>
+#include <util/C2InterfaceHelper.h>
+
+namespace {
+
+enum ExtendedC2ParamIndexKind : C2Param::type_index_t {
+    kParamIndexVendorInt32 = C2Param::TYPE_INDEX_VENDOR_START,
+    kParamIndexVendorInt64,
+    kParamIndexVendorString,
+};
+
+typedef C2PortParam<C2Info, C2Int32Value, kParamIndexVendorInt32> C2PortVendorInt32Info;
+constexpr char C2_PARAMKEY_VENDOR_INT32[] = "example.int32";
+constexpr char KEY_VENDOR_INT32[] = "vendor.example.int32.value";
+
+typedef C2StreamParam<C2Info, C2Int64Value, kParamIndexVendorInt64> C2StreamVendorInt64Info;
+constexpr char C2_PARAMKEY_VENDOR_INT64[] = "example.int64";
+constexpr char KEY_VENDOR_INT64[] = "vendor.example.int64.value";
+
+typedef C2PortParam<C2Info, C2StringValue, kParamIndexVendorString> C2PortVendorStringInfo;
+constexpr char C2_PARAMKEY_VENDOR_STRING[] = "example.string";
+constexpr char KEY_VENDOR_STRING[] = "vendor.example.string.value";
+
+}  // namespace
+
+namespace android {
+
+class CCodecConfigTest : public ::testing::Test {
+public:
+    constexpr static int32_t kCodec2Int32 = 0xC0DEC2;
+    constexpr static int64_t kCodec2Int64 = 0xC0DEC2C0DEC2ll;
+    constexpr static char kCodec2Str[] = "codec2";
+
+    CCodecConfigTest()
+        : mReflector{std::make_shared<C2ReflectorHelper>()} {
+        sp<hardware::media::c2::V1_0::utils::CachedConfigurable> cachedConfigurable =
+            new hardware::media::c2::V1_0::utils::CachedConfigurable(
+                    std::make_unique<Configurable>(mReflector));
+        cachedConfigurable->init(std::make_shared<Cache>());
+        mConfigurable = std::make_shared<Codec2Client::Configurable>(cachedConfigurable);
+    }
+
+    struct Cache : public hardware::media::c2::V1_0::utils::ParameterCache {
+        c2_status_t validate(const std::vector<std::shared_ptr<C2ParamDescriptor>>&) override {
+            return C2_OK;
+        }
+    };
+
+    class Configurable : public hardware::media::c2::V1_0::utils::ConfigurableC2Intf {
+    public:
+        explicit Configurable(const std::shared_ptr<C2ReflectorHelper> &reflector)
+            : ConfigurableC2Intf("name", 0u),
+              mImpl(reflector) {
+        }
+
+        c2_status_t query(
+                const std::vector<C2Param::Index> &indices,
+                c2_blocking_t mayBlock,
+                std::vector<std::unique_ptr<C2Param>>* const params) const override {
+            return mImpl.query({}, indices, mayBlock, params);
+        }
+
+        c2_status_t config(
+                const std::vector<C2Param*> &params,
+                c2_blocking_t mayBlock,
+                std::vector<std::unique_ptr<C2SettingResult>>* const failures) override {
+            return mImpl.config(params, mayBlock, failures);
+        }
+
+        c2_status_t querySupportedParams(
+                std::vector<std::shared_ptr<C2ParamDescriptor>>* const params) const override {
+            return mImpl.querySupportedParams(params);
+        }
+
+        c2_status_t querySupportedValues(
+                std::vector<C2FieldSupportedValuesQuery>& fields,
+                c2_blocking_t mayBlock) const override {
+            return mImpl.querySupportedValues(fields, mayBlock);
+        }
+
+    private:
+        class Impl : public C2InterfaceHelper {
+        public:
+            explicit Impl(const std::shared_ptr<C2ReflectorHelper> &reflector)
+                : C2InterfaceHelper{reflector} {
+                setDerivedInstance(this);
+
+                addParameter(
+                        DefineParam(mInt32Input, C2_PARAMKEY_VENDOR_INT32)
+                        .withDefault(new C2PortVendorInt32Info::input(0))
+                        .withFields({C2F(mInt32Input, value).any()})
+                        .withSetter(Setter<decltype(mInt32Input)::element_type>)
+                        .build());
+
+                addParameter(
+                        DefineParam(mInt64Output, C2_PARAMKEY_VENDOR_INT64)
+                        .withDefault(new C2StreamVendorInt64Info::output(0u, 0))
+                        .withFields({C2F(mInt64Output, value).any()})
+                        .withSetter(Setter<decltype(mInt64Output)::element_type>)
+                        .build());
+
+                addParameter(
+                        DefineParam(mStringInput, C2_PARAMKEY_VENDOR_STRING)
+                        .withDefault(decltype(mStringInput)::element_type::AllocShared(1, ""))
+                        .withFields({C2F(mStringInput, m.value).any()})
+                        .withSetter(Setter<decltype(mStringInput)::element_type>)
+                        .build());
+
+                // TODO: SDK params
+            }
+        private:
+            std::shared_ptr<C2PortVendorInt32Info::input> mInt32Input;
+            std::shared_ptr<C2StreamVendorInt64Info::output> mInt64Output;
+            std::shared_ptr<C2PortVendorStringInfo::input> mStringInput;
+
+            template<typename T>
+            static C2R Setter(bool, C2P<T> &) {
+                return C2R::Ok();
+            }
+        };
+
+        Impl mImpl;
+    };
+
+    std::shared_ptr<C2ReflectorHelper> mReflector;
+    std::shared_ptr<Codec2Client::Configurable> mConfigurable;
+    CCodecConfig mConfig;
+};
+
+using D = CCodecConfig::Domain;
+
+template<typename T>
+T *FindParam(const std::vector<std::unique_ptr<C2Param>> &vec) {
+    for (const std::unique_ptr<C2Param> &param : vec) {
+        if (param->coreIndex() == T::CORE_INDEX) {
+            return static_cast<T *>(param.get());
+        }
+    }
+    return nullptr;
+}
+
+TEST_F(CCodecConfigTest, SetVendorParam) {
+    ASSERT_EQ(OK, mConfig.initialize(mReflector, mConfigurable));
+
+    sp<AMessage> format{new AMessage};
+    format->setInt32(KEY_VENDOR_INT32, kCodec2Int32);
+    format->setInt64(KEY_VENDOR_INT64, kCodec2Int64);
+    format->setString(KEY_VENDOR_STRING, kCodec2Str);
+
+    std::vector<std::unique_ptr<C2Param>> configUpdate;
+    ASSERT_EQ(OK, mConfig.getConfigUpdateFromSdkParams(
+            mConfigurable, format, D::IS_INPUT | D::IS_OUTPUT, C2_MAY_BLOCK, &configUpdate));
+
+    ASSERT_EQ(3u, configUpdate.size());
+    C2PortVendorInt32Info::input *i32 =
+        FindParam<std::remove_pointer<decltype(i32)>::type>(configUpdate);
+    ASSERT_NE(nullptr, i32);
+    ASSERT_EQ(kCodec2Int32, i32->value);
+
+    C2StreamVendorInt64Info::output *i64 =
+        FindParam<std::remove_pointer<decltype(i64)>::type>(configUpdate);
+    ASSERT_NE(nullptr, i64);
+    ASSERT_EQ(kCodec2Int64, i64->value);
+
+    C2PortVendorStringInfo::input *str =
+        FindParam<std::remove_pointer<decltype(str)>::type>(configUpdate);
+    ASSERT_NE(nullptr, str);
+    ASSERT_STREQ(kCodec2Str, str->m.value);
+}
+
+TEST_F(CCodecConfigTest, VendorParamUpdate_Unsubscribed) {
+    ASSERT_EQ(OK, mConfig.initialize(mReflector, mConfigurable));
+
+    std::vector<std::unique_ptr<C2Param>> configUpdate;
+    C2PortVendorInt32Info::input i32(kCodec2Int32);
+    C2StreamVendorInt64Info::output i64(0u, kCodec2Int64);
+    std::unique_ptr<C2PortVendorStringInfo::input> str =
+        C2PortVendorStringInfo::input::AllocUnique(strlen(kCodec2Str) + 1, kCodec2Str);
+    configUpdate.push_back(C2Param::Copy(i32));
+    configUpdate.push_back(C2Param::Copy(i64));
+    configUpdate.push_back(std::move(str));
+
+    // The vendor parameters are not yet subscribed
+    ASSERT_FALSE(mConfig.updateConfiguration(configUpdate, D::IS_INPUT | D::IS_OUTPUT));
+
+    int32_t vendorInt32{0};
+    ASSERT_FALSE(mConfig.mInputFormat->findInt32(KEY_VENDOR_INT32, &vendorInt32))
+            << "mInputFormat = " << mConfig.mInputFormat->debugString().c_str();
+    ASSERT_FALSE(mConfig.mOutputFormat->findInt32(KEY_VENDOR_INT32, &vendorInt32))
+            << "mOutputFormat = " << mConfig.mOutputFormat->debugString().c_str();
+
+    int64_t vendorInt64{0};
+    ASSERT_FALSE(mConfig.mInputFormat->findInt64(KEY_VENDOR_INT64, &vendorInt64))
+            << "mInputFormat = " << mConfig.mInputFormat->debugString().c_str();
+    ASSERT_FALSE(mConfig.mOutputFormat->findInt64(KEY_VENDOR_INT64, &vendorInt64))
+            << "mOutputFormat = " << mConfig.mOutputFormat->debugString().c_str();
+
+    AString vendorString;
+    ASSERT_FALSE(mConfig.mInputFormat->findString(KEY_VENDOR_STRING, &vendorString))
+            << "mInputFormat = " << mConfig.mInputFormat->debugString().c_str();
+    ASSERT_FALSE(mConfig.mOutputFormat->findString(KEY_VENDOR_STRING, &vendorString))
+            << "mOutputFormat = " << mConfig.mOutputFormat->debugString().c_str();
+}
+
+TEST_F(CCodecConfigTest, VendorParamUpdate_AllSubscribed) {
+    ASSERT_EQ(OK, mConfig.initialize(mReflector, mConfigurable));
+
+    // Force subscribe to all vendor params
+    ASSERT_EQ(OK, mConfig.subscribeToAllVendorParams(mConfigurable, C2_MAY_BLOCK));
+
+    std::vector<std::unique_ptr<C2Param>> configUpdate;
+    C2PortVendorInt32Info::input i32(kCodec2Int32);
+    C2StreamVendorInt64Info::output i64(0u, kCodec2Int64);
+    std::unique_ptr<C2PortVendorStringInfo::input> str =
+        C2PortVendorStringInfo::input::AllocUnique(strlen(kCodec2Str) + 1, kCodec2Str);
+    configUpdate.push_back(C2Param::Copy(i32));
+    configUpdate.push_back(C2Param::Copy(i64));
+    configUpdate.push_back(std::move(str));
+
+    ASSERT_TRUE(mConfig.updateConfiguration(configUpdate, D::IS_INPUT | D::IS_OUTPUT));
+
+    int32_t vendorInt32{0};
+    ASSERT_TRUE(mConfig.mInputFormat->findInt32(KEY_VENDOR_INT32, &vendorInt32))
+            << "mInputFormat = " << mConfig.mInputFormat->debugString().c_str();
+    ASSERT_EQ(kCodec2Int32, vendorInt32);
+    ASSERT_FALSE(mConfig.mOutputFormat->findInt32(KEY_VENDOR_INT32, &vendorInt32))
+            << "mOutputFormat = " << mConfig.mOutputFormat->debugString().c_str();
+
+    int64_t vendorInt64{0};
+    ASSERT_FALSE(mConfig.mInputFormat->findInt64(KEY_VENDOR_INT64, &vendorInt64))
+            << "mInputFormat = " << mConfig.mInputFormat->debugString().c_str();
+    ASSERT_TRUE(mConfig.mOutputFormat->findInt64(KEY_VENDOR_INT64, &vendorInt64))
+            << "mOutputFormat = " << mConfig.mOutputFormat->debugString().c_str();
+    ASSERT_EQ(kCodec2Int64, vendorInt64);
+
+    AString vendorString;
+    ASSERT_TRUE(mConfig.mInputFormat->findString(KEY_VENDOR_STRING, &vendorString))
+            << "mInputFormat = " << mConfig.mInputFormat->debugString().c_str();
+    ASSERT_STREQ(kCodec2Str, vendorString.c_str());
+    ASSERT_FALSE(mConfig.mOutputFormat->findString(KEY_VENDOR_STRING, &vendorString))
+            << "mOutputFormat = " << mConfig.mOutputFormat->debugString().c_str();
+}
+
+TEST_F(CCodecConfigTest, VendorParamUpdate_PartiallySubscribed) {
+    ASSERT_EQ(OK, mConfig.initialize(mReflector, mConfigurable));
+
+    // Subscribe to example.int32 only
+    std::vector<std::unique_ptr<C2Param>> configUpdate;
+    sp<AMessage> format{new AMessage};
+    format->setInt32(KEY_VENDOR_INT32, 0);
+    configUpdate.clear();
+    ASSERT_EQ(OK, mConfig.getConfigUpdateFromSdkParams(
+            mConfigurable, format, D::IS_INPUT | D::IS_OUTPUT, C2_MAY_BLOCK, &configUpdate));
+    ASSERT_EQ(OK, mConfig.setParameters(mConfigurable, configUpdate, C2_MAY_BLOCK));
+
+    C2PortVendorInt32Info::input i32(kCodec2Int32);
+    C2StreamVendorInt64Info::output i64(0u, kCodec2Int64);
+    std::unique_ptr<C2PortVendorStringInfo::input> str =
+        C2PortVendorStringInfo::input::AllocUnique(strlen(kCodec2Str) + 1, kCodec2Str);
+    configUpdate.clear();
+    configUpdate.push_back(C2Param::Copy(i32));
+    configUpdate.push_back(C2Param::Copy(i64));
+    configUpdate.push_back(std::move(str));
+
+    // Only example.i32 should be updated
+    ASSERT_TRUE(mConfig.updateConfiguration(configUpdate, D::IS_INPUT | D::IS_OUTPUT));
+
+    int32_t vendorInt32{0};
+    ASSERT_TRUE(mConfig.mInputFormat->findInt32(KEY_VENDOR_INT32, &vendorInt32))
+            << "mInputFormat = " << mConfig.mInputFormat->debugString().c_str();
+    ASSERT_EQ(kCodec2Int32, vendorInt32);
+    ASSERT_FALSE(mConfig.mOutputFormat->findInt32(KEY_VENDOR_INT32, &vendorInt32))
+            << "mOutputFormat = " << mConfig.mOutputFormat->debugString().c_str();
+
+    int64_t vendorInt64{0};
+    ASSERT_FALSE(mConfig.mInputFormat->findInt64(KEY_VENDOR_INT64, &vendorInt64))
+            << "mInputFormat = " << mConfig.mInputFormat->debugString().c_str();
+    ASSERT_FALSE(mConfig.mOutputFormat->findInt64(KEY_VENDOR_INT64, &vendorInt64))
+            << "mOutputFormat = " << mConfig.mOutputFormat->debugString().c_str();
+
+    AString vendorString;
+    ASSERT_FALSE(mConfig.mInputFormat->findString(KEY_VENDOR_STRING, &vendorString))
+            << "mInputFormat = " << mConfig.mInputFormat->debugString().c_str();
+    ASSERT_FALSE(mConfig.mOutputFormat->findString(KEY_VENDOR_STRING, &vendorString))
+            << "mOutputFormat = " << mConfig.mOutputFormat->debugString().c_str();
+}
+
+} // namespace android
diff --git a/media/libaaudio/src/legacy/AudioStreamLegacy.cpp b/media/libaaudio/src/legacy/AudioStreamLegacy.cpp
index 91d2eff..abc3239 100644
--- a/media/libaaudio/src/legacy/AudioStreamLegacy.cpp
+++ b/media/libaaudio/src/legacy/AudioStreamLegacy.cpp
@@ -72,7 +72,7 @@
 
 // Implement FixedBlockProcessor
 int32_t AudioStreamLegacy::onProcessFixedBlock(uint8_t *buffer, int32_t numBytes) {
-    int32_t numFrames = numBytes / getBytesPerDeviceFrame();
+    int32_t numFrames = numBytes / mBlockAdapterBytesPerFrame;
     return (int32_t) callDataCallbackFrames(buffer, numFrames);
 }
 
diff --git a/media/libaaudio/src/legacy/AudioStreamLegacy.h b/media/libaaudio/src/legacy/AudioStreamLegacy.h
index 8e78554..da9205f 100644
--- a/media/libaaudio/src/legacy/AudioStreamLegacy.h
+++ b/media/libaaudio/src/legacy/AudioStreamLegacy.h
@@ -133,6 +133,7 @@
     MonotonicCounter           mTimestampPosition;
 
     FixedBlockAdapter         *mBlockAdapter = nullptr;
+    int32_t                    mBlockAdapterBytesPerFrame = 0;
     aaudio_wrapping_frames_t   mPositionWhenStarting = 0;
     int32_t                    mCallbackBufferSize = 0;
     const android::sp<StreamDeviceCallback>   mDeviceCallback;
diff --git a/media/libaaudio/src/legacy/AudioStreamRecord.cpp b/media/libaaudio/src/legacy/AudioStreamRecord.cpp
index 2ed82d2..904edd1 100644
--- a/media/libaaudio/src/legacy/AudioStreamRecord.cpp
+++ b/media/libaaudio/src/legacy/AudioStreamRecord.cpp
@@ -213,7 +213,10 @@
 
     // We may need to pass the data through a block size adapter to guarantee constant size.
     if (mCallbackBufferSize != AAUDIO_UNSPECIFIED) {
-        int callbackSizeBytes = getBytesPerFrame() * mCallbackBufferSize;
+        // The block adapter runs before the format conversion.
+        // So we need to use the device frame size.
+        mBlockAdapterBytesPerFrame = getBytesPerDeviceFrame();
+        int callbackSizeBytes = mBlockAdapterBytesPerFrame * mCallbackBufferSize;
         mFixedBlockWriter.open(callbackSizeBytes);
         mBlockAdapter = &mFixedBlockWriter;
     } else {
diff --git a/media/libaaudio/src/legacy/AudioStreamTrack.cpp b/media/libaaudio/src/legacy/AudioStreamTrack.cpp
index 00963d6..d54d043 100644
--- a/media/libaaudio/src/legacy/AudioStreamTrack.cpp
+++ b/media/libaaudio/src/legacy/AudioStreamTrack.cpp
@@ -196,7 +196,10 @@
 
     // We may need to pass the data through a block size adapter to guarantee constant size.
     if (mCallbackBufferSize != AAUDIO_UNSPECIFIED) {
-        int callbackSizeBytes = getBytesPerFrame() * mCallbackBufferSize;
+        // This may need to change if we add format conversion before
+        // the block size adaptation.
+        mBlockAdapterBytesPerFrame = getBytesPerFrame();
+        int callbackSizeBytes = mBlockAdapterBytesPerFrame * mCallbackBufferSize;
         mFixedBlockReader.open(callbackSizeBytes);
         mBlockAdapter = &mFixedBlockReader;
     } else {
diff --git a/media/libaudioprocessing/Android.bp b/media/libaudioprocessing/Android.bp
index 1a6a5a2..39b0ceb 100644
--- a/media/libaudioprocessing/Android.bp
+++ b/media/libaudioprocessing/Android.bp
@@ -19,6 +19,25 @@
         // uncomment to disable NEON on architectures that actually do support NEON, for benchmarking
         // "-DUSE_NEON=false",
     ],
+
+    arch: {
+        x86: {
+            avx2: {
+                cflags: [
+                    "-mavx2",
+                    "-mfma",
+                ],
+            },
+        },
+        x86_64: {
+            avx2: {
+                cflags: [
+                    "-mavx2",
+                    "-mfma",
+                ],
+            },
+        },
+    },
 }
 
 cc_library_shared {
diff --git a/media/libaudioprocessing/AudioMixer.cpp b/media/libaudioprocessing/AudioMixer.cpp
index c0b11a4..1a31420 100644
--- a/media/libaudioprocessing/AudioMixer.cpp
+++ b/media/libaudioprocessing/AudioMixer.cpp
@@ -162,10 +162,10 @@
     // discard the previous downmixer if there was one
     unprepareForDownmix();
     // MONO_HACK Only remix (upmix or downmix) if the track and mixer/device channel masks
-    // are not the same and not handled internally, as mono -> stereo currently is.
+    // are not the same and not handled internally, as mono for channel position masks is.
     if (channelMask == mMixerChannelMask
             || (channelMask == AUDIO_CHANNEL_OUT_MONO
-                    && mMixerChannelMask == AUDIO_CHANNEL_OUT_STEREO)) {
+                    && isAudioChannelPositionMask(mMixerChannelMask))) {
         return NO_ERROR;
     }
     // DownmixerBufferProvider is only used for position masks.
diff --git a/media/libaudioprocessing/AudioMixerBase.cpp b/media/libaudioprocessing/AudioMixerBase.cpp
index 75c077d..64f91fe 100644
--- a/media/libaudioprocessing/AudioMixerBase.cpp
+++ b/media/libaudioprocessing/AudioMixerBase.cpp
@@ -643,14 +643,28 @@
             if (n & NEEDS_RESAMPLE) {
                 all16BitsStereoNoResample = false;
                 resampling = true;
-                t->hook = TrackBase::getTrackHook(TRACKTYPE_RESAMPLE, t->mMixerChannelCount,
-                        t->mMixerInFormat, t->mMixerFormat);
+                if ((n & NEEDS_CHANNEL_COUNT__MASK) == NEEDS_CHANNEL_1
+                        && t->channelMask == AUDIO_CHANNEL_OUT_MONO // MONO_HACK
+                        && isAudioChannelPositionMask(t->mMixerChannelMask)) {
+                    t->hook = TrackBase::getTrackHook(
+                            TRACKTYPE_RESAMPLEMONO, t->mMixerChannelCount,
+                            t->mMixerInFormat, t->mMixerFormat);
+                } else if ((n & NEEDS_CHANNEL_COUNT__MASK) >= NEEDS_CHANNEL_2
+                        && t->useStereoVolume()) {
+                    t->hook = TrackBase::getTrackHook(
+                            TRACKTYPE_RESAMPLESTEREO, t->mMixerChannelCount,
+                            t->mMixerInFormat, t->mMixerFormat);
+                } else {
+                    t->hook = TrackBase::getTrackHook(
+                            TRACKTYPE_RESAMPLE, t->mMixerChannelCount,
+                            t->mMixerInFormat, t->mMixerFormat);
+                }
                 ALOGV_IF((n & NEEDS_CHANNEL_COUNT__MASK) > NEEDS_CHANNEL_2,
                         "Track %d needs downmix + resample", name);
             } else {
                 if ((n & NEEDS_CHANNEL_COUNT__MASK) == NEEDS_CHANNEL_1){
                     t->hook = TrackBase::getTrackHook(
-                            (t->mMixerChannelMask == AUDIO_CHANNEL_OUT_STEREO  // TODO: MONO_HACK
+                            (isAudioChannelPositionMask(t->mMixerChannelMask)  // TODO: MONO_HACK
                                     && t->channelMask == AUDIO_CHANNEL_OUT_MONO)
                                 ? TRACKTYPE_NORESAMPLEMONO : TRACKTYPE_NORESAMPLE,
                             t->mMixerChannelCount,
@@ -658,8 +672,11 @@
                     all16BitsStereoNoResample = false;
                 }
                 if ((n & NEEDS_CHANNEL_COUNT__MASK) >= NEEDS_CHANNEL_2){
-                    t->hook = TrackBase::getTrackHook(TRACKTYPE_NORESAMPLE, t->mMixerChannelCount,
-                            t->mMixerInFormat, t->mMixerFormat);
+                    t->hook = TrackBase::getTrackHook(
+                            t->useStereoVolume() ? TRACKTYPE_NORESAMPLESTEREO
+                                    : TRACKTYPE_NORESAMPLE,
+                            t->mMixerChannelCount, t->mMixerInFormat,
+                            t->mMixerFormat);
                     ALOGV_IF((n & NEEDS_CHANNEL_COUNT__MASK) > NEEDS_CHANNEL_2,
                             "Track %d needs downmix", name);
                 }
@@ -691,7 +708,8 @@
                         // special case handling due to implicit channel duplication.
                         // Stereo or Multichannel should actually be fine here.
                         mHook = getProcessHook(PROCESSTYPE_NORESAMPLEONETRACK,
-                                t->mMixerChannelCount, t->mMixerInFormat, t->mMixerFormat);
+                                t->mMixerChannelCount, t->mMixerInFormat, t->mMixerFormat,
+                                t->useStereoVolume());
                     }
                 }
             }
@@ -726,7 +744,8 @@
                 const std::shared_ptr<TrackBase> &t = mTracks[mEnabled[0]];
                 // Muted single tracks handled by allMuted above.
                 mHook = getProcessHook(PROCESSTYPE_NORESAMPLEONETRACK,
-                        t->mMixerChannelCount, t->mMixerInFormat, t->mMixerFormat);
+                        t->mMixerChannelCount, t->mMixerInFormat, t->mMixerFormat,
+                        t->useStereoVolume());
             }
         }
     }
@@ -1450,7 +1469,7 @@
         }
 
         const size_t outFrames = b.frameCount;
-        t->volumeMix<MIXTYPE, is_same<TI, float>::value /* USEFLOATVOL */, false /* ADJUSTVOL */> (
+        t->volumeMix<MIXTYPE, std::is_same_v<TI, float> /* USEFLOATVOL */, false /* ADJUSTVOL */> (
                 out, outFrames, in, aux, ramp);
 
         out += outFrames * channels;
@@ -1463,7 +1482,7 @@
         t->bufferProvider->releaseBuffer(&b);
     }
     if (ramp) {
-        t->adjustVolumeRamp(aux != NULL, is_same<TI, float>::value);
+        t->adjustVolumeRamp(aux != NULL, std::is_same_v<TI, float>);
     }
 }
 
@@ -1481,7 +1500,8 @@
     ALOGVV("track__Resample\n");
     mResampler->setSampleRate(sampleRate);
     const bool ramp = needsRamp();
-    if (ramp || aux != NULL) {
+    if (MIXTYPE == MIXTYPE_MONOEXPAND || MIXTYPE == MIXTYPE_STEREOEXPAND
+            || ramp || aux != NULL) {
         // if ramp:        resample with unity gain to temp buffer and scale/mix in 2nd step.
         // if aux != NULL: resample with unity gain to temp buffer then apply send level.
 
@@ -1489,7 +1509,7 @@
         memset(temp, 0, outFrameCount * mMixerChannelCount * sizeof(TO));
         mResampler->resample((int32_t*)temp, outFrameCount, bufferProvider);
 
-        volumeMix<MIXTYPE, is_same<TI, float>::value /* USEFLOATVOL */, true /* ADJUSTVOL */>(
+        volumeMix<MIXTYPE, std::is_same_v<TI, float> /* USEFLOATVOL */, true /* ADJUSTVOL */>(
                 out, outFrameCount, temp, aux, ramp);
 
     } else { // constant volume gain
@@ -1513,7 +1533,7 @@
     ALOGVV("track__NoResample\n");
     const TI *in = static_cast<const TI *>(mIn);
 
-    volumeMix<MIXTYPE, is_same<TI, float>::value /* USEFLOATVOL */, true /* ADJUSTVOL */>(
+    volumeMix<MIXTYPE, std::is_same_v<TI, float> /* USEFLOATVOL */, true /* ADJUSTVOL */>(
             out, frameCount, in, aux, needsRamp());
 
     // MIXTYPE_MONOEXPAND reads a single input channel and expands to NCHAN output channels.
@@ -1601,6 +1621,38 @@
             break;
         }
         break;
+    case TRACKTYPE_RESAMPLESTEREO:
+        switch (mixerInFormat) {
+        case AUDIO_FORMAT_PCM_FLOAT:
+            return (AudioMixerBase::hook_t) &TrackBase::track__Resample<
+                    MIXTYPE_MULTI_STEREOVOL, float /*TO*/, float /*TI*/,
+                    TYPE_AUX>;
+        case AUDIO_FORMAT_PCM_16_BIT:
+            return (AudioMixerBase::hook_t) &TrackBase::track__Resample<
+                    MIXTYPE_MULTI_STEREOVOL, int32_t /*TO*/, int16_t /*TI*/,
+                    TYPE_AUX>;
+        default:
+            LOG_ALWAYS_FATAL("bad mixerInFormat: %#x", mixerInFormat);
+            break;
+        }
+        break;
+    // RESAMPLEMONO needs MIXTYPE_STEREOEXPAND since resampler will upmix mono
+    // track to stereo track
+    case TRACKTYPE_RESAMPLEMONO:
+        switch (mixerInFormat) {
+        case AUDIO_FORMAT_PCM_FLOAT:
+            return (AudioMixerBase::hook_t) &TrackBase::track__Resample<
+                    MIXTYPE_STEREOEXPAND, float /*TO*/, float /*TI*/,
+                    TYPE_AUX>;
+        case AUDIO_FORMAT_PCM_16_BIT:
+            return (AudioMixerBase::hook_t) &TrackBase::track__Resample<
+                    MIXTYPE_STEREOEXPAND, int32_t /*TO*/, int16_t /*TI*/,
+                    TYPE_AUX>;
+        default:
+            LOG_ALWAYS_FATAL("bad mixerInFormat: %#x", mixerInFormat);
+            break;
+        }
+        break;
     case TRACKTYPE_NORESAMPLEMONO:
         switch (mixerInFormat) {
         case AUDIO_FORMAT_PCM_FLOAT:
@@ -1627,6 +1679,21 @@
             break;
         }
         break;
+    case TRACKTYPE_NORESAMPLESTEREO:
+        switch (mixerInFormat) {
+        case AUDIO_FORMAT_PCM_FLOAT:
+            return (AudioMixerBase::hook_t) &TrackBase::track__NoResample<
+                    MIXTYPE_MULTI_STEREOVOL, float /*TO*/, float /*TI*/,
+                    TYPE_AUX>;
+        case AUDIO_FORMAT_PCM_16_BIT:
+            return (AudioMixerBase::hook_t) &TrackBase::track__NoResample<
+                    MIXTYPE_MULTI_STEREOVOL, int32_t /*TO*/, int16_t /*TI*/,
+                    TYPE_AUX>;
+        default:
+            LOG_ALWAYS_FATAL("bad mixerInFormat: %#x", mixerInFormat);
+            break;
+        }
+        break;
     default:
         LOG_ALWAYS_FATAL("bad trackType: %d", trackType);
         break;
@@ -1644,7 +1711,8 @@
 /* static */
 AudioMixerBase::process_hook_t AudioMixerBase::getProcessHook(
         int processType, uint32_t channelCount,
-        audio_format_t mixerInFormat, audio_format_t mixerOutFormat)
+        audio_format_t mixerInFormat, audio_format_t mixerOutFormat,
+        bool stereoVolume)
 {
     if (processType != PROCESSTYPE_NORESAMPLEONETRACK) { // Only NORESAMPLEONETRACK
         LOG_ALWAYS_FATAL("bad processType: %d", processType);
@@ -1654,36 +1722,79 @@
         return &AudioMixerBase::process__oneTrack16BitsStereoNoResampling;
     }
     LOG_ALWAYS_FATAL_IF(channelCount > MAX_NUM_CHANNELS);
-    switch (mixerInFormat) {
-    case AUDIO_FORMAT_PCM_FLOAT:
-        switch (mixerOutFormat) {
+
+    if (stereoVolume) { // templated arguments require explicit values.
+        switch (mixerInFormat) {
         case AUDIO_FORMAT_PCM_FLOAT:
-            return &AudioMixerBase::process__noResampleOneTrack<
-                    MIXTYPE_MULTI_SAVEONLY, float /*TO*/, float /*TI*/, TYPE_AUX>;
+            switch (mixerOutFormat) {
+            case AUDIO_FORMAT_PCM_FLOAT:
+                return &AudioMixerBase::process__noResampleOneTrack<
+                        MIXTYPE_MULTI_SAVEONLY_STEREOVOL, float /*TO*/,
+                        float /*TI*/, TYPE_AUX>;
+            case AUDIO_FORMAT_PCM_16_BIT:
+                return &AudioMixerBase::process__noResampleOneTrack<
+                        MIXTYPE_MULTI_SAVEONLY_STEREOVOL, int16_t /*TO*/,
+                        float /*TI*/, TYPE_AUX>;
+            default:
+                LOG_ALWAYS_FATAL("bad mixerOutFormat: %#x", mixerOutFormat);
+                break;
+            }
+            break;
         case AUDIO_FORMAT_PCM_16_BIT:
-            return &AudioMixerBase::process__noResampleOneTrack<
-                    MIXTYPE_MULTI_SAVEONLY, int16_t /*TO*/, float /*TI*/, TYPE_AUX>;
+            switch (mixerOutFormat) {
+            case AUDIO_FORMAT_PCM_FLOAT:
+                return &AudioMixerBase::process__noResampleOneTrack<
+                        MIXTYPE_MULTI_SAVEONLY_STEREOVOL, float /*TO*/,
+                        int16_t /*TI*/, TYPE_AUX>;
+            case AUDIO_FORMAT_PCM_16_BIT:
+                return &AudioMixerBase::process__noResampleOneTrack<
+                        MIXTYPE_MULTI_SAVEONLY_STEREOVOL, int16_t /*TO*/,
+                        int16_t /*TI*/, TYPE_AUX>;
+            default:
+                LOG_ALWAYS_FATAL("bad mixerOutFormat: %#x", mixerOutFormat);
+                break;
+            }
+            break;
         default:
-            LOG_ALWAYS_FATAL("bad mixerOutFormat: %#x", mixerOutFormat);
+            LOG_ALWAYS_FATAL("bad mixerInFormat: %#x", mixerInFormat);
             break;
         }
-        break;
-    case AUDIO_FORMAT_PCM_16_BIT:
-        switch (mixerOutFormat) {
-        case AUDIO_FORMAT_PCM_FLOAT:
-            return &AudioMixerBase::process__noResampleOneTrack<
-                    MIXTYPE_MULTI_SAVEONLY, float /*TO*/, int16_t /*TI*/, TYPE_AUX>;
-        case AUDIO_FORMAT_PCM_16_BIT:
-            return &AudioMixerBase::process__noResampleOneTrack<
-                    MIXTYPE_MULTI_SAVEONLY, int16_t /*TO*/, int16_t /*TI*/, TYPE_AUX>;
-        default:
-            LOG_ALWAYS_FATAL("bad mixerOutFormat: %#x", mixerOutFormat);
-            break;
-        }
-        break;
-    default:
-        LOG_ALWAYS_FATAL("bad mixerInFormat: %#x", mixerInFormat);
-        break;
+    } else {
+          switch (mixerInFormat) {
+          case AUDIO_FORMAT_PCM_FLOAT:
+              switch (mixerOutFormat) {
+              case AUDIO_FORMAT_PCM_FLOAT:
+                  return &AudioMixerBase::process__noResampleOneTrack<
+                          MIXTYPE_MULTI_SAVEONLY, float /*TO*/,
+                          float /*TI*/, TYPE_AUX>;
+              case AUDIO_FORMAT_PCM_16_BIT:
+                  return &AudioMixerBase::process__noResampleOneTrack<
+                          MIXTYPE_MULTI_SAVEONLY, int16_t /*TO*/,
+                          float /*TI*/, TYPE_AUX>;
+              default:
+                  LOG_ALWAYS_FATAL("bad mixerOutFormat: %#x", mixerOutFormat);
+                  break;
+              }
+              break;
+          case AUDIO_FORMAT_PCM_16_BIT:
+              switch (mixerOutFormat) {
+              case AUDIO_FORMAT_PCM_FLOAT:
+                  return &AudioMixerBase::process__noResampleOneTrack<
+                          MIXTYPE_MULTI_SAVEONLY, float /*TO*/,
+                          int16_t /*TI*/, TYPE_AUX>;
+              case AUDIO_FORMAT_PCM_16_BIT:
+                  return &AudioMixerBase::process__noResampleOneTrack<
+                          MIXTYPE_MULTI_SAVEONLY, int16_t /*TO*/,
+                          int16_t /*TI*/, TYPE_AUX>;
+              default:
+                  LOG_ALWAYS_FATAL("bad mixerOutFormat: %#x", mixerOutFormat);
+                  break;
+              }
+              break;
+          default:
+              LOG_ALWAYS_FATAL("bad mixerInFormat: %#x", mixerInFormat);
+              break;
+          }
     }
     return NULL;
 }
diff --git a/media/libaudioprocessing/AudioMixerOps.h b/media/libaudioprocessing/AudioMixerOps.h
index f33e361..2748182 100644
--- a/media/libaudioprocessing/AudioMixerOps.h
+++ b/media/libaudioprocessing/AudioMixerOps.h
@@ -19,21 +19,10 @@
 
 namespace android {
 
-/* Behavior of is_same<>::value is true if the types are identical,
- * false otherwise. Identical to the STL std::is_same.
- */
-template<typename T, typename U>
-struct is_same
-{
-    static const bool value = false;
-};
-
-template<typename T>
-struct is_same<T, T>  // partial specialization
-{
-    static const bool value = true;
-};
-
+// Hack to make static_assert work in a constexpr
+// https://en.cppreference.com/w/cpp/language/if
+template <int N>
+inline constexpr bool dependent_false = false;
 
 /* MixMul is a multiplication operator to scale an audio input signal
  * by a volume gain, with the formula:
@@ -179,7 +168,7 @@
 
 template <typename TO, typename TI>
 inline void MixAccum(TO *auxaccum, TI value) {
-    if (!is_same<TO, TI>::value) {
+    if (!std::is_same_v<TO, TI>) {
         LOG_ALWAYS_FATAL("MixAccum type not properly specialized: %zu %zu\n",
                 sizeof(TO), sizeof(TI));
     }
@@ -228,9 +217,79 @@
     MIXTYPE_MULTI_SAVEONLY,
     MIXTYPE_MULTI_MONOVOL,
     MIXTYPE_MULTI_SAVEONLY_MONOVOL,
+    MIXTYPE_MULTI_STEREOVOL,
+    MIXTYPE_MULTI_SAVEONLY_STEREOVOL,
+    MIXTYPE_STEREOEXPAND,
 };
 
 /*
+ * TODO: We should work on non-interleaved streams - the
+ * complexity of working on interleaved streams is now getting
+ * too high, and likely limits compiler optimization.
+ */
+template <int MIXTYPE, int NCHAN,
+        typename TO, typename TI, typename TV,
+        typename F>
+void stereoVolumeHelper(TO*& out, const TI*& in, const TV *vol, F f) {
+    static_assert(NCHAN > 0 && NCHAN <= 8);
+    static_assert(MIXTYPE == MIXTYPE_MULTI_STEREOVOL
+            || MIXTYPE == MIXTYPE_MULTI_SAVEONLY_STEREOVOL
+            || MIXTYPE == MIXTYPE_STEREOEXPAND);
+    auto proc = [](auto& a, const auto& b) {
+        if constexpr (MIXTYPE == MIXTYPE_MULTI_STEREOVOL) {
+            a += b;
+        } else {
+            a = b;
+        }
+    };
+    auto inp = [&in]() -> const TI& {
+        if constexpr (MIXTYPE == MIXTYPE_STEREOEXPAND) {
+            return *in;
+        } else {
+            return *in++;
+        }
+    };
+
+    // HALs should only expose the canonical channel masks.
+    proc(*out++, f(inp(), vol[0])); // front left
+    if constexpr (NCHAN == 1) return;
+    proc(*out++, f(inp(), vol[1])); // front right
+    if constexpr (NCHAN == 2)  return;
+    if constexpr (NCHAN == 4) {
+        proc(*out++, f(inp(), vol[0])); // back left
+        proc(*out++, f(inp(), vol[1])); // back right
+        return;
+    }
+
+    // TODO: Precompute center volume if not ramping.
+    std::decay_t<TV> center;
+    if constexpr (std::is_floating_point_v<TV>) {
+        center = (vol[0] + vol[1]) * 0.5;       // do not use divide
+    } else {
+        center = (vol[0] >> 1) + (vol[1] >> 1); // rounds to 0.
+    }
+    proc(*out++, f(inp(), center)); // center (or 2.1 LFE)
+    if constexpr (NCHAN == 3) return;
+    if constexpr (NCHAN == 5) {
+        proc(*out++, f(inp(), vol[0]));  // back left
+        proc(*out++, f(inp(), vol[1]));  // back right
+        return;
+    }
+
+    proc(*out++, f(inp(), center)); // lfe
+    proc(*out++, f(inp(), vol[0])); // back left
+    proc(*out++, f(inp(), vol[1])); // back right
+    if constexpr (NCHAN == 6) return;
+    if constexpr (NCHAN == 7) {
+        proc(*out++, f(inp(), center)); // back center
+        return;
+    }
+    // NCHAN == 8
+    proc(*out++, f(inp(), vol[0])); // side left
+    proc(*out++, f(inp(), vol[1])); // side right
+}
+
+/*
  * The volumeRampMulti and volumeRamp functions take a MIXTYPE
  * which indicates the per-frame mixing and accumulation strategy.
  *
@@ -271,6 +330,17 @@
  * MIXTYPE_MULTI_SAVEONLY_MONOVOL:
  *   Same as MIXTYPE_MULTI_SAVEONLY, but uses only volume[0].
  *
+ * MIXTYPE_MULTI_STEREOVOL:
+ *   Same as MIXTYPE_MULTI, but uses only volume[0] and volume[1].
+ *
+ * MIXTYPE_MULTI_SAVEONLY_STEREOVOL:
+ *   Same as MIXTYPE_MULTI_SAVEONLY, but uses only volume[0] and volume[1].
+ *
+ * MIXTYPE_STEREOEXPAND:
+ *   Stereo input channel. NCHAN represents number of output channels.
+ *   Expand size 2 array "in" and "vol" to multi-channel output. Note
+ *   that the 2 array is assumed to have replicated L+R.
+ *
  */
 
 template <int MIXTYPE, int NCHAN,
@@ -284,41 +354,44 @@
     if (aux != NULL) {
         do {
             TA auxaccum = 0;
-            switch (MIXTYPE) {
-            case MIXTYPE_MULTI:
+            if constexpr (MIXTYPE == MIXTYPE_MULTI) {
                 for (int i = 0; i < NCHAN; ++i) {
                     *out++ += MixMulAux<TO, TI, TV, TA>(*in++, vol[i], &auxaccum);
                     vol[i] += volinc[i];
                 }
-                break;
-            case MIXTYPE_MONOEXPAND:
+            } else if constexpr (MIXTYPE == MIXTYPE_MONOEXPAND) {
                 for (int i = 0; i < NCHAN; ++i) {
                     *out++ += MixMulAux<TO, TI, TV, TA>(*in, vol[i], &auxaccum);
                     vol[i] += volinc[i];
                 }
                 in++;
-                break;
-            case MIXTYPE_MULTI_SAVEONLY:
+            } else if constexpr (MIXTYPE == MIXTYPE_MULTI_SAVEONLY) {
                 for (int i = 0; i < NCHAN; ++i) {
                     *out++ = MixMulAux<TO, TI, TV, TA>(*in++, vol[i], &auxaccum);
                     vol[i] += volinc[i];
                 }
-                break;
-            case MIXTYPE_MULTI_MONOVOL:
+            } else if constexpr (MIXTYPE == MIXTYPE_MULTI_MONOVOL) {
                 for (int i = 0; i < NCHAN; ++i) {
                     *out++ += MixMulAux<TO, TI, TV, TA>(*in++, vol[0], &auxaccum);
                 }
                 vol[0] += volinc[0];
-                break;
-            case MIXTYPE_MULTI_SAVEONLY_MONOVOL:
+            } else if constexpr (MIXTYPE == MIXTYPE_MULTI_SAVEONLY_MONOVOL) {
                 for (int i = 0; i < NCHAN; ++i) {
                     *out++ = MixMulAux<TO, TI, TV, TA>(*in++, vol[0], &auxaccum);
                 }
                 vol[0] += volinc[0];
-                break;
-            default:
-                LOG_ALWAYS_FATAL("invalid mixtype %d", MIXTYPE);
-                break;
+            } else if constexpr (MIXTYPE == MIXTYPE_MULTI_STEREOVOL
+                    || MIXTYPE == MIXTYPE_MULTI_SAVEONLY_STEREOVOL
+                    || MIXTYPE == MIXTYPE_STEREOEXPAND) {
+                stereoVolumeHelper<MIXTYPE, NCHAN>(
+                        out, in, vol, [&auxaccum] (auto &a, const auto &b) {
+                    return MixMulAux<TO, TI, TV, TA>(a, b, &auxaccum);
+                });
+                if constexpr (MIXTYPE == MIXTYPE_STEREOEXPAND) in += 2;
+                vol[0] += volinc[0];
+                vol[1] += volinc[1];
+            } else /* constexpr */ {
+                static_assert(dependent_false<MIXTYPE>, "invalid mixtype");
             }
             auxaccum /= NCHAN;
             *aux++ += MixMul<TA, TA, TAV>(auxaccum, *vola);
@@ -326,41 +399,43 @@
         } while (--frameCount);
     } else {
         do {
-            switch (MIXTYPE) {
-            case MIXTYPE_MULTI:
+            if constexpr (MIXTYPE == MIXTYPE_MULTI) {
                 for (int i = 0; i < NCHAN; ++i) {
                     *out++ += MixMul<TO, TI, TV>(*in++, vol[i]);
                     vol[i] += volinc[i];
                 }
-                break;
-            case MIXTYPE_MONOEXPAND:
+            } else if constexpr (MIXTYPE == MIXTYPE_MONOEXPAND) {
                 for (int i = 0; i < NCHAN; ++i) {
                     *out++ += MixMul<TO, TI, TV>(*in, vol[i]);
                     vol[i] += volinc[i];
                 }
                 in++;
-                break;
-            case MIXTYPE_MULTI_SAVEONLY:
+            } else if constexpr (MIXTYPE == MIXTYPE_MULTI_SAVEONLY) {
                 for (int i = 0; i < NCHAN; ++i) {
                     *out++ = MixMul<TO, TI, TV>(*in++, vol[i]);
                     vol[i] += volinc[i];
                 }
-                break;
-            case MIXTYPE_MULTI_MONOVOL:
+            } else if constexpr (MIXTYPE == MIXTYPE_MULTI_MONOVOL) {
                 for (int i = 0; i < NCHAN; ++i) {
                     *out++ += MixMul<TO, TI, TV>(*in++, vol[0]);
                 }
                 vol[0] += volinc[0];
-                break;
-            case MIXTYPE_MULTI_SAVEONLY_MONOVOL:
+            } else if constexpr (MIXTYPE == MIXTYPE_MULTI_SAVEONLY_MONOVOL) {
                 for (int i = 0; i < NCHAN; ++i) {
                     *out++ = MixMul<TO, TI, TV>(*in++, vol[0]);
                 }
                 vol[0] += volinc[0];
-                break;
-            default:
-                LOG_ALWAYS_FATAL("invalid mixtype %d", MIXTYPE);
-                break;
+            } else if constexpr (MIXTYPE == MIXTYPE_MULTI_STEREOVOL
+                    || MIXTYPE == MIXTYPE_MULTI_SAVEONLY_STEREOVOL
+                    || MIXTYPE == MIXTYPE_STEREOEXPAND) {
+                stereoVolumeHelper<MIXTYPE, NCHAN>(out, in, vol, [] (auto &a, const auto &b) {
+                    return MixMul<TO, TI, TV>(a, b);
+                });
+                if constexpr (MIXTYPE == MIXTYPE_STEREOEXPAND) in += 2;
+                vol[0] += volinc[0];
+                vol[1] += volinc[1];
+            } else /* constexpr */ {
+                static_assert(dependent_false<MIXTYPE>, "invalid mixtype");
             }
         } while (--frameCount);
     }
@@ -377,72 +452,73 @@
     if (aux != NULL) {
         do {
             TA auxaccum = 0;
-            switch (MIXTYPE) {
-            case MIXTYPE_MULTI:
+            if constexpr (MIXTYPE == MIXTYPE_MULTI) {
                 for (int i = 0; i < NCHAN; ++i) {
                     *out++ += MixMulAux<TO, TI, TV, TA>(*in++, vol[i], &auxaccum);
                 }
-                break;
-            case MIXTYPE_MONOEXPAND:
+            } else if constexpr (MIXTYPE == MIXTYPE_MONOEXPAND) {
                 for (int i = 0; i < NCHAN; ++i) {
                     *out++ += MixMulAux<TO, TI, TV, TA>(*in, vol[i], &auxaccum);
                 }
                 in++;
-                break;
-            case MIXTYPE_MULTI_SAVEONLY:
+            } else if constexpr (MIXTYPE == MIXTYPE_MULTI_SAVEONLY) {
                 for (int i = 0; i < NCHAN; ++i) {
                     *out++ = MixMulAux<TO, TI, TV, TA>(*in++, vol[i], &auxaccum);
                 }
-                break;
-            case MIXTYPE_MULTI_MONOVOL:
+            } else if constexpr (MIXTYPE == MIXTYPE_MULTI_MONOVOL) {
                 for (int i = 0; i < NCHAN; ++i) {
                     *out++ += MixMulAux<TO, TI, TV, TA>(*in++, vol[0], &auxaccum);
                 }
-                break;
-            case MIXTYPE_MULTI_SAVEONLY_MONOVOL:
+            } else if constexpr (MIXTYPE == MIXTYPE_MULTI_SAVEONLY_MONOVOL) {
                 for (int i = 0; i < NCHAN; ++i) {
                     *out++ = MixMulAux<TO, TI, TV, TA>(*in++, vol[0], &auxaccum);
                 }
-                break;
-            default:
-                LOG_ALWAYS_FATAL("invalid mixtype %d", MIXTYPE);
-                break;
+            } else if constexpr (MIXTYPE == MIXTYPE_MULTI_STEREOVOL
+                    || MIXTYPE == MIXTYPE_MULTI_SAVEONLY_STEREOVOL
+                    || MIXTYPE == MIXTYPE_STEREOEXPAND) {
+                stereoVolumeHelper<MIXTYPE, NCHAN>(
+                        out, in, vol, [&auxaccum] (auto &a, const auto &b) {
+                    return MixMulAux<TO, TI, TV, TA>(a, b, &auxaccum);
+                });
+                if constexpr (MIXTYPE == MIXTYPE_STEREOEXPAND) in += 2;
+            } else /* constexpr */ {
+                static_assert(dependent_false<MIXTYPE>, "invalid mixtype");
             }
             auxaccum /= NCHAN;
             *aux++ += MixMul<TA, TA, TAV>(auxaccum, vola);
         } while (--frameCount);
     } else {
         do {
-            switch (MIXTYPE) {
-            case MIXTYPE_MULTI:
+            if constexpr (MIXTYPE == MIXTYPE_MULTI) {
                 for (int i = 0; i < NCHAN; ++i) {
                     *out++ += MixMul<TO, TI, TV>(*in++, vol[i]);
                 }
-                break;
-            case MIXTYPE_MONOEXPAND:
+            } else if constexpr (MIXTYPE == MIXTYPE_MONOEXPAND) {
                 for (int i = 0; i < NCHAN; ++i) {
                     *out++ += MixMul<TO, TI, TV>(*in, vol[i]);
                 }
                 in++;
-                break;
-            case MIXTYPE_MULTI_SAVEONLY:
+            } else if constexpr (MIXTYPE == MIXTYPE_MULTI_SAVEONLY) {
                 for (int i = 0; i < NCHAN; ++i) {
                     *out++ = MixMul<TO, TI, TV>(*in++, vol[i]);
                 }
-                break;
-            case MIXTYPE_MULTI_MONOVOL:
+            } else if constexpr (MIXTYPE == MIXTYPE_MULTI_MONOVOL) {
                 for (int i = 0; i < NCHAN; ++i) {
                     *out++ += MixMul<TO, TI, TV>(*in++, vol[0]);
                 }
-                break;
-            case MIXTYPE_MULTI_SAVEONLY_MONOVOL:
+            } else if constexpr (MIXTYPE == MIXTYPE_MULTI_SAVEONLY_MONOVOL) {
                 for (int i = 0; i < NCHAN; ++i) {
                     *out++ = MixMul<TO, TI, TV>(*in++, vol[0]);
                 }
-                break;
-            default:
-                LOG_ALWAYS_FATAL("invalid mixtype %d", MIXTYPE);
-                break;
+            } else if constexpr (MIXTYPE == MIXTYPE_MULTI_STEREOVOL
+                    || MIXTYPE == MIXTYPE_MULTI_SAVEONLY_STEREOVOL
+                    || MIXTYPE == MIXTYPE_STEREOEXPAND) {
+                stereoVolumeHelper<MIXTYPE, NCHAN>(out, in, vol, [] (auto &a, const auto &b) {
+                    return MixMul<TO, TI, TV>(a, b);
+                });
+                if constexpr (MIXTYPE == MIXTYPE_STEREOEXPAND) in += 2;
+            } else /* constexpr */ {
+                static_assert(dependent_false<MIXTYPE>, "invalid mixtype");
             }
         } while (--frameCount);
     }
diff --git a/media/libaudioprocessing/AudioResamplerFirOps.h b/media/libaudioprocessing/AudioResamplerFirOps.h
index 2e4cee3..a3f5ff5 100644
--- a/media/libaudioprocessing/AudioResamplerFirOps.h
+++ b/media/libaudioprocessing/AudioResamplerFirOps.h
@@ -36,13 +36,20 @@
 #include <arm_neon.h>
 #endif
 
-#if defined(__SSSE3__)  // Should be supported in x86 ABI for both 32 & 64-bit.
+#if defined(__AVX2__)  // Should be supported in x86 ABI for both 32 & 64-bit.
+#define USE_AVX2 (true)  // Inference AVX2/FMA Intrinsics
 #define USE_SSE (true)
+#include <immintrin.h>
+#elif defined(__SSSE3__)  // Should be supported in x86 ABI for both 32 & 64-bit.
+#define USE_SSE (true)  // Inference SSE Intrinsics
+#define USE_AVX2 (false)
 #include <tmmintrin.h>
 #else
 #define USE_SSE (false)
+#define USE_AVX2(false)
 #endif
 
+
 template<typename T, typename U>
 struct is_same
 {
diff --git a/media/libaudioprocessing/AudioResamplerFirProcessSSE.h b/media/libaudioprocessing/AudioResamplerFirProcessSSE.h
index 30233b5..1c16bc4 100644
--- a/media/libaudioprocessing/AudioResamplerFirProcessSSE.h
+++ b/media/libaudioprocessing/AudioResamplerFirProcessSSE.h
@@ -80,11 +80,16 @@
             posCoef1 = _mm_sub_ps(posCoef1, posCoef);
             negCoef = _mm_sub_ps(negCoef, negCoef1);
 
+
+            #if USE_AVX2
+            posCoef = _mm_fmadd_ps(posCoef1, interp, posCoef);
+            negCoef = _mm_fmadd_ps(negCoef, interp, negCoef1);
+            #else
             posCoef1 = _mm_mul_ps(posCoef1, interp);
             negCoef = _mm_mul_ps(negCoef, interp);
-
             posCoef = _mm_add_ps(posCoef1, posCoef);
             negCoef = _mm_add_ps(negCoef, negCoef1);
+            #endif //USE_AVX2
         }
         switch (CHANNELS) {
         case 1: {
@@ -94,11 +99,17 @@
             sN += 4;
 
             posSamp = _mm_shuffle_ps(posSamp, posSamp, 0x1B);
+
+            #if USE_AVX2
+            accL = _mm_fmadd_ps(posSamp, posCoef, accL);
+            accL = _mm_fmadd_ps(negSamp, negCoef, accL);
+            #else
             posSamp = _mm_mul_ps(posSamp, posCoef);
             negSamp = _mm_mul_ps(negSamp, negCoef);
-
             accL = _mm_add_ps(accL, posSamp);
             accL = _mm_add_ps(accL, negSamp);
+            #endif
+
         } break;
         case 2: {
             __m128 posSamp0 = _mm_loadu_ps(sP);
@@ -114,15 +125,23 @@
             __m128 negSampL = _mm_shuffle_ps(negSamp0, negSamp1, 0x88);
             __m128 negSampR = _mm_shuffle_ps(negSamp0, negSamp1, 0xDD);
 
-            posSampL = _mm_mul_ps(posSampL, posCoef);
-            posSampR = _mm_mul_ps(posSampR, posCoef);
-            negSampL = _mm_mul_ps(negSampL, negCoef);
-            negSampR = _mm_mul_ps(negSampR, negCoef);
+           #if USE_AVX2
+           accL = _mm_fmadd_ps(posSampL, posCoef, accL);
+           accR = _mm_fmadd_ps(posSampR, posCoef, accR);
+           accL = _mm_fmadd_ps(negSampL, negCoef, accL);
+           accR = _mm_fmadd_ps(negSampR, negCoef, accR);
+           #else
+           posSampL = _mm_mul_ps(posSampL, posCoef);
+           posSampR = _mm_mul_ps(posSampR, posCoef);
+           negSampL = _mm_mul_ps(negSampL, negCoef);
+           negSampR = _mm_mul_ps(negSampR, negCoef);
 
-            accL = _mm_add_ps(accL, posSampL);
-            accR = _mm_add_ps(accR, posSampR);
-            accL = _mm_add_ps(accL, negSampL);
-            accR = _mm_add_ps(accR, negSampR);
+           accL = _mm_add_ps(accL, posSampL);
+           accR = _mm_add_ps(accR, posSampR);
+           accL = _mm_add_ps(accL, negSampL);
+           accR = _mm_add_ps(accR, negSampR);
+           #endif
+
         } break;
         }
     } while (count -= 4);
@@ -144,9 +163,13 @@
         outAccum = _mm_hadd_ps(accL, accR);
         outAccum = _mm_hadd_ps(outAccum, outAccum);
     }
-
+    #if USE_AVX2
+    outSamp = _mm_fmadd_ps(outAccum, vLR,outSamp);
+    #else
     outAccum = _mm_mul_ps(outAccum, vLR);
     outSamp = _mm_add_ps(outSamp, outAccum);
+    #endif
+
     _mm_storel_pi(reinterpret_cast<__m64*>(out), outSamp);
 }
 
diff --git a/media/libaudioprocessing/include/media/AudioMixerBase.h b/media/libaudioprocessing/include/media/AudioMixerBase.h
index 805b6d0..cf84b83 100644
--- a/media/libaudioprocessing/include/media/AudioMixerBase.h
+++ b/media/libaudioprocessing/include/media/AudioMixerBase.h
@@ -188,13 +188,21 @@
     enum {
         TRACKTYPE_NOP,
         TRACKTYPE_RESAMPLE,
+        TRACKTYPE_RESAMPLEMONO,
+        TRACKTYPE_RESAMPLESTEREO,
         TRACKTYPE_NORESAMPLE,
         TRACKTYPE_NORESAMPLEMONO,
+        TRACKTYPE_NORESAMPLESTEREO,
     };
 
     // process hook functionality
     using process_hook_t = void(AudioMixerBase::*)();
 
+    static bool isAudioChannelPositionMask(audio_channel_mask_t channelMask) {
+        return audio_channel_mask_get_representation(channelMask)
+                == AUDIO_CHANNEL_REPRESENTATION_POSITION;
+    }
+
     struct TrackBase;
     using hook_t = void(TrackBase::*)(
             int32_t* output, size_t numOutFrames, int32_t* temp, int32_t* aux);
@@ -219,6 +227,9 @@
         size_t      getUnreleasedFrames() const { return mResampler.get() != nullptr ?
                                                     mResampler->getUnreleasedFrames() : 0; };
 
+        bool        useStereoVolume() const { return channelMask == AUDIO_CHANNEL_OUT_STEREO
+                                        && isAudioChannelPositionMask(mMixerChannelMask); }
+
         static hook_t getTrackHook(int trackType, uint32_t channelCount,
                 audio_format_t mixerInFormat, audio_format_t mixerOutFormat);
 
@@ -327,7 +338,8 @@
     void process__noResampleOneTrack();
 
     static process_hook_t getProcessHook(int processType, uint32_t channelCount,
-            audio_format_t mixerInFormat, audio_format_t mixerOutFormat);
+            audio_format_t mixerInFormat, audio_format_t mixerOutFormat,
+            bool useStereoVolume);
 
     static void convertMixerFormat(void *out, audio_format_t mixerOutFormat,
             void *in, audio_format_t mixerInFormat, size_t sampleCount);
diff --git a/media/libaudioprocessing/tests/Android.bp b/media/libaudioprocessing/tests/Android.bp
index 20c2c2c..18acef7 100644
--- a/media/libaudioprocessing/tests/Android.bp
+++ b/media/libaudioprocessing/tests/Android.bp
@@ -55,3 +55,26 @@
     srcs: ["test-resampler.cpp"],
     static_libs: ["libsndfile"],
 }
+
+//
+// build mixerops objdump
+//
+// This is used to verify proper optimization of the code.
+//
+// For example, use:
+// ./prebuilts/gcc/linux-x86/aarch64/aarch64-linux-android-4.9/bin/aarch64-linux-android-objdump
+//      -d --source ./out/target/product/crosshatch/symbols/system/bin/mixerops_objdump
+//
+cc_binary {
+    name: "mixerops_objdump",
+    srcs: ["mixerops_objdump.cpp"],
+}
+
+//
+// build mixerops benchmark
+//
+cc_benchmark {
+    name: "mixerops_benchmark",
+    srcs: ["mixerops_benchmark.cpp"],
+    static_libs: ["libgoogle-benchmark"],
+}
diff --git a/media/libaudioprocessing/tests/mixerops_benchmark.cpp b/media/libaudioprocessing/tests/mixerops_benchmark.cpp
new file mode 100644
index 0000000..86f5429
--- /dev/null
+++ b/media/libaudioprocessing/tests/mixerops_benchmark.cpp
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <inttypes.h>
+#include <type_traits>
+#include "../../../../system/media/audio_utils/include/audio_utils/primitives.h"
+#define LOG_ALWAYS_FATAL(...)
+
+#include <../AudioMixerOps.h>
+
+#include <benchmark/benchmark.h>
+
+using namespace android;
+
+template <int MIXTYPE, int NCHAN>
+static void BM_VolumeRampMulti(benchmark::State& state) {
+    constexpr size_t FRAME_COUNT = 1000;
+    constexpr size_t SAMPLE_COUNT = FRAME_COUNT * NCHAN;
+
+    // data inialized to 0.
+    float out[SAMPLE_COUNT]{};
+    float in[SAMPLE_COUNT]{};
+    float aux[FRAME_COUNT]{};
+
+    // volume initialized to 0
+    float vola = 0.f;
+    float vol[2] = {0.f, 0.f};
+
+    // some volume increment
+    float volainc = 0.01f;
+    float volinc[2] = {0.01f, 0.01f};
+
+    while (state.KeepRunning()) {
+        benchmark::DoNotOptimize(out);
+        benchmark::DoNotOptimize(in);
+        volumeRampMulti<MIXTYPE, NCHAN>(out, FRAME_COUNT, in, aux, vol, volinc, &vola, volainc);
+        benchmark::ClobberMemory();
+    }
+}
+
+template <int MIXTYPE, int NCHAN>
+static void BM_VolumeMulti(benchmark::State& state) {
+    constexpr size_t FRAME_COUNT = 1000;
+    constexpr size_t SAMPLE_COUNT = FRAME_COUNT * NCHAN;
+
+    // data inialized to 0.
+    float out[SAMPLE_COUNT]{};
+    float in[SAMPLE_COUNT]{};
+    float aux[FRAME_COUNT]{};
+
+    // volume initialized to 0
+    float vola = 0.f;
+    float vol[2] = {0.f, 0.f};
+
+
+    while (state.KeepRunning()) {
+        benchmark::DoNotOptimize(out);
+        benchmark::DoNotOptimize(in);
+        volumeMulti<MIXTYPE, NCHAN>(out, FRAME_COUNT, in, aux, vol, vola);
+        benchmark::ClobberMemory();
+    }
+}
+
+BENCHMARK_TEMPLATE(BM_VolumeRampMulti, MIXTYPE_MULTI, 2);
+BENCHMARK_TEMPLATE(BM_VolumeRampMulti, MIXTYPE_MULTI_SAVEONLY, 2);
+BENCHMARK_TEMPLATE(BM_VolumeRampMulti, MIXTYPE_MULTI_STEREOVOL, 2);
+BENCHMARK_TEMPLATE(BM_VolumeRampMulti, MIXTYPE_MULTI_SAVEONLY_STEREOVOL, 2);
+
+BENCHMARK_TEMPLATE(BM_VolumeRampMulti, MIXTYPE_MULTI, 4);
+BENCHMARK_TEMPLATE(BM_VolumeRampMulti, MIXTYPE_MULTI_SAVEONLY, 4);
+BENCHMARK_TEMPLATE(BM_VolumeRampMulti, MIXTYPE_MULTI_STEREOVOL, 4);
+BENCHMARK_TEMPLATE(BM_VolumeRampMulti, MIXTYPE_MULTI_SAVEONLY_STEREOVOL, 4);
+
+BENCHMARK_TEMPLATE(BM_VolumeRampMulti, MIXTYPE_MULTI, 5);
+BENCHMARK_TEMPLATE(BM_VolumeRampMulti, MIXTYPE_MULTI_SAVEONLY, 5);
+BENCHMARK_TEMPLATE(BM_VolumeRampMulti, MIXTYPE_MULTI_STEREOVOL, 5);
+BENCHMARK_TEMPLATE(BM_VolumeRampMulti, MIXTYPE_MULTI_SAVEONLY_STEREOVOL, 5);
+
+BENCHMARK_TEMPLATE(BM_VolumeRampMulti, MIXTYPE_MULTI, 8);
+BENCHMARK_TEMPLATE(BM_VolumeRampMulti, MIXTYPE_MULTI_SAVEONLY, 8);
+BENCHMARK_TEMPLATE(BM_VolumeRampMulti, MIXTYPE_MULTI_STEREOVOL, 8);
+BENCHMARK_TEMPLATE(BM_VolumeRampMulti, MIXTYPE_MULTI_SAVEONLY_STEREOVOL, 8);
+
+BENCHMARK_TEMPLATE(BM_VolumeMulti, MIXTYPE_MULTI, 8);
+BENCHMARK_TEMPLATE(BM_VolumeMulti, MIXTYPE_MULTI_SAVEONLY, 8);
+BENCHMARK_TEMPLATE(BM_VolumeMulti, MIXTYPE_MULTI_STEREOVOL, 8);
+BENCHMARK_TEMPLATE(BM_VolumeMulti, MIXTYPE_MULTI_SAVEONLY_STEREOVOL, 8);
+
+BENCHMARK_MAIN();
diff --git a/media/libaudioprocessing/tests/mixerops_objdump.cpp b/media/libaudioprocessing/tests/mixerops_objdump.cpp
new file mode 100644
index 0000000..ee7e837
--- /dev/null
+++ b/media/libaudioprocessing/tests/mixerops_objdump.cpp
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <inttypes.h>
+#include <type_traits>
+#include "../../../../system/media/audio_utils/include/audio_utils/primitives.h"
+#define LOG_ALWAYS_FATAL(...)
+
+#include <../AudioMixerOps.h>
+
+using namespace android;
+
+template <int MIXTYPE, int NCHAN>
+static void checkVolumeRampMulti() {
+    constexpr size_t FRAME_COUNT = 1000;
+    constexpr size_t SAMPLE_COUNT = FRAME_COUNT * NCHAN;
+
+    // data inialized to 0.
+    float out[SAMPLE_COUNT]{};
+    float in[SAMPLE_COUNT]{};
+    float aux[FRAME_COUNT]{};
+
+    // volume initialized to 0
+    float vola = 0.f;
+    float vol[2] = {0.f, 0.f};
+
+    // some volume increment
+    float volainc = 0.01f;
+    float volinc[2] = {0.01f, 0.01f};
+
+    // try the multi ramp code.
+    volumeRampMulti<MIXTYPE, NCHAN>(out, FRAME_COUNT, in, aux, vol, volinc, &vola, volainc);
+}
+
+// Use this to check the objdump to ensure reasonable code.
+int main() {
+    checkVolumeRampMulti<MIXTYPE_MULTI_STEREOVOL, 5>();
+    return EXIT_SUCCESS;
+}
diff --git a/media/libstagefright/MediaExtractorFactory.cpp b/media/libstagefright/MediaExtractorFactory.cpp
index 44dbfe9..94267a1 100644
--- a/media/libstagefright/MediaExtractorFactory.cpp
+++ b/media/libstagefright/MediaExtractorFactory.cpp
@@ -280,7 +280,7 @@
 
     std::shared_ptr<std::list<sp<ExtractorPlugin>>> newList(new std::list<sp<ExtractorPlugin>>());
 
-    android_namespace_t *mediaNs = android_get_exported_namespace("com.android.media");
+    android_namespace_t *mediaNs = android_get_exported_namespace("com_android_media");
     if (mediaNs != NULL) {
         const android_dlextinfo dlextinfo = {
             .flags = ANDROID_DLEXT_USE_NAMESPACE,
diff --git a/media/libstagefright/TEST_MAPPING b/media/libstagefright/TEST_MAPPING
index fac9511..8b36ea5 100644
--- a/media/libstagefright/TEST_MAPPING
+++ b/media/libstagefright/TEST_MAPPING
@@ -1,7 +1,6 @@
 {
-  "postsubmit": [
+  "presubmit": [
     {
-      // TODO: move to presubmit once we verify the tests are not flaky
       "name": "CtsMediaTestCases",
       "options": [
         {
@@ -13,9 +12,14 @@
         // TODO: b/149314419
         {
           "exclude-filter": "android.media.cts.AudioPlaybackCaptureTest"
+        },
+        {
+          "exclude-filter": "android.media.cts.AudioRecordTest"
         }
       ]
-    },
+    }
+  ],
+  "postsubmit": [
     {
        "name": "BatteryChecker_test"
     }
diff --git a/media/libstagefright/codecs/m4v_h263/enc/test/Android.bp b/media/libstagefright/codecs/m4v_h263/enc/test/Android.bp
new file mode 100644
index 0000000..b9a8117
--- /dev/null
+++ b/media/libstagefright/codecs/m4v_h263/enc/test/Android.bp
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+cc_test {
+    name: "Mpeg4H263EncoderTest",
+    gtest: true,
+
+    srcs : [ "Mpeg4H263EncoderTest.cpp" ],
+
+    shared_libs: [
+        "libutils",
+        "liblog",
+    ],
+
+    static_libs: [
+        "libstagefright_m4vh263enc",
+    ],
+
+    cflags: [
+        "-DOSCL_IMPORT_REF=",
+        "-Wall",
+        "-Werror",
+    ],
+
+    sanitize: {
+        misc_undefined: [
+            "signed-integer-overflow",
+            "unsigned-integer-overflow",
+        ],
+        cfi: true,
+    },
+}
diff --git a/media/libstagefright/codecs/m4v_h263/enc/test/AndroidTest.xml b/media/libstagefright/codecs/m4v_h263/enc/test/AndroidTest.xml
new file mode 100644
index 0000000..5218932
--- /dev/null
+++ b/media/libstagefright/codecs/m4v_h263/enc/test/AndroidTest.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<configuration description="Test module config for MPEG4H263 encoder unit tests">
+    <option name="test-suite-tag" value="Mpeg4H263EncoderTest" />
+    <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+        <option name="cleanup" value="true" />
+        <option name="push" value="Mpeg4H263EncoderTest->/data/local/tmp/Mpeg4H263EncoderTest/" />
+        <option name="push-file"
+            key="https://storage.googleapis.com/android_media/frameworks/av/media/libstagefright/codecs/m4v_h263/enc/test/Mpeg4H263Encoder.zip?unzip=true"
+            value="/data/local/tmp/Mpeg4H263EncoderTestRes/" />
+    </target_preparer>
+
+    <test class="com.android.tradefed.testtype.GTest" >
+        <option name="native-test-device-path" value="/data/local/tmp" />
+        <option name="module-name" value="Mpeg4H263EncoderTest" />
+        <option name="native-test-flag" value="-P /data/local/tmp/Mpeg4H263EncoderTestRes/" />
+    </test>
+</configuration>
\ No newline at end of file
diff --git a/media/libstagefright/codecs/m4v_h263/enc/test/Mpeg4H263EncoderTest.cpp b/media/libstagefright/codecs/m4v_h263/enc/test/Mpeg4H263EncoderTest.cpp
new file mode 100644
index 0000000..78c705a
--- /dev/null
+++ b/media/libstagefright/codecs/m4v_h263/enc/test/Mpeg4H263EncoderTest.cpp
@@ -0,0 +1,250 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "Mpeg4H263EncoderTest"
+#include <utils/Log.h>
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+
+#include "mp4enc_api.h"
+
+#include "Mpeg4H263EncoderTestEnvironment.h"
+
+#define ENCODED_FILE "/data/local/tmp/Mpeg4H263Output"
+
+// assuming a worst case compression of 2X
+constexpr int16_t kCompressionRatio = 2;
+constexpr int8_t kIDRFrameRefreshIntervalInSec = 1;
+
+static Mpeg4H263EncoderTestEnvironment *gEnv = nullptr;
+
+class Mpeg4H263EncoderTest
+    : public ::testing::TestWithParam<tuple<string, bool, int32_t, int32_t, float, int32_t>> {
+  private:
+    void initEncoderParams();
+
+  public:
+    Mpeg4H263EncoderTest()
+        : mInputBuffer(nullptr),
+          mOutputBuffer(nullptr),
+          mFpInput(nullptr),
+          mFpOutput(nullptr),
+          mEncodeHandle(nullptr),
+          mEncodeControl(nullptr) {}
+
+    ~Mpeg4H263EncoderTest() {
+        if(mFpInput) {
+            fclose(mFpInput);
+        }
+        if(mFpOutput) {
+            fclose(mFpOutput);
+        }
+        if(mInputBuffer) free(mInputBuffer);
+        if(mOutputBuffer) free(mOutputBuffer);
+        if(mEncodeHandle) free(mEncodeHandle);
+        if(mEncodeControl) free(mEncodeControl);
+    }
+
+    void SetUp() override {
+        tuple<string /* fileName */, bool /* isMpeg4 */, int32_t /* frameWidth */,
+          int32_t /* frameHeight */, float /* frameRate */, int32_t /* bitRate */>
+            params = GetParam();
+        mFileName = gEnv->getRes() + get<0>(params);
+        mIsMpeg4 = get<1>(params);
+        mFrameWidth = get<2>(params);
+        mFrameHeight = get<3>(params);
+        mFrameRate = get<4>(params);
+        mBitRate = get<5>(params);
+
+        ASSERT_TRUE(mFrameWidth % 16 == 0) << "Frame Width should be multiple of 16";
+        ASSERT_TRUE(mFrameHeight % 16 == 0) << "Frame Height should be multiple of 16";
+        ASSERT_LE(mFrameWidth, (mIsMpeg4 ? 720 : 352))
+                << "Frame Width <= 720 for Mpeg4 and <= 352 for H263";
+        ASSERT_LE(mFrameHeight, (mIsMpeg4 ? 480 : 288))
+                << "Frame Height <= 480 for Mpeg4 and <= 288 for H263";
+        ASSERT_LE(mFrameRate, 30) << "Frame rate less than or equal to 30";
+        ASSERT_LE(mBitRate, 2048) << "Bit rate less than or equal to 2048 kbps";
+
+        mOutputBufferSize = ( mFrameWidth * mFrameHeight * 3/2 ) / kCompressionRatio;
+        mEncodeHandle = new VideoEncOptions;
+        ASSERT_NE(mEncodeHandle, nullptr) << "Failed to get Video Encoding options object";
+        memset(mEncodeHandle, 0, sizeof(VideoEncOptions));
+        mEncodeControl = new VideoEncControls;
+        ASSERT_NE(mEncodeControl, nullptr) << "Failed to get Video Encoding control object";
+        memset(mEncodeControl, 0, sizeof(VideoEncControls));
+        ASSERT_NO_FATAL_FAILURE(initEncoderParams())
+                << "Failed to get the default Encoding parameters!";
+    }
+
+    int64_t getTotalFrames();
+    void processEncoder(int32_t);
+    bool mIsMpeg4;
+    int32_t mFrameWidth, mFrameHeight, mBitRate;
+    int64_t mOutputBufferSize;
+    float mFrameRate;
+    string mFileName;
+    uint8_t *mInputBuffer, *mOutputBuffer;
+    FILE *mFpInput, *mFpOutput;
+    VideoEncOptions *mEncodeHandle;
+    VideoEncControls *mEncodeControl;
+};
+
+void Mpeg4H263EncoderTest::initEncoderParams() {
+    bool status = PVGetDefaultEncOption(mEncodeHandle, 0);
+    ASSERT_TRUE(status);
+
+    mEncodeHandle->rcType = VBR_1;
+    mEncodeHandle->vbvDelay = 5.0f;
+    mEncodeHandle->profile_level = CORE_PROFILE_LEVEL2;
+    mEncodeHandle->packetSize = 32;
+    mEncodeHandle->rvlcEnable = PV_OFF;
+    mEncodeHandle->numLayers = 1;
+    mEncodeHandle->timeIncRes = 1000;
+    mEncodeHandle->iQuant[0] = 15;
+    mEncodeHandle->pQuant[0] = 12;
+    mEncodeHandle->quantType[0] = 0;
+    mEncodeHandle->noFrameSkipped = PV_OFF;
+    mEncodeHandle->numIntraMB = 0;
+    mEncodeHandle->sceneDetect = PV_ON;
+    mEncodeHandle->searchRange = 16;
+    mEncodeHandle->mv8x8Enable = PV_OFF;
+    mEncodeHandle->gobHeaderInterval = 0;
+    mEncodeHandle->useACPred = PV_ON;
+    mEncodeHandle->intraDCVlcTh = 0;
+    if(!mIsMpeg4) {
+        mEncodeHandle->encMode = H263_MODE;
+    } else {
+        mEncodeHandle->encMode = COMBINE_MODE_WITH_ERR_RES;
+    }
+    mEncodeHandle->encWidth[0] = mFrameWidth;
+    mEncodeHandle->encHeight[0] = mFrameHeight;
+    mEncodeHandle->encFrameRate[0] = mFrameRate;
+    mEncodeHandle->bitRate[0] = mBitRate * 1024;
+    mEncodeHandle->tickPerSrc = mEncodeHandle->timeIncRes / mFrameRate;
+    if (kIDRFrameRefreshIntervalInSec == 0) {
+        // All I frames.
+        mEncodeHandle->intraPeriod = 1;
+    } else {
+        mEncodeHandle->intraPeriod = (kIDRFrameRefreshIntervalInSec * mFrameRate);
+    }
+}
+
+int64_t Mpeg4H263EncoderTest::getTotalFrames() {
+    int32_t frameSize = (mFrameWidth * mFrameHeight * 3) / 2;
+    struct stat buf;
+    stat(mFileName.c_str(), &buf);
+    size_t fileSize = buf.st_size;
+    int64_t totalFrames = (int64_t)(fileSize/frameSize);
+    return totalFrames;
+}
+
+void Mpeg4H263EncoderTest::processEncoder(int32_t numFramesToEncode) {
+    bool status;
+    int64_t numEncodedFrames = 0;
+    int32_t bytesRead;
+    int32_t frameSize = (mFrameWidth * mFrameHeight * 3) / 2;
+    while(numFramesToEncode != 0) {
+        bytesRead = fread(mInputBuffer, 1, frameSize, mFpInput);
+        // End of file.
+        if (bytesRead != frameSize) {
+            break;
+        }
+
+        VideoEncFrameIO videoIn, videoOut;
+        videoIn.height = mFrameHeight;
+        videoIn.pitch = mFrameWidth;
+        videoIn.timestamp = (numEncodedFrames * 1000) / mFrameRate;  // in ms.
+        videoIn.yChan = mInputBuffer;
+        videoIn.uChan = videoIn.yChan + videoIn.height * videoIn.pitch;
+        videoIn.vChan = videoIn.uChan + ((videoIn.height * videoIn.pitch) >> 2);
+        uint32_t modTimeMs = 0;
+        int32_t dataLength = mOutputBufferSize;
+        int32_t nLayer = 0;
+        status = PVEncodeVideoFrame(mEncodeControl, &videoIn, &videoOut, &modTimeMs, mOutputBuffer,
+                                    &dataLength, &nLayer);
+        ASSERT_TRUE(status) << "Failed to Encode: " << mFileName;
+
+        MP4HintTrack hintTrack;
+        status = PVGetHintTrack(mEncodeControl, &hintTrack);
+        ASSERT_TRUE(status) << "Failed to get hint track!";
+        UChar *overrunBuffer = PVGetOverrunBuffer(mEncodeControl);
+        ASSERT_EQ(overrunBuffer, nullptr) << "Overrun of buffer!";
+
+        int64_t numBytes = fwrite(mOutputBuffer, 1, dataLength, mFpOutput);
+        ASSERT_EQ(numBytes, dataLength) << "Failed to write to the output file!";
+        numEncodedFrames++;
+        numFramesToEncode--;
+    }
+}
+
+TEST_P(Mpeg4H263EncoderTest, EncodeTest) {
+    mInputBuffer = (uint8_t *)malloc((mFrameWidth * mFrameWidth * 3) / 2);
+    ASSERT_NE(mInputBuffer, nullptr) << "Failed to allocate the input buffer!";
+
+    mOutputBuffer = (uint8_t *)malloc(mOutputBufferSize);
+    ASSERT_NE(mOutputBuffer, nullptr) << "Failed to allocate the output buffer!";
+
+    mFpInput = fopen(mFileName.c_str(), "rb");
+    ASSERT_NE(mFpInput, nullptr) << "Failed to open the input file: " << mFileName;
+
+    mFpOutput = fopen(ENCODED_FILE, "wb");
+    ASSERT_NE(mFpOutput, nullptr) << "Failed to open the output file:" << ENCODED_FILE;
+
+    bool status = PVInitVideoEncoder(mEncodeControl, mEncodeHandle);
+    ASSERT_TRUE(status) << "Failed to initialize the encoder!";
+
+    // Get VOL header.
+    int32_t size = mOutputBufferSize;
+    status = PVGetVolHeader(mEncodeControl, mOutputBuffer, &size, 0);
+    ASSERT_TRUE(status) << "Failed to get the VOL header!";
+
+    // Write the VOL header on the first frame.
+    int32_t numBytes = fwrite(mOutputBuffer, 1, size, mFpOutput);
+    ASSERT_EQ(numBytes, size) << "Failed to write the VOL header!";
+
+    int64_t totalFrames = getTotalFrames();
+    ASSERT_NO_FATAL_FAILURE(processEncoder(totalFrames)) << "Failed to Encode: " << mFileName;
+    status = PVCleanUpVideoEncoder(mEncodeControl);
+    ASSERT_TRUE(status) << "Failed to clean up the encoder resources!";
+}
+
+INSTANTIATE_TEST_SUITE_P(
+        EncodeTest, Mpeg4H263EncoderTest,
+        ::testing::Values(
+                make_tuple("bbb_352x288_420p_30fps_32frames.yuv", false, 352, 288, 25, 1024),
+                make_tuple("bbb_352x288_420p_30fps_32frames.yuv", true, 352, 288, 25, 1024),
+                make_tuple("bbb_352x288_420p_30fps_32frames.yuv", false, 176, 144, 25, 1024),
+                make_tuple("bbb_352x288_420p_30fps_32frames.yuv", true, 176, 144, 25, 1024),
+                make_tuple("football_qvga.yuv", false, 352, 288, 25, 1024),
+                make_tuple("football_qvga.yuv", true, 352, 288, 25, 1024),
+                make_tuple("football_qvga.yuv", false, 176, 144, 30, 1024),
+                make_tuple("football_qvga.yuv", true, 176, 144, 30, 1024)));
+
+int32_t main(int argc, char **argv) {
+    gEnv = new Mpeg4H263EncoderTestEnvironment();
+    ::testing::AddGlobalTestEnvironment(gEnv);
+    ::testing::InitGoogleTest(&argc, argv);
+    uint8_t status = gEnv->initFromOptions(argc, argv);
+    if (status == 0) {
+        status = RUN_ALL_TESTS();
+        ALOGI("Encoder Test Result = %d\n", status);
+    }
+    return status;
+}
diff --git a/media/libstagefright/codecs/m4v_h263/enc/test/Mpeg4H263EncoderTestEnvironment.h b/media/libstagefright/codecs/m4v_h263/enc/test/Mpeg4H263EncoderTestEnvironment.h
new file mode 100644
index 0000000..7ee36e0
--- /dev/null
+++ b/media/libstagefright/codecs/m4v_h263/enc/test/Mpeg4H263EncoderTestEnvironment.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __MPEG4_H263_ENCODER_TEST_ENVIRONMENT_H__
+#define __MPEG4_H263_ENCODER_TEST_ENVIRONMENT_H__
+
+#include <gtest/gtest.h>
+
+#include <getopt.h>
+
+using namespace std;
+
+class Mpeg4H263EncoderTestEnvironment : public::testing::Environment {
+  public:
+    Mpeg4H263EncoderTestEnvironment() : res("/data/local/tmp/Mpeg4H263EncoderTest/") {}
+
+    // 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 Mpeg4H263EncoderTestEnvironment::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  // __MPEG4_H263_ENCODER_TEST_ENVIRONMENT_H__
diff --git a/media/libstagefright/codecs/m4v_h263/enc/test/README.md b/media/libstagefright/codecs/m4v_h263/enc/test/README.md
new file mode 100644
index 0000000..25de878
--- /dev/null
+++ b/media/libstagefright/codecs/m4v_h263/enc/test/README.md
@@ -0,0 +1,38 @@
+## Media Testing ##
+---
+
+#### Mpeg4H263Encoder :
+The Mpeg4H263Encoder Test Suite validates the Mpeg4 and H263 encoder available in libstagefright.
+Run the following steps to build the test suite:
+```
+m Mpeg4H263EncoderTest
+```
+
+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/Mpeg4H263EncoderTest/Mpeg4H263EncoderTest /data/local/tmp/
+```
+
+To test 32-bit binary push binaries from nativetest.
+```
+adb push ${OUT}/data/nativetest/Mpeg4H263EncoderTest/Mpeg4H263EncoderTest /data/local/tmp/
+```
+
+The resource file for the tests is taken from [here](https://storage.googleapis.com/android_media/frameworks/av/media/libstagefright/codecs/m4v_h263/enc/test/Mpeg4H263Encoder.zip ) Download, unzip and push these files into device for testing.
+
+```
+adb push Mpeg4H263Encoder/. /data/local/tmp/
+```
+
+usage: Mpeg4H263EncoderTest -P \<path_to_folder\>
+```
+adb shell /data/local/tmp/Mpeg4H263EncoderTest -P /data/local/tmp/
+```
+Alternatively, the test can also be run using atest command.
+
+```
+atest Mpeg4H263EncoderTest -- --enable-module-dynamic-download=true
+```
diff --git a/media/libstagefright/codecs/mp3dec/test/AndroidTest.xml b/media/libstagefright/codecs/mp3dec/test/AndroidTest.xml
new file mode 100644
index 0000000..7ff9732
--- /dev/null
+++ b/media/libstagefright/codecs/mp3dec/test/AndroidTest.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<configuration description="Test module config for Mp3 Decoder unit test">
+    <option name="test-suite-tag" value="Mp3DecoderTest" />
+    <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+        <option name="cleanup" value="true" />
+        <option name="push" value="Mp3DecoderTest->/data/local/tmp/Mp3DecoderTest" />
+        <option name="push-file"
+            key="https://storage.googleapis.com/android_media/frameworks/av/media/libstagefright/mp3dec/test/Mp3DecoderTest.zip?unzip=true"
+            value="/data/local/tmp/Mp3DecoderTestRes/" />
+    </target_preparer>
+
+    <test class="com.android.tradefed.testtype.GTest" >
+        <option name="native-test-device-path" value="/data/local/tmp" />
+        <option name="module-name" value="Mp3DecoderTest" />
+        <option name="native-test-flag" value="-P /data/local/tmp/Mp3DecoderTestRes/" />
+    </test>
+</configuration>
diff --git a/media/libstagefright/codecs/mp3dec/test/README.md b/media/libstagefright/codecs/mp3dec/test/README.md
index 019239e..f59fec7 100644
--- a/media/libstagefright/codecs/mp3dec/test/README.md
+++ b/media/libstagefright/codecs/mp3dec/test/README.md
@@ -22,13 +22,18 @@
 adb push ${OUT}/data/nativetest/Mp3DecoderTest/Mp3DecoderTest /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 mp3 folder and push all the files in this folder to /data/local/tmp/ on the device.
+The resource file for the tests is taken from [here](https://storage.googleapis.com/android_media/frameworks/av/media/libstagefright/mp3dec/test/Mp3DecoderTest.zip). Download, unzip and push these files into device for testing.
+
 ```
-adb push mp3/. /data/local/tmp/
+adb push Mp3DecoderTestRes/. /data/local/tmp/
 ```
 
 usage: Mp3DecoderTest -P \<path_to_folder\>
 ```
-adb shell /data/local/tmp/Mp3DecoderTest -P /data/local/tmp/
+adb shell /data/local/tmp/Mp3DecoderTest -P /data/local/tmp/Mp3DecoderTestRes/
+```
+Alternatively, the test can also be run using atest command.
+
+```
+atest Mp3DecoderTest -- --enable-module-dynamic-download=true
 ```
diff --git a/media/libstagefright/flac/dec/test/Android.bp b/media/libstagefright/flac/dec/test/Android.bp
new file mode 100644
index 0000000..70ca80a
--- /dev/null
+++ b/media/libstagefright/flac/dec/test/Android.bp
@@ -0,0 +1,50 @@
+/*
+ * 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: "FlacDecoderTest",
+    gtest: true,
+
+    srcs: [
+        "FlacDecoderTest.cpp",
+    ],
+
+    shared_libs: [
+        "liblog",
+    ],
+
+    static_libs: [
+        "libstagefright_flacdec",
+        "libFLAC",
+    ],
+
+    header_libs: [
+        "libstagefright_foundation_headers",
+    ],
+
+    cflags: [
+        "-Werror",
+        "-Wall",
+    ],
+
+    sanitize: {
+        misc_undefined: [
+            "unsigned-integer-overflow",
+            "signed-integer-overflow",
+        ],
+        cfi: true,
+    },
+}
diff --git a/media/libstagefright/flac/dec/test/AndroidTest.xml b/media/libstagefright/flac/dec/test/AndroidTest.xml
new file mode 100644
index 0000000..bebba8e
--- /dev/null
+++ b/media/libstagefright/flac/dec/test/AndroidTest.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<configuration description="Test module config for flac decoder unit tests">
+    <option name="test-suite-tag" value="FlacDecoderTest" />
+    <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+        <option name="cleanup" value="true" />
+        <option name="push" value="FlacDecoderTest->/data/local/tmp/FlacDecoderTest/" />
+        <option name="push-file"
+            key="https://storage.googleapis.com/android_media/frameworks/av/media/libstagefright/flac/dec/test/FlacDecoder.zip?unzip=true"
+            value="/data/local/tmp/FlacDecoderTestRes/" />
+    </target_preparer>
+
+    <test class="com.android.tradefed.testtype.GTest" >
+        <option name="native-test-device-path" value="/data/local/tmp" />
+        <option name="module-name" value="FlacDecoderTest" />
+        <option name="native-test-flag" value="-P /data/local/tmp/FlacDecoderTestRes/" />
+    </test>
+</configuration>
\ No newline at end of file
diff --git a/media/libstagefright/flac/dec/test/FlacDecoderTest.cpp b/media/libstagefright/flac/dec/test/FlacDecoderTest.cpp
new file mode 100644
index 0000000..34f12db
--- /dev/null
+++ b/media/libstagefright/flac/dec/test/FlacDecoderTest.cpp
@@ -0,0 +1,270 @@
+/*
+ * 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 "FlacDecoderTest"
+
+#include <utils/Log.h>
+#include <fstream>
+
+#include "FLACDecoder.h"
+
+#include "FlacDecoderTestEnvironment.h"
+
+#define OUTPUT_FILE_NAME "/data/local/tmp/FlacDecoderOutput.raw"
+#define CODEC_CONFIG_FLAG 32
+
+constexpr uint32_t kMaxCount = 10;
+constexpr int32_t kMaxBlockSize = 4096;
+
+using namespace android;
+
+struct FrameInfo {
+    int32_t bytesCount;
+    uint32_t flags;
+    int64_t timestamp;
+};
+
+static FlacDecoderTestEnvironment *gEnv = nullptr;
+
+class FLACDecoderTest : public ::testing::TestWithParam<tuple<string, string, bool>> {
+  public:
+    FLACDecoderTest() : mFLACDecoder(nullptr), mHasStreamInfo(false), mInputBufferCount(0) {}
+
+    ~FLACDecoderTest() {
+        if (mEleStream.is_open()) mEleStream.close();
+        if (mFLACDecoder) delete mFLACDecoder;
+        mFLACDecoder = nullptr;
+    }
+
+    virtual void SetUp() override {
+        mFLACDecoder = FLACDecoder::Create();
+        ASSERT_NE(mFLACDecoder, nullptr) << "initDecoder: failed to create FLACDecoder";
+    }
+
+    int32_t processFlacDecoder(vector<FrameInfo> Info, int32_t offset, int32_t range,
+                               bool outputFloat, ofstream &ostrm);
+
+    FLACDecoder *mFLACDecoder;
+    FLAC__StreamMetadata_StreamInfo mStreamInfo;
+
+    bool mHasStreamInfo;
+    int32_t mInputBufferCount;
+    ifstream mEleStream;
+};
+
+void getInfo(string infoFileName, vector<FrameInfo> &Info) {
+    ifstream eleInfo;
+    eleInfo.open(infoFileName);
+    ASSERT_EQ(eleInfo.is_open(), true);
+    int32_t bytesCount = 0;
+    uint32_t flags = 0;
+    uint32_t timestamp = 0;
+    while (1) {
+        if (!(eleInfo >> bytesCount)) break;
+        eleInfo >> flags;
+        eleInfo >> timestamp;
+        Info.push_back({bytesCount, flags, timestamp});
+    }
+    if (eleInfo.is_open()) eleInfo.close();
+}
+
+int32_t FLACDecoderTest::processFlacDecoder(vector<FrameInfo> Info, int32_t offset, int32_t range,
+                                            bool outputFloat, ofstream &ostrm) {
+    memset(&mStreamInfo, 0, sizeof(mStreamInfo));
+
+    int32_t frameID = offset;
+    if (range + offset > Info.size() || range < 0 || offset > Info.size() - 1 || offset < 0) {
+        ALOGE("Invalid offset or range or both passed for decoding");
+        ALOGE("offset = %d \t range = %d  \t Info.size() = %zu", offset, range, Info.size());
+        return -1;
+    }
+
+    while (1) {
+        if (frameID == Info.size() || frameID == (offset + range)) break;
+        int64_t flags = (Info)[frameID].flags;
+        int32_t size = (Info)[frameID].bytesCount;
+        if (size < 0) {
+            ALOGE("Size for the memory allocation is negative");
+            return -1;
+        }
+        char *data = (char *)malloc(size);
+        if (!data) {
+            ALOGE("Insufficient memory to read frame");
+            return -1;
+        }
+
+        mEleStream.read(data, size);
+        if (mEleStream.gcount() != size) {
+            if (data) {
+                free(data);
+                data = nullptr;
+            }
+            ALOGE("Invalid size read, requested: %d and read: %zu", size, mEleStream.gcount());
+            return -1;
+        }
+
+        if (flags == CODEC_CONFIG_FLAG && mInputBufferCount == 0) {
+            status_t decoderErr = mFLACDecoder->parseMetadata((uint8_t *)data, size);
+            if (decoderErr == WOULD_BLOCK) {
+                ALOGV("process: parseMetadata is Blocking, Continue %d", decoderErr);
+            } else if (decoderErr == OK) {
+                mStreamInfo = mFLACDecoder->getStreamInfo();
+                if (mStreamInfo.sample_rate && mStreamInfo.max_blocksize && mStreamInfo.channels) {
+                    mHasStreamInfo = true;
+                }
+                ALOGV("decoder configuration : %d Hz, %d channels, %d samples,"
+                      " %d block size",
+                      mStreamInfo.sample_rate, mStreamInfo.channels,
+                      (int32_t)mStreamInfo.total_samples, mStreamInfo.max_blocksize);
+            } else {
+                ALOGE("FLACDecoder parseMetaData returns error %d", decoderErr);
+                if (data) {
+                    free(data);
+                    data = nullptr;
+                }
+                return decoderErr;
+            }
+        } else {
+            const size_t sampleSize = outputFloat ? sizeof(float) : sizeof(int16_t);
+            size_t outSize = mHasStreamInfo
+                                     ? mStreamInfo.max_blocksize * mStreamInfo.channels * sampleSize
+                                     : kMaxBlockSize * FLACDecoder::kMaxChannels * sampleSize;
+
+            void *out_buf = malloc(outSize);
+            if (!out_buf) {
+                if (data) {
+                    free(data);
+                    data = nullptr;
+                }
+                ALOGE("Output buffer allocation failed");
+                return -1;
+            }
+            status_t decoderErr = mFLACDecoder->decodeOneFrame((uint8_t *)data, size, out_buf,
+                                                               &outSize, outputFloat);
+            if (decoderErr != OK) {
+                ALOGE("decodeOneFrame returns error %d", decoderErr);
+                if (data) {
+                    free(data);
+                    data = nullptr;
+                }
+                if (out_buf) {
+                    free(out_buf);
+                    out_buf = nullptr;
+                }
+                return decoderErr;
+            }
+            ostrm.write(reinterpret_cast<char *>(out_buf), outSize);
+            free(out_buf);
+            out_buf = nullptr;
+        }
+        mInputBufferCount++;
+        frameID++;
+        free(data);
+        data = nullptr;
+    }
+    ALOGV("frameID=%d", frameID);
+    return 0;
+}
+
+TEST_F(FLACDecoderTest, CreateDeleteTest) {
+    if (mFLACDecoder) delete mFLACDecoder;
+    mFLACDecoder = nullptr;
+
+    for (int32_t i = 0; i < kMaxCount; i++) {
+        mFLACDecoder = FLACDecoder::Create();
+        ASSERT_NE(mFLACDecoder, nullptr) << "FLACDecoder Creation Failed";
+        if (mFLACDecoder) delete mFLACDecoder;
+        mFLACDecoder = nullptr;
+    }
+}
+
+TEST_P(FLACDecoderTest, FlushTest) {
+    tuple<string /* InputFileName */, string /* InfoFileName */, bool /* outputfloat */> params =
+            GetParam();
+
+    string inputFileName = gEnv->getRes() + get<0>(params);
+    string infoFileName = gEnv->getRes() + get<1>(params);
+    bool outputFloat = get<2>(params);
+
+    vector<FrameInfo> Info;
+    getInfo(infoFileName, Info);
+
+    mEleStream.open(inputFileName, ifstream::binary);
+    ASSERT_EQ(mEleStream.is_open(), true);
+
+    ofstream ostrm;
+    ostrm.open(OUTPUT_FILE_NAME, std::ofstream::binary);
+    ASSERT_EQ(ostrm.is_open(), true);
+
+    int32_t status = processFlacDecoder(Info, 0, Info.size() / 3, outputFloat, ostrm);
+    ASSERT_EQ(status, 0) << "Test Failed. Decode returned error = " << status << endl;
+    mFLACDecoder->flush();
+    mHasStreamInfo = false;
+    status = processFlacDecoder(Info, (Info.size() / 3), Info.size() - (Info.size() / 3),
+                                outputFloat, ostrm);
+    ostrm.close();
+    Info.clear();
+    ASSERT_EQ(status, 0) << "Test Failed. Decode returned error = " << status << endl;
+}
+
+TEST_P(FLACDecoderTest, DecodeTest) {
+    tuple<string /* InputFileName */, string /* InfoFileName */, bool /* outputfloat */> params =
+            GetParam();
+
+    string inputFileName = gEnv->getRes() + get<0>(params);
+    string infoFileName = gEnv->getRes() + get<1>(params);
+    bool outputFloat = get<2>(params);
+
+    vector<FrameInfo> Info;
+    getInfo(infoFileName, Info);
+
+    mEleStream.open(inputFileName, ifstream::binary);
+    ASSERT_EQ(mEleStream.is_open(), true);
+
+    ofstream ostrm;
+    ostrm.open(OUTPUT_FILE_NAME, std::ofstream::binary);
+    ASSERT_EQ(ostrm.is_open(), true);
+
+    int32_t status = processFlacDecoder(Info, 0, Info.size(), outputFloat, ostrm);
+    ostrm.close();
+    Info.clear();
+    ASSERT_EQ(status, 0) << "Test Failed. Decode returned error = " << status << endl;
+}
+
+// TODO: Add remaining tests
+INSTANTIATE_TEST_SUITE_P(
+        FLACDecoderTestAll, FLACDecoderTest,
+        ::testing::Values(make_tuple("bbb_flac_stereo_680kbps_48000hz.flac",
+                                     "bbb_flac_stereo_680kbps_48000hz.info", true),
+                          make_tuple("bbb_flac_stereo_680kbps_48000hz.flac",
+                                     "bbb_flac_stereo_680kbps_48000hz.info", false),
+                          make_tuple("bbb_flac_stereo_600kbps_44100hz.flac",
+                                     "bbb_flac_stereo_600kbps_44100hz.info", true),
+                          make_tuple("bbb_flac_stereo_600kbps_44100hz.flac",
+                                     "bbb_flac_stereo_600kbps_44100hz.info", false)));
+
+int main(int argc, char **argv) {
+    gEnv = new FlacDecoderTestEnvironment();
+    ::testing::AddGlobalTestEnvironment(gEnv);
+    ::testing::InitGoogleTest(&argc, argv);
+    int status = gEnv->initFromOptions(argc, argv);
+    if (status == 0) {
+        status = RUN_ALL_TESTS();
+        ALOGV("Flac Decoder Test Result = %d\n", status);
+    }
+    return status;
+}
diff --git a/media/libstagefright/flac/dec/test/FlacDecoderTestEnvironment.h b/media/libstagefright/flac/dec/test/FlacDecoderTestEnvironment.h
new file mode 100644
index 0000000..1334bba
--- /dev/null
+++ b/media/libstagefright/flac/dec/test/FlacDecoderTestEnvironment.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 __FLAC_DECODER_TEST_ENVIRONMENT_H__
+#define __FLAC_DECODER_TEST_ENVIRONMENT_H__
+
+#include <gtest/gtest.h>
+
+#include <getopt.h>
+
+using namespace std;
+
+class FlacDecoderTestEnvironment : public ::testing::Environment {
+  public:
+    FlacDecoderTestEnvironment() : 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 FlacDecoderTestEnvironment::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  // __FLAC_DECODER_TEST_ENVIRONMENT_H__
diff --git a/media/libstagefright/flac/dec/test/README.md b/media/libstagefright/flac/dec/test/README.md
new file mode 100644
index 0000000..4d194cd
--- /dev/null
+++ b/media/libstagefright/flac/dec/test/README.md
@@ -0,0 +1,40 @@
+## Media Testing ##
+---
+#### FlacDecoder :
+The FlacDecoder Test Suite validates the FlacDecoder available in libstagefright.
+
+Run the following steps to build the test suite:
+```
+m FlacDecoderTest
+```
+
+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/FlacDecoderTest/FlacDecoderTest /data/local/tmp/
+```
+
+To test 32-bit binary push binaries from nativetest.
+```
+adb push ${OUT}/data/nativetest/FlacDecoderTest/FlacDecoderTest /data/local/tmp/
+```
+
+The resource file for the tests is taken from [here](https://storage.googleapis.com/android_media/frameworks/av/media/libstagefright/flac/dec/test/FlacDecoder.zip).
+Download, unzip and push these files into device for testing.
+
+```
+adb push FlacDecoder /data/local/tmp/
+```
+
+usage: FlacDecoderTest -P \<path_to_folder\>
+```
+adb shell /data/local/tmp/FlacDecoderTest -P /data/local/tmp/FlacDecoder/
+```
+Alternatively, the test can also be run using atest command.
+
+```
+atest FlacDecoderTest -- --enable-module-dynamic-download=true
+```
diff --git a/media/ndk/NdkJavaVMHelper.cpp b/media/ndk/NdkJavaVMHelper.cpp
index baf2744..a77016c 100644
--- a/media/ndk/NdkJavaVMHelper.cpp
+++ b/media/ndk/NdkJavaVMHelper.cpp
@@ -16,7 +16,7 @@
 
 #define LOG_TAG "NdkJavaVMHelper"
 
-#include <media/NdkJavaVMHelper.h>
+#include "NdkJavaVMHelperPriv.h"
 #include <utils/Log.h>
 
 namespace android {
diff --git a/media/ndk/include/media/NdkJavaVMHelper.h b/media/ndk/NdkJavaVMHelperPriv.h
similarity index 86%
rename from media/ndk/include/media/NdkJavaVMHelper.h
rename to media/ndk/NdkJavaVMHelperPriv.h
index 1c20275..49f087e 100644
--- a/media/ndk/include/media/NdkJavaVMHelper.h
+++ b/media/ndk/NdkJavaVMHelperPriv.h
@@ -14,9 +14,9 @@
  * limitations under the License.
  */
 
-#ifndef NDK_JAVA_VM_HELPER_H_
+#ifndef NDK_JAVA_VM_HELPER_PRIV_H_
 
-#define NDK_JAVA_VM_HELPER_H_
+#define NDK_JAVA_VM_HELPER_PRIV_H_
 
 #include "jni.h"
 
@@ -28,4 +28,4 @@
 
 }  // namespace android
 
-#endif  // NDK_JAVA_VM_HELPER_H_
\ No newline at end of file
+#endif  // NDK_JAVA_VM_HELPER_PRIV_H_
\ No newline at end of file
diff --git a/media/ndk/NdkMediaDataSource.cpp b/media/ndk/NdkMediaDataSource.cpp
index 98ccd6c..3e6d7d6 100644
--- a/media/ndk/NdkMediaDataSource.cpp
+++ b/media/ndk/NdkMediaDataSource.cpp
@@ -17,6 +17,7 @@
 //#define LOG_NDEBUG 0
 #define LOG_TAG "NdkMediaDataSource"
 
+#include "NdkJavaVMHelperPriv.h"
 #include "NdkMediaDataSourcePriv.h"
 
 #include <inttypes.h>
@@ -30,7 +31,6 @@
 #include <datasource/HTTPBase.h>
 #include <datasource/NuCachedSource2.h>
 #include <media/IMediaHTTPService.h>
-#include <media/NdkJavaVMHelper.h>
 #include <media/NdkMediaError.h>
 #include <media/NdkMediaDataSource.h>
 #include <media/stagefright/InterfaceUtils.h>
diff --git a/media/tests/benchmark/MediaBenchmarkTest/src/main/cpp/Android.bp b/media/tests/benchmark/MediaBenchmarkTest/src/main/cpp/Android.bp
index c72eb55..3e5e4c8 100644
--- a/media/tests/benchmark/MediaBenchmarkTest/src/main/cpp/Android.bp
+++ b/media/tests/benchmark/MediaBenchmarkTest/src/main/cpp/Android.bp
@@ -1,5 +1,6 @@
 cc_test_library {
     name: "libmediabenchmark_jni",
+    sdk_version: "current",
 
     defaults: [
         "libmediabenchmark_common-defaults",
diff --git a/media/tests/benchmark/src/native/common/Android.bp b/media/tests/benchmark/src/native/common/Android.bp
index f8ea25c..d4389da 100644
--- a/media/tests/benchmark/src/native/common/Android.bp
+++ b/media/tests/benchmark/src/native/common/Android.bp
@@ -46,6 +46,8 @@
 
 cc_defaults {
     name: "libmediabenchmark-defaults",
+    sdk_version: "current",
+    stl: "c++_shared",
 
     shared_libs: [
         "libmediandk",
diff --git a/services/camera/libcameraservice/common/CameraProviderManager.cpp b/services/camera/libcameraservice/common/CameraProviderManager.cpp
index b20774a..2c97a38 100644
--- a/services/camera/libcameraservice/common/CameraProviderManager.cpp
+++ b/services/camera/libcameraservice/common/CameraProviderManager.cpp
@@ -1352,29 +1352,9 @@
 
     // Get list of concurrent streaming camera device combinations
     if (mMinorVersion >= 6) {
-        hardware::Return<void> ret = interface2_6->getConcurrentStreamingCameraIds([&status, this](
-                Status concurrentIdStatus, // TODO: Move all instances of hidl_string to 'using'
-                const hardware::hidl_vec<hardware::hidl_vec<hardware::hidl_string>>&
-                        cameraDeviceIdCombinations) {
-            status = concurrentIdStatus;
-            if (status == Status::OK) {
-                for (auto& combination : cameraDeviceIdCombinations) {
-                    std::unordered_set<std::string> deviceIds;
-                    for (auto &cameraDeviceId : combination) {
-                        deviceIds.insert(cameraDeviceId.c_str());
-                    }
-                    mConcurrentCameraIdCombinations.push_back(std::move(deviceIds));
-                }
-            } });
-        if (!ret.isOk()) {
-            ALOGE("%s: Transaction error in getting camera ID list from provider '%s': %s",
-                    __FUNCTION__, mProviderName.c_str(), linked.description().c_str());
-            return DEAD_OBJECT;
-        }
-        if (status != Status::OK) {
-            ALOGE("%s: Unable to query for camera devices from provider '%s'",
-                    __FUNCTION__, mProviderName.c_str());
-            return mapToStatusT(status);
+        res = getConcurrentStreamingCameraIdsInternalLocked(interface2_6);
+        if (res != OK) {
+            return res;
         }
     }
 
@@ -1626,6 +1606,75 @@
     return OK;
 }
 
+status_t CameraProviderManager::ProviderInfo::getConcurrentStreamingCameraIdsInternalLocked(
+        sp<provider::V2_6::ICameraProvider> &interface2_6) {
+    if (interface2_6 == nullptr) {
+        ALOGE("%s: null interface provided", __FUNCTION__);
+        return BAD_VALUE;
+    }
+    Status status = Status::OK;
+    hardware::Return<void> ret =
+            interface2_6->getConcurrentStreamingCameraIds([&status, this](
+            Status concurrentIdStatus, // TODO: Move all instances of hidl_string to 'using'
+            const hardware::hidl_vec<hardware::hidl_vec<hardware::hidl_string>>&
+                        cameraDeviceIdCombinations) {
+            status = concurrentIdStatus;
+            if (status == Status::OK) {
+                mConcurrentCameraIdCombinations.clear();
+                for (auto& combination : cameraDeviceIdCombinations) {
+                    std::unordered_set<std::string> deviceIds;
+                    for (auto &cameraDeviceId : combination) {
+                        deviceIds.insert(cameraDeviceId.c_str());
+                    }
+                    mConcurrentCameraIdCombinations.push_back(std::move(deviceIds));
+                }
+            } });
+    if (!ret.isOk()) {
+        ALOGE("%s: Transaction error in getting concurrent camera ID list from provider '%s'",
+                __FUNCTION__, mProviderName.c_str());
+            return DEAD_OBJECT;
+    }
+    if (status != Status::OK) {
+        ALOGE("%s: Unable to query for camera devices from provider '%s'",
+                    __FUNCTION__, mProviderName.c_str());
+        return mapToStatusT(status);
+    }
+    return OK;
+}
+
+status_t CameraProviderManager::ProviderInfo::reCacheConcurrentStreamingCameraIdsLocked() {
+    if (mMinorVersion < 6) {
+      // Unsupported operation, nothing to do here
+      return OK;
+    }
+    // Check if the provider is currently active - not going to start it up for this notification
+    auto interface = mSavedInterface != nullptr ? mSavedInterface : mActiveInterface.promote();
+    if (interface == nullptr) {
+        ALOGE("%s: camera provider interface for %s is not valid", __FUNCTION__,
+                mProviderName.c_str());
+        return INVALID_OPERATION;
+    }
+    auto castResult = provider::V2_6::ICameraProvider::castFrom(interface);
+
+    if (castResult.isOk()) {
+        sp<provider::V2_6::ICameraProvider> interface2_6 = castResult;
+        if (interface2_6 != nullptr) {
+            return getConcurrentStreamingCameraIdsInternalLocked(interface2_6);
+        } else {
+            // This should not happen since mMinorVersion >= 6
+            ALOGE("%s: mMinorVersion was >= 6, but interface2_6 was nullptr", __FUNCTION__);
+            return UNKNOWN_ERROR;
+        }
+    }
+    return OK;
+}
+
+std::vector<std::unordered_set<std::string>>
+CameraProviderManager::ProviderInfo::getConcurrentCameraIdCombinations() {
+    std::lock_guard<std::mutex> lock(mLock);
+    return mConcurrentCameraIdCombinations;
+}
+
 hardware::Return<void> CameraProviderManager::ProviderInfo::cameraDeviceStatusChange(
         const hardware::hidl_string& cameraDeviceName,
         CameraDeviceStatus newStatus) {
@@ -1659,6 +1708,10 @@
         }
         listener = mManager->getStatusListener();
         initialized = mInitialized;
+        if (reCacheConcurrentStreamingCameraIdsLocked() != OK) {
+            ALOGE("%s: CameraProvider %s could not re-cache concurrent streaming camera id list ",
+                      __FUNCTION__, mProviderName.c_str());
+        }
     }
     // Call without lock held to allow reentrancy into provider manager
     // Don't send the callback if providerInfo hasn't been initialized.
@@ -2747,7 +2800,7 @@
     std::vector<std::unordered_set<std::string>> deviceIdCombinations;
     std::lock_guard<std::mutex> lock(mInterfaceMutex);
     for (auto &provider : mProviders) {
-        for (auto &combinations : provider->mConcurrentCameraIdCombinations) {
+        for (auto &combinations : provider->getConcurrentCameraIdCombinations()) {
             deviceIdCombinations.push_back(combinations);
         }
     }
@@ -2831,7 +2884,7 @@
     // TODO: we should also do a findDeviceInfoLocked here ?
     for (auto &provider : mProviders) {
         if (checkIfSetContainsAll(cameraIdsAndSessionConfigs,
-                provider->mConcurrentCameraIdCombinations)) {
+                provider->getConcurrentCameraIdCombinations())) {
             // For each camera device in cameraIdsAndSessionConfigs collect
             // the streamConfigs and create the HAL
             // CameraIdAndStreamCombination, exit early if needed
diff --git a/services/camera/libcameraservice/common/CameraProviderManager.h b/services/camera/libcameraservice/common/CameraProviderManager.h
index 42da227..1a02980 100644
--- a/services/camera/libcameraservice/common/CameraProviderManager.h
+++ b/services/camera/libcameraservice/common/CameraProviderManager.h
@@ -417,6 +417,9 @@
         status_t notifyDeviceStateChange(
                 hardware::hidl_bitfield<hardware::camera::provider::V2_5::DeviceState>
                     newDeviceState);
+
+        std::vector<std::unordered_set<std::string>> getConcurrentCameraIdCombinations();
+
         /**
          * Query the camera provider for concurrent stream configuration support
          */
@@ -510,8 +513,6 @@
         // physical camera IDs.
         std::vector<std::string> mProviderPublicCameraIds;
 
-        std::vector<std::unordered_set<std::string>> mConcurrentCameraIdCombinations;
-
         // HALv1-specific camera fields, including the actual device interface
         struct DeviceInfo1 : public DeviceInfo {
             typedef hardware::camera::device::V1_0::ICameraDevice InterfaceT;
@@ -600,6 +601,8 @@
 
         bool mInitialized = false;
 
+        std::vector<std::unordered_set<std::string>> mConcurrentCameraIdCombinations;
+
         // Templated method to instantiate the right kind of DeviceInfo and call the
         // right CameraProvider getCameraDeviceInterface_* method.
         template<class DeviceInfoT>
@@ -623,6 +626,12 @@
         static metadata_vendor_id_t generateVendorTagId(const std::string &name);
 
         void removeDevice(std::string id);
+
+        // Expects to have mLock locked
+        status_t reCacheConcurrentStreamingCameraIdsLocked();
+        // Expects to have mLock locked
+        status_t getConcurrentStreamingCameraIdsInternalLocked(
+                sp<hardware::camera::provider::V2_6::ICameraProvider> &interface2_6);
     };
 
     // Utility to find a DeviceInfo by ID; pointer is only valid while mInterfaceMutex is held