Merge "Camera: Add HEIC support"
diff --git a/cmds/screenrecord/screenrecord.cpp b/cmds/screenrecord/screenrecord.cpp
index 7803ccc..c361690 100644
--- a/cmds/screenrecord/screenrecord.cpp
+++ b/cmds/screenrecord/screenrecord.cpp
@@ -86,6 +86,7 @@
 using android::INFO_FORMAT_CHANGED;
 using android::INFO_OUTPUT_BUFFERS_CHANGED;
 using android::INVALID_OPERATION;
+using android::NAME_NOT_FOUND;
 using android::NO_ERROR;
 using android::UNKNOWN_ERROR;
 
@@ -585,8 +586,12 @@
     self->startThreadPool();
 
     // Get main display parameters.
-    sp<IBinder> mainDpy = SurfaceComposerClient::getBuiltInDisplay(
-            ISurfaceComposer::eDisplayIdMain);
+    const sp<IBinder> mainDpy = SurfaceComposerClient::getInternalDisplayToken();
+    if (mainDpy == nullptr) {
+        fprintf(stderr, "ERROR: no display\n");
+        return NAME_NOT_FOUND;
+    }
+
     DisplayInfo mainDpyInfo;
     err = SurfaceComposerClient::getDisplayInfo(mainDpy, &mainDpyInfo);
     if (err != NO_ERROR) {
diff --git a/cmds/stagefright/codec.cpp b/cmds/stagefright/codec.cpp
index a463ec5..e5a4337 100644
--- a/cmds/stagefright/codec.cpp
+++ b/cmds/stagefright/codec.cpp
@@ -411,10 +411,12 @@
         composerClient = new SurfaceComposerClient;
         CHECK_EQ(composerClient->initCheck(), (status_t)OK);
 
-        sp<IBinder> display(SurfaceComposerClient::getBuiltInDisplay(
-                ISurfaceComposer::eDisplayIdMain));
+        const sp<IBinder> display = SurfaceComposerClient::getInternalDisplayToken();
+        CHECK(display != nullptr);
+
         DisplayInfo info;
-        SurfaceComposerClient::getDisplayInfo(display, &info);
+        CHECK_EQ(SurfaceComposerClient::getDisplayInfo(display, &info), NO_ERROR);
+
         ssize_t displayWidth = info.w;
         ssize_t displayHeight = info.h;
 
diff --git a/cmds/stagefright/mediafilter.cpp b/cmds/stagefright/mediafilter.cpp
index f0ee0e1..2cf6955 100644
--- a/cmds/stagefright/mediafilter.cpp
+++ b/cmds/stagefright/mediafilter.cpp
@@ -748,10 +748,12 @@
         composerClient = new SurfaceComposerClient;
         CHECK_EQ((status_t)OK, composerClient->initCheck());
 
-        android::sp<IBinder> display(SurfaceComposerClient::getBuiltInDisplay(
-                ISurfaceComposer::eDisplayIdMain));
+        const android::sp<IBinder> display = SurfaceComposerClient::getInternalDisplayToken();
+        CHECK(display != nullptr);
+
         DisplayInfo info;
-        SurfaceComposerClient::getDisplayInfo(display, &info);
+        CHECK_EQ(SurfaceComposerClient::getDisplayInfo(display, &info), NO_ERROR);
+
         ssize_t displayWidth = info.w;
         ssize_t displayHeight = info.h;
 
diff --git a/cmds/stagefright/stream.cpp b/cmds/stagefright/stream.cpp
index b2f39dc..35bdbc0 100644
--- a/cmds/stagefright/stream.cpp
+++ b/cmds/stagefright/stream.cpp
@@ -318,10 +318,12 @@
     sp<SurfaceComposerClient> composerClient = new SurfaceComposerClient;
     CHECK_EQ(composerClient->initCheck(), (status_t)OK);
 
-    sp<IBinder> display(SurfaceComposerClient::getBuiltInDisplay(
-            ISurfaceComposer::eDisplayIdMain));
+    const sp<IBinder> display = SurfaceComposerClient::getInternalDisplayToken();
+    CHECK(display != nullptr);
+
     DisplayInfo info;
-    SurfaceComposerClient::getDisplayInfo(display, &info);
+    CHECK_EQ(SurfaceComposerClient::getDisplayInfo(display, &info), NO_ERROR);
+
     ssize_t displayWidth = info.w;
     ssize_t displayHeight = info.h;
 
diff --git a/drm/mediacas/plugins/clearkey/ClearKeyCasPlugin.cpp b/drm/mediacas/plugins/clearkey/ClearKeyCasPlugin.cpp
index 27bd631..bf35224 100644
--- a/drm/mediacas/plugins/clearkey/ClearKeyCasPlugin.cpp
+++ b/drm/mediacas/plugins/clearkey/ClearKeyCasPlugin.cpp
@@ -97,13 +97,13 @@
 ///////////////////////////////////////////////////////////////////////////////
 ClearKeyCasPlugin::ClearKeyCasPlugin(
         void *appData, CasPluginCallback callback)
-    : mCallback(callback), mAppData(appData) {
+    : mCallback(callback), mCallbackExt(NULL), mAppData(appData) {
     ALOGV("CTOR");
 }
 
 ClearKeyCasPlugin::ClearKeyCasPlugin(
         void *appData, CasPluginCallbackExt callback)
-    : mCallbackExt(callback), mAppData(appData) {
+    : mCallback(NULL), mCallbackExt(callback), mAppData(appData) {
     ALOGV("CTOR");
 }
 
diff --git a/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/types.h b/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/types.h
index c38e674..b9f3aa8 100644
--- a/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/types.h
+++ b/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/types.h
@@ -120,11 +120,9 @@
         IComponentStore::ComponentTraits* d,
         const C2Component::Traits& s);
 
-// ComponentTraits -> C2Component::Traits, std::unique_ptr<std::vector<std::string>>
-// Note: The output d is only valid as long as aliasesBuffer remains alive.
+// ComponentTraits -> C2Component::Traits
 bool objcpy(
         C2Component::Traits* d,
-        std::unique_ptr<std::vector<std::string>>* aliasesBuffer,
         const IComponentStore::ComponentTraits& s);
 
 // C2StructDescriptor -> StructDescriptor
diff --git a/media/codec2/hidl/1.0/utils/types.cpp b/media/codec2/hidl/1.0/utils/types.cpp
index caed839..02cdc23 100644
--- a/media/codec2/hidl/1.0/utils/types.cpp
+++ b/media/codec2/hidl/1.0/utils/types.cpp
@@ -351,7 +351,6 @@
 // ComponentTraits -> C2Component::Traits, std::unique_ptr<std::vector<std::string>>
 bool objcpy(
         C2Component::Traits* d,
-        std::unique_ptr<std::vector<std::string>>* aliasesBuffer,
         const IComponentStore::ComponentTraits& s) {
     d->name = s.name.c_str();
 
@@ -394,15 +393,9 @@
 
     d->rank = static_cast<C2Component::rank_t>(s.rank);
     d->mediaType = s.mediaType.c_str();
-
-    // aliasesBuffer must not be resized after this.
-    *aliasesBuffer = std::make_unique<std::vector<std::string>>(
-            s.aliases.size());
-    (*aliasesBuffer)->resize(s.aliases.size());
-    std::vector<C2StringLiteral> dAliases(s.aliases.size());
+    d->aliases.resize(s.aliases.size());
     for (size_t i = 0; i < s.aliases.size(); ++i) {
-        (**aliasesBuffer)[i] = s.aliases[i].c_str();
-        d->aliases[i] = (**aliasesBuffer)[i].c_str();
+        d->aliases[i] = s.aliases[i];
     }
     return true;
 }
diff --git a/media/codec2/hidl/client/client.cpp b/media/codec2/hidl/client/client.cpp
index e02c2ca..3808be5 100644
--- a/media/codec2/hidl/client/client.cpp
+++ b/media/codec2/hidl/client/client.cpp
@@ -564,9 +564,8 @@
                     return;
                 }
                 mTraitsList.resize(t.size());
-                mAliasesBuffer.resize(t.size());
                 for (size_t i = 0; i < t.size(); ++i) {
-                    if (!objcpy(&mTraitsList[i], &mAliasesBuffer[i], t[i])) {
+                    if (!objcpy(&mTraitsList[i], t[i])) {
                         LOG(ERROR) << "listComponents -- corrupted output.";
                         return;
                     }
diff --git a/media/codec2/hidl/client/include/codec2/hidl/client.h b/media/codec2/hidl/client/include/codec2/hidl/client.h
index 5b3afca..478ce6e 100644
--- a/media/codec2/hidl/client/include/codec2/hidl/client.h
+++ b/media/codec2/hidl/client/include/codec2/hidl/client.h
@@ -232,8 +232,6 @@
     mutable bool mListed;
     std::string mServiceName;
     mutable std::vector<C2Component::Traits> mTraitsList;
-    mutable std::vector<std::unique_ptr<std::vector<std::string>>>
-            mAliasesBuffer;
 
     sp<::android::hardware::media::bufferpool::V2_0::IClientManager>
             mHostPoolManager;
diff --git a/media/codec2/sfplugin/CCodec.cpp b/media/codec2/sfplugin/CCodec.cpp
index ed1f85b..bc22045 100644
--- a/media/codec2/sfplugin/CCodec.cpp
+++ b/media/codec2/sfplugin/CCodec.cpp
@@ -937,6 +937,47 @@
     (new AMessage(kWhatCreateInputSurface, this))->post();
 }
 
+sp<PersistentSurface> CCodec::CreateOmxInputSurface() {
+    using namespace android::hardware::media::omx::V1_0;
+    using namespace android::hardware::media::omx::V1_0::utils;
+    using namespace android::hardware::graphics::bufferqueue::V1_0::utils;
+    typedef android::hardware::media::omx::V1_0::Status OmxStatus;
+    android::sp<IOmx> omx = IOmx::getService();
+    typedef android::hardware::graphics::bufferqueue::V1_0::
+            IGraphicBufferProducer HGraphicBufferProducer;
+    typedef android::hardware::media::omx::V1_0::
+            IGraphicBufferSource HGraphicBufferSource;
+    OmxStatus s;
+    android::sp<HGraphicBufferProducer> gbp;
+    android::sp<HGraphicBufferSource> gbs;
+    android::Return<void> transStatus = omx->createInputSurface(
+            [&s, &gbp, &gbs](
+                    OmxStatus status,
+                    const android::sp<HGraphicBufferProducer>& producer,
+                    const android::sp<HGraphicBufferSource>& source) {
+                s = status;
+                gbp = producer;
+                gbs = source;
+            });
+    if (transStatus.isOk() && s == OmxStatus::OK) {
+        return new PersistentSurface(
+                new H2BGraphicBufferProducer(gbp),
+                sp<::android::IGraphicBufferSource>(new LWGraphicBufferSource(gbs)));
+    }
+
+    return nullptr;
+}
+
+sp<PersistentSurface> CCodec::CreateCompatibleInputSurface() {
+    sp<PersistentSurface> surface(CreateInputSurface());
+
+    if (surface == nullptr) {
+        surface = CreateOmxInputSurface();
+    }
+
+    return surface;
+}
+
 void CCodec::createInputSurface() {
     status_t err;
     sp<IGraphicBufferProducer> bufferProducer;
@@ -949,7 +990,7 @@
         outputFormat = config->mOutputFormat;
     }
 
-    std::shared_ptr<PersistentSurface> persistentSurface(CreateInputSurface());
+    sp<PersistentSurface> persistentSurface = CreateCompatibleInputSurface();
 
     if (persistentSurface->getHidlTarget()) {
         sp<IInputSurface> hidlInputSurface = IInputSurface::castFrom(
@@ -1669,46 +1710,17 @@
     return new android::CCodec;
 }
 
+// Create Codec 2.0 input surface
 extern "C" android::PersistentSurface *CreateInputSurface() {
     // Attempt to create a Codec2's input surface.
     std::shared_ptr<android::Codec2Client::InputSurface> inputSurface =
             android::Codec2Client::CreateInputSurface();
-    if (inputSurface) {
-        return new android::PersistentSurface(
-                inputSurface->getGraphicBufferProducer(),
-                static_cast<android::sp<android::hidl::base::V1_0::IBase>>(
-                inputSurface->getHalInterface()));
+    if (!inputSurface) {
+        return nullptr;
     }
-
-    // Fall back to OMX.
-    using namespace android::hardware::media::omx::V1_0;
-    using namespace android::hardware::media::omx::V1_0::utils;
-    using namespace android::hardware::graphics::bufferqueue::V1_0::utils;
-    typedef android::hardware::media::omx::V1_0::Status OmxStatus;
-    android::sp<IOmx> omx = IOmx::getService();
-    typedef android::hardware::graphics::bufferqueue::V1_0::
-            IGraphicBufferProducer HGraphicBufferProducer;
-    typedef android::hardware::media::omx::V1_0::
-            IGraphicBufferSource HGraphicBufferSource;
-    OmxStatus s;
-    android::sp<HGraphicBufferProducer> gbp;
-    android::sp<HGraphicBufferSource> gbs;
-    android::Return<void> transStatus = omx->createInputSurface(
-            [&s, &gbp, &gbs](
-                    OmxStatus status,
-                    const android::sp<HGraphicBufferProducer>& producer,
-                    const android::sp<HGraphicBufferSource>& source) {
-                s = status;
-                gbp = producer;
-                gbs = source;
-            });
-    if (transStatus.isOk() && s == OmxStatus::OK) {
-        return new android::PersistentSurface(
-                new H2BGraphicBufferProducer(gbp),
-                sp<::android::IGraphicBufferSource>(
-                    new LWGraphicBufferSource(gbs)));
-    }
-
-    return nullptr;
+    return new android::PersistentSurface(
+            inputSurface->getGraphicBufferProducer(),
+            static_cast<android::sp<android::hidl::base::V1_0::IBase>>(
+            inputSurface->getHalInterface()));
 }
 
diff --git a/media/codec2/sfplugin/CCodec.h b/media/codec2/sfplugin/CCodec.h
index bb8bd19..ba5f5f3 100644
--- a/media/codec2/sfplugin/CCodec.h
+++ b/media/codec2/sfplugin/CCodec.h
@@ -89,6 +89,16 @@
     void flush();
     void release(bool sendCallback);
 
+    /**
+     * Creates an input surface for the current device configuration compatible with CCodec.
+     * This could be backed by the C2 HAL or the OMX HAL.
+     */
+    static sp<PersistentSurface> CreateCompatibleInputSurface();
+
+    /// Creates an input surface to the OMX HAL
+    static sp<PersistentSurface> CreateOmxInputSurface();
+
+    /// handle a create input surface call
     void createInputSurface();
     void setInputSurface(const sp<PersistentSurface> &surface);
     status_t setupInputSurface(const std::shared_ptr<InputSurfaceWrapper> &surface);
diff --git a/media/codec2/sfplugin/CCodecBufferChannel.cpp b/media/codec2/sfplugin/CCodecBufferChannel.cpp
index 587f83c..6842fa5 100644
--- a/media/codec2/sfplugin/CCodecBufferChannel.cpp
+++ b/media/codec2/sfplugin/CCodecBufferChannel.cpp
@@ -94,6 +94,11 @@
      */
     virtual void getArray(Vector<sp<MediaCodecBuffer>> *) const {}
 
+    /**
+     * Return number of buffers the client owns.
+     */
+    virtual size_t numClientBuffers() const = 0;
+
 protected:
     std::string mComponentName; ///< name of component for debugging
     std::string mChannelName; ///< name of channel for debugging
@@ -152,11 +157,6 @@
      */
     virtual std::unique_ptr<InputBuffers> toArrayMode(size_t size) = 0;
 
-    /**
-     * Return number of buffers the client owns.
-     */
-    virtual size_t numClientBuffers() const = 0;
-
 protected:
     // Pool to obtain blocks for input buffers.
     std::shared_ptr<C2BlockPool> mPool;
@@ -1226,6 +1226,10 @@
         mImpl.realloc(alloc);
     }
 
+    size_t numClientBuffers() const final {
+        return mImpl.numClientBuffers();
+    }
+
 private:
     BuffersArrayImpl mImpl;
 };
@@ -1287,6 +1291,10 @@
         return std::move(array);
     }
 
+    size_t numClientBuffers() const final {
+        return mImpl.numClientBuffers();
+    }
+
     /**
      * Return an appropriate Codec2Buffer object for the type of buffers.
      *
@@ -1816,9 +1824,17 @@
 }
 
 void CCodecBufferChannel::feedInputBufferIfAvailableInternal() {
-    while (!mInputMetEos &&
-           !mReorderStash.lock()->hasPending() &&
-           !mPipelineWatcher.lock()->pipelineFull()) {
+    if (mInputMetEos ||
+           mReorderStash.lock()->hasPending() ||
+           mPipelineWatcher.lock()->pipelineFull()) {
+        return;
+    } else {
+        Mutexed<std::unique_ptr<OutputBuffers>>::Locked buffers(mOutputBuffers);
+        if ((*buffers)->numClientBuffers() >= mNumOutputSlots) {
+            return;
+        }
+    }
+    for (size_t i = 0; i < mNumInputSlots; ++i) {
         sp<MediaCodecBuffer> inBuffer;
         size_t index;
         {
diff --git a/media/codec2/sfplugin/Codec2InfoBuilder.cpp b/media/codec2/sfplugin/Codec2InfoBuilder.cpp
index 5f0dd0b..ead0a9b 100644
--- a/media/codec2/sfplugin/Codec2InfoBuilder.cpp
+++ b/media/codec2/sfplugin/Codec2InfoBuilder.cpp
@@ -68,262 +68,146 @@
             s.compare(s.size() - suffixLen, suffixLen, suffix) == 0;
 }
 
-// Constants from ACodec
-constexpr OMX_U32 kPortIndexInput = 0;
-constexpr OMX_U32 kPortIndexOutput = 1;
-constexpr OMX_U32 kMaxIndicesToCheck = 32;
+void addSupportedProfileLevels(
+        std::shared_ptr<Codec2Client::Interface> intf,
+        MediaCodecInfo::CapabilitiesWriter *caps,
+        const Traits& trait, const std::string &mediaType) {
+    std::shared_ptr<C2Mapper::ProfileLevelMapper> mapper =
+        C2Mapper::GetProfileLevelMapper(trait.mediaType);
+    // if we don't know the media type, pass through all values unmapped
 
-status_t queryOmxCapabilities(
-        const char* name, const char* mediaType, bool isEncoder,
-        MediaCodecInfo::CapabilitiesWriter* caps) {
-
-    const char *role = GetComponentRole(isEncoder, mediaType);
-    if (role == nullptr) {
-        return BAD_VALUE;
-    }
-
-    using namespace ::android::hardware::media::omx::V1_0;
-    using ::android::hardware::Return;
-    using ::android::hardware::Void;
-    using ::android::hardware::hidl_vec;
-    using ::android::hardware::media::omx::V1_0::utils::LWOmxNode;
-
-    sp<IOmx> omx = IOmx::getService();
-    if (!omx) {
-        ALOGW("Could not obtain IOmx service.");
-        return NO_INIT;
-    }
-
-    struct Observer : IOmxObserver {
-        virtual Return<void> onMessages(const hidl_vec<Message>&) override {
-            return Void();
-        }
+    // TODO: we cannot find levels that are local 'maxima' without knowing the coding
+    // e.g. H.263 level 45 and level 30 could be two values for highest level as
+    // they don't include one another. For now we use the last supported value.
+    bool encoder = trait.kind == C2Component::KIND_ENCODER;
+    C2StreamProfileLevelInfo pl(encoder /* output */, 0u);
+    std::vector<C2FieldSupportedValuesQuery> profileQuery = {
+        C2FieldSupportedValuesQuery::Possible(C2ParamField(&pl, &pl.profile))
     };
 
-    sp<Observer> observer = new Observer();
-    Status status;
-    sp<IOmxNode> tOmxNode;
-    Return<void> transStatus = omx->allocateNode(
-            name, observer,
-            [&status, &tOmxNode](Status s, const sp<IOmxNode>& n) {
-                status = s;
-                tOmxNode = n;
-            });
-    if (!transStatus.isOk()) {
-        ALOGW("IOmx::allocateNode -- transaction failed.");
-        return NO_INIT;
-    }
-    if (status != Status::OK) {
-        ALOGW("IOmx::allocateNode -- error returned: %d.",
-                static_cast<int>(status));
-        return NO_INIT;
+    c2_status_t err = intf->querySupportedValues(profileQuery, C2_DONT_BLOCK);
+    ALOGV("query supported profiles -> %s | %s", asString(err), asString(profileQuery[0].status));
+    if (err != C2_OK || profileQuery[0].status != C2_OK) {
+        return;
     }
 
-    sp<LWOmxNode> omxNode = new LWOmxNode(tOmxNode);
-
-    status_t err = SetComponentRole(omxNode, role);
-    if (err != OK) {
-        omxNode->freeNode();
-        ALOGW("Failed to SetComponentRole: component = %s, role = %s.",
-                name, role);
-        return err;
+    // we only handle enumerated values
+    if (profileQuery[0].values.type != C2FieldSupportedValues::VALUES) {
+        return;
     }
 
-    bool isVideo = hasPrefix(mediaType, "video/") == 0;
-    bool isImage = hasPrefix(mediaType, "image/") == 0;
+    // determine if codec supports HDR
+    bool supportsHdr = false;
+    bool supportsHdr10Plus = false;
 
-    if (isVideo || isImage) {
-        OMX_VIDEO_PARAM_PROFILELEVELTYPE param;
-        InitOMXParams(&param);
-        param.nPortIndex = isEncoder ? kPortIndexOutput : kPortIndexInput;
-
-        for (OMX_U32 index = 0; index <= kMaxIndicesToCheck; ++index) {
-            param.nProfileIndex = index;
-            status_t err = omxNode->getParameter(
-                    OMX_IndexParamVideoProfileLevelQuerySupported,
-                    &param, sizeof(param));
-            if (err != OK) {
+    std::vector<std::shared_ptr<C2ParamDescriptor>> paramDescs;
+    c2_status_t err1 = intf->querySupportedParams(&paramDescs);
+    if (err1 == C2_OK) {
+        for (const std::shared_ptr<C2ParamDescriptor> &desc : paramDescs) {
+            switch ((uint32_t)desc->index()) {
+            case C2StreamHdr10PlusInfo::output::PARAM_TYPE:
+                supportsHdr10Plus = true;
+                break;
+            case C2StreamHdrStaticInfo::output::PARAM_TYPE:
+                supportsHdr = true;
+                break;
+            default:
                 break;
             }
-            caps->addProfileLevel(param.eProfile, param.eLevel);
-
-            // AVC components may not list the constrained profiles explicitly, but
-            // decoders that support a profile also support its constrained version.
-            // Encoders must explicitly support constrained profiles.
-            if (!isEncoder && strcasecmp(mediaType, MEDIA_MIMETYPE_VIDEO_AVC) == 0) {
-                if (param.eProfile == OMX_VIDEO_AVCProfileHigh) {
-                    caps->addProfileLevel(OMX_VIDEO_AVCProfileConstrainedHigh, param.eLevel);
-                } else if (param.eProfile == OMX_VIDEO_AVCProfileBaseline) {
-                    caps->addProfileLevel(OMX_VIDEO_AVCProfileConstrainedBaseline, param.eLevel);
-                }
-            }
-
-            if (index == kMaxIndicesToCheck) {
-                ALOGW("[%s] stopping checking profiles after %u: %x/%x",
-                        name, index,
-                        param.eProfile, param.eLevel);
-            }
-        }
-
-        // Color format query
-        // return colors in the order reported by the OMX component
-        // prefix "flexible" standard ones with the flexible equivalent
-        OMX_VIDEO_PARAM_PORTFORMATTYPE portFormat;
-        InitOMXParams(&portFormat);
-        portFormat.nPortIndex = isEncoder ? kPortIndexInput : kPortIndexOutput;
-        for (OMX_U32 index = 0; index <= kMaxIndicesToCheck; ++index) {
-            portFormat.nIndex = index;
-            status_t err = omxNode->getParameter(
-                    OMX_IndexParamVideoPortFormat,
-                    &portFormat, sizeof(portFormat));
-            if (err != OK) {
-                break;
-            }
-
-            OMX_U32 flexibleEquivalent;
-            if (IsFlexibleColorFormat(
-                    omxNode, portFormat.eColorFormat, false /* usingNativeWindow */,
-                    &flexibleEquivalent)) {
-                caps->addColorFormat(flexibleEquivalent);
-            }
-            caps->addColorFormat(portFormat.eColorFormat);
-
-            if (index == kMaxIndicesToCheck) {
-                ALOGW("[%s] stopping checking formats after %u: %s(%x)",
-                        name, index,
-                        asString(portFormat.eColorFormat), portFormat.eColorFormat);
-            }
-        }
-    } else if (strcasecmp(mediaType, MEDIA_MIMETYPE_AUDIO_AAC) == 0) {
-        // More audio codecs if they have profiles.
-        OMX_AUDIO_PARAM_ANDROID_PROFILETYPE param;
-        InitOMXParams(&param);
-        param.nPortIndex = isEncoder ? kPortIndexOutput : kPortIndexInput;
-        for (OMX_U32 index = 0; index <= kMaxIndicesToCheck; ++index) {
-            param.nProfileIndex = index;
-            status_t err = omxNode->getParameter(
-                    (OMX_INDEXTYPE)OMX_IndexParamAudioProfileQuerySupported,
-                    &param, sizeof(param));
-            if (err != OK) {
-                break;
-            }
-            // For audio, level is ignored.
-            caps->addProfileLevel(param.eProfile, 0 /* level */);
-
-            if (index == kMaxIndicesToCheck) {
-                ALOGW("[%s] stopping checking profiles after %u: %x",
-                        name, index,
-                        param.eProfile);
-            }
-        }
-
-        // NOTE: Without Android extensions, OMX does not provide a way to query
-        // AAC profile support
-        if (param.nProfileIndex == 0) {
-            ALOGW("component %s doesn't support profile query.", name);
         }
     }
 
-    if (isVideo && !isEncoder) {
-        native_handle_t *sidebandHandle = nullptr;
-        if (omxNode->configureVideoTunnelMode(
-                kPortIndexOutput, OMX_TRUE, 0, &sidebandHandle) == OK) {
-            // tunneled playback includes adaptive playback
-        } else {
-            // tunneled playback is not supported
-            caps->removeDetail(MediaCodecInfo::Capabilities::FEATURE_TUNNELED_PLAYBACK);
-            if (omxNode->setPortMode(
-                    kPortIndexOutput, IOMX::kPortModeDynamicANWBuffer) == OK ||
-                    omxNode->prepareForAdaptivePlayback(
-                            kPortIndexOutput, OMX_TRUE,
-                            1280 /* width */, 720 /* height */) != OK) {
-                // adaptive playback is not supported
-                caps->removeDetail(MediaCodecInfo::Capabilities::FEATURE_ADAPTIVE_PLAYBACK);
-            }
-        }
-    }
+    // For VP9, the static info is always propagated by framework.
+    supportsHdr |= (mediaType == MIMETYPE_VIDEO_VP9);
 
-    if (isVideo && isEncoder) {
-        OMX_VIDEO_CONFIG_ANDROID_INTRAREFRESHTYPE params;
-        InitOMXParams(&params);
-        params.nPortIndex = kPortIndexOutput;
-
-        OMX_VIDEO_PARAM_INTRAREFRESHTYPE fallbackParams;
-        InitOMXParams(&fallbackParams);
-        fallbackParams.nPortIndex = kPortIndexOutput;
-        fallbackParams.eRefreshMode = OMX_VIDEO_IntraRefreshCyclic;
-
-        if (omxNode->getConfig(
-                (OMX_INDEXTYPE)OMX_IndexConfigAndroidIntraRefresh,
-                &params, sizeof(params)) != OK &&
-                omxNode->getParameter(
-                    OMX_IndexParamVideoIntraRefresh, &fallbackParams,
-                    sizeof(fallbackParams)) != OK) {
-            // intra refresh is not supported
-            caps->removeDetail(MediaCodecInfo::Capabilities::FEATURE_INTRA_REFRESH);
-        }
-    }
-
-    omxNode->freeNode();
-    return OK;
-}
-
-void buildOmxInfo(const MediaCodecsXmlParser& parser,
-                  MediaCodecListWriter* writer) {
-    uint32_t omxRank = ::android::base::GetUintProperty(
-            "debug.stagefright.omx_default_rank", uint32_t(0x100));
-    for (const MediaCodecsXmlParser::Codec& codec : parser.getCodecMap()) {
-        const std::string &name = codec.first;
-        if (!hasPrefix(codec.first, "OMX.")) {
+    for (C2Value::Primitive profile : profileQuery[0].values.values) {
+        pl.profile = (C2Config::profile_t)profile.ref<uint32_t>();
+        std::vector<std::unique_ptr<C2SettingResult>> failures;
+        err = intf->config({&pl}, C2_DONT_BLOCK, &failures);
+        ALOGV("set profile to %u -> %s", pl.profile, asString(err));
+        std::vector<C2FieldSupportedValuesQuery> levelQuery = {
+            C2FieldSupportedValuesQuery::Current(C2ParamField(&pl, &pl.level))
+        };
+        err = intf->querySupportedValues(levelQuery, C2_DONT_BLOCK);
+        ALOGV("query supported levels -> %s | %s", asString(err), asString(levelQuery[0].status));
+        if (err != C2_OK || levelQuery[0].status != C2_OK
+                || levelQuery[0].values.type != C2FieldSupportedValues::VALUES
+                || levelQuery[0].values.values.size() == 0) {
             continue;
         }
-        const MediaCodecsXmlParser::CodecProperties &properties = codec.second;
-        bool encoder = properties.isEncoder;
-        std::unique_ptr<MediaCodecInfoWriter> info =
-                writer->addMediaCodecInfo();
-        info->setName(name.c_str());
-        info->setOwner("default");
-        typename std::underlying_type<MediaCodecInfo::Attributes>::type attrs = 0;
-        if (encoder) {
-            attrs |= MediaCodecInfo::kFlagIsEncoder;
-        }
-        // NOTE: we don't support software-only codecs in OMX
-        if (!hasPrefix(name, "OMX.google.")) {
-            attrs |= MediaCodecInfo::kFlagIsVendor;
-            if (properties.quirkSet.find("attribute::software-codec")
-                    == properties.quirkSet.end()) {
-                attrs |= MediaCodecInfo::kFlagIsHardwareAccelerated;
-            }
-        }
-        info->setAttributes(attrs);
-        info->setRank(omxRank);
-        // OMX components don't have aliases
-        for (const MediaCodecsXmlParser::Type &type : properties.typeMap) {
-            const std::string &mediaType = type.first;
-            std::unique_ptr<MediaCodecInfo::CapabilitiesWriter> caps =
-                    info->addMediaType(mediaType.c_str());
-            const MediaCodecsXmlParser::AttributeMap &attrMap = type.second;
-            for (const MediaCodecsXmlParser::Attribute& attr : attrMap) {
-                const std::string &key = attr.first;
-                const std::string &value = attr.second;
-                if (hasPrefix(key, "feature-") &&
-                        !hasPrefix(key, "feature-bitrate-modes")) {
-                    caps->addDetail(key.c_str(), hasPrefix(value, "1") ? 1 : 0);
-                } else {
-                    caps->addDetail(key.c_str(), value.c_str());
+
+        C2Value::Primitive level = levelQuery[0].values.values.back();
+        pl.level = (C2Config::level_t)level.ref<uint32_t>();
+        ALOGV("supporting level: %u", pl.level);
+        int32_t sdkProfile, sdkLevel;
+        if (mapper && mapper->mapProfile(pl.profile, &sdkProfile)
+                && mapper->mapLevel(pl.level, &sdkLevel)) {
+            caps->addProfileLevel((uint32_t)sdkProfile, (uint32_t)sdkLevel);
+            // also list HDR profiles if component supports HDR
+            if (supportsHdr) {
+                auto hdrMapper = C2Mapper::GetHdrProfileLevelMapper(trait.mediaType);
+                if (hdrMapper && hdrMapper->mapProfile(pl.profile, &sdkProfile)) {
+                    caps->addProfileLevel((uint32_t)sdkProfile, (uint32_t)sdkLevel);
+                }
+                if (supportsHdr10Plus) {
+                    hdrMapper = C2Mapper::GetHdrProfileLevelMapper(
+                            trait.mediaType, true /*isHdr10Plus*/);
+                    if (hdrMapper && hdrMapper->mapProfile(pl.profile, &sdkProfile)) {
+                        caps->addProfileLevel((uint32_t)sdkProfile, (uint32_t)sdkLevel);
+                    }
                 }
             }
-            status_t err = queryOmxCapabilities(
-                    name.c_str(),
-                    mediaType.c_str(),
-                    encoder,
-                    caps.get());
-            if (err != OK) {
-                ALOGI("Failed to query capabilities for %s (media type: %s). Error: %d",
-                        name.c_str(),
-                        mediaType.c_str(),
-                        static_cast<int>(err));
+        } else if (!mapper) {
+            caps->addProfileLevel(pl.profile, pl.level);
+        }
+
+        // for H.263 also advertise the second highest level if the
+        // codec supports level 45, as level 45 only covers level 10
+        // TODO: move this to some form of a setting so it does not
+        // have to be here
+        if (mediaType == MIMETYPE_VIDEO_H263) {
+            C2Config::level_t nextLevel = C2Config::LEVEL_UNUSED;
+            for (C2Value::Primitive v : levelQuery[0].values.values) {
+                C2Config::level_t level = (C2Config::level_t)v.ref<uint32_t>();
+                if (level < C2Config::LEVEL_H263_45 && level > nextLevel) {
+                    nextLevel = level;
+                }
             }
+            if (nextLevel != C2Config::LEVEL_UNUSED
+                    && nextLevel != pl.level
+                    && mapper
+                    && mapper->mapProfile(pl.profile, &sdkProfile)
+                    && mapper->mapLevel(nextLevel, &sdkLevel)) {
+                caps->addProfileLevel(
+                        (uint32_t)sdkProfile, (uint32_t)sdkLevel);
+            }
+        }
+    }
+}
+
+void addSupportedColorFormats(
+        std::shared_ptr<Codec2Client::Interface> intf,
+        MediaCodecInfo::CapabilitiesWriter *caps,
+        const Traits& trait, const std::string &mediaType) {
+    (void)intf;
+
+    // TODO: get this from intf() as well, but how do we map them to
+    // MediaCodec color formats?
+    bool encoder = trait.kind == C2Component::KIND_ENCODER;
+    if (mediaType.find("video") != std::string::npos) {
+        // vendor video codecs prefer opaque format
+        if (trait.name.find("android") == std::string::npos) {
+            caps->addColorFormat(COLOR_FormatSurface);
+        }
+        caps->addColorFormat(COLOR_FormatYUV420Flexible);
+        caps->addColorFormat(COLOR_FormatYUV420Planar);
+        caps->addColorFormat(COLOR_FormatYUV420SemiPlanar);
+        caps->addColorFormat(COLOR_FormatYUV420PackedPlanar);
+        caps->addColorFormat(COLOR_FormatYUV420PackedSemiPlanar);
+        // framework video encoders must support surface format, though it is unclear
+        // that they will be able to map it if it is opaque
+        if (encoder && trait.name.find("android") != std::string::npos) {
+            caps->addColorFormat(COLOR_FormatSurface);
         }
     }
 }
@@ -335,7 +219,7 @@
     // properly. (Assume "full" behavior eventually.)
     //
     // debug.stagefright.ccodec supports 5 values.
-    //   0 - Only OMX components are available.
+    //   0 - No Codec 2.0 components are available.
     //   1 - Audio decoders and encoders with prefix "c2.android." are available
     //       and ranked first.
     //       All other components with prefix "c2.android." are available with
@@ -366,306 +250,156 @@
 
     MediaCodecsXmlParser parser(
             MediaCodecsXmlParser::defaultSearchDirs,
-            option == 0 ? "media_codecs.xml" :
-                          "media_codecs_c2.xml",
-            option == 0 ? "media_codecs_performance.xml" :
-                          "media_codecs_performance_c2.xml");
+            "media_codecs_c2.xml",
+            "media_codecs_performance_c2.xml");
     if (parser.getParsingStatus() != OK) {
         ALOGD("XML parser no good");
         return OK;
     }
 
-    bool surfaceTest(Codec2Client::CreateInputSurface());
-    if (option == 0 || (option != 4 && !surfaceTest)) {
-        buildOmxInfo(parser, writer);
-    }
-
     for (const Traits& trait : traits) {
         C2Component::rank_t rank = trait.rank;
 
-        std::shared_ptr<Codec2Client::Interface> intf =
-            Codec2Client::CreateInterfaceByName(trait.name.c_str());
-        if (!intf || parser.getCodecMap().count(intf->getName()) == 0) {
-            ALOGD("%s not found in xml", trait.name.c_str());
-            continue;
-        }
-        std::string canonName = intf->getName();
-
-        // TODO: Remove this block once all codecs are enabled by default.
-        switch (option) {
-        case 0:
-            continue;
-        case 1:
-            if (hasPrefix(canonName, "c2.vda.")) {
-                break;
+        // Interface must be accessible for us to list the component, and there also
+        // must be an XML entry for the codec. Codec aliases listed in the traits
+        // allow additional XML entries to be specified for each alias. These will
+        // be listed as separate codecs. If no XML entry is specified for an alias,
+        // those will be treated as an additional alias specified in the XML entry
+        // for the interface name.
+        std::vector<std::string> nameAndAliases = trait.aliases;
+        nameAndAliases.insert(nameAndAliases.begin(), trait.name);
+        for (const std::string &nameOrAlias : nameAndAliases) {
+            bool isAlias = trait.name != nameOrAlias;
+            std::shared_ptr<Codec2Client::Interface> intf =
+                Codec2Client::CreateInterfaceByName(nameOrAlias.c_str());
+            if (!intf) {
+                ALOGD("could not create interface for %s'%s'",
+                        isAlias ? "alias " : "",
+                        nameOrAlias.c_str());
+                continue;
             }
-            if (hasPrefix(canonName, "c2.android.")) {
-                if (trait.domain == C2Component::DOMAIN_AUDIO) {
+            if (parser.getCodecMap().count(nameOrAlias) == 0) {
+                if (isAlias) {
+                    std::unique_ptr<MediaCodecInfoWriter> baseCodecInfo =
+                        writer->findMediaCodecInfo(trait.name.c_str());
+                    if (!baseCodecInfo) {
+                        ALOGD("alias '%s' not found in xml but canonical codec info '%s' missing",
+                                nameOrAlias.c_str(),
+                                trait.name.c_str());
+                    } else {
+                        ALOGD("alias '%s' not found in xml; use an XML <Alias> tag for this",
+                                nameOrAlias.c_str());
+                        // merge alias into existing codec
+                        baseCodecInfo->addAlias(nameOrAlias.c_str());
+                    }
+                } else {
+                    ALOGD("component '%s' not found in xml", trait.name.c_str());
+                }
+                continue;
+            }
+            std::string canonName = trait.name;
+
+            // TODO: Remove this block once all codecs are enabled by default.
+            switch (option) {
+            case 0:
+                continue;
+            case 1:
+                if (hasPrefix(canonName, "c2.vda.")) {
+                    break;
+                }
+                if (hasPrefix(canonName, "c2.android.")) {
+                    if (trait.domain == C2Component::DOMAIN_AUDIO) {
+                        rank = 1;
+                        break;
+                    }
+                    break;
+                }
+                if (hasSuffix(canonName, ".avc.decoder") ||
+                        hasSuffix(canonName, ".avc.encoder")) {
+                    rank = std::numeric_limits<decltype(rank)>::max();
+                    break;
+                }
+                continue;
+            case 2:
+                if (hasPrefix(canonName, "c2.vda.")) {
+                    break;
+                }
+                if (hasPrefix(canonName, "c2.android.")) {
                     rank = 1;
                     break;
                 }
+                if (hasSuffix(canonName, ".avc.decoder") ||
+                        hasSuffix(canonName, ".avc.encoder")) {
+                    rank = std::numeric_limits<decltype(rank)>::max();
+                    break;
+                }
+                continue;
+            case 3:
+                if (hasPrefix(canonName, "c2.android.")) {
+                    rank = 1;
+                }
                 break;
             }
-            if (hasSuffix(canonName, ".avc.decoder") ||
-                    hasSuffix(canonName, ".avc.encoder")) {
-                rank = std::numeric_limits<decltype(rank)>::max();
-                break;
-            }
-            continue;
-        case 2:
-            if (hasPrefix(canonName, "c2.vda.")) {
-                break;
-            }
-            if (hasPrefix(canonName, "c2.android.")) {
-                rank = 1;
-                break;
-            }
-            if (hasSuffix(canonName, ".avc.decoder") ||
-                    hasSuffix(canonName, ".avc.encoder")) {
-                rank = std::numeric_limits<decltype(rank)>::max();
-                break;
-            }
-            continue;
-        case 3:
-            if (hasPrefix(canonName, "c2.android.")) {
-                rank = 1;
-            }
-            break;
-        }
 
-        ALOGV("canonName = %s", canonName.c_str());
-        std::unique_ptr<MediaCodecInfoWriter> codecInfo = writer->addMediaCodecInfo();
-        codecInfo->setName(trait.name.c_str());
-        codecInfo->setOwner(("codec2::" + trait.owner).c_str());
-        const MediaCodecsXmlParser::CodecProperties &codec = parser.getCodecMap().at(canonName);
+            ALOGV("adding codec entry for '%s'", nameOrAlias.c_str());
+            std::unique_ptr<MediaCodecInfoWriter> codecInfo = writer->addMediaCodecInfo();
+            codecInfo->setName(nameOrAlias.c_str());
+            codecInfo->setOwner(("codec2::" + trait.owner).c_str());
+            const MediaCodecsXmlParser::CodecProperties &codec =
+                parser.getCodecMap().at(nameOrAlias);
 
-        bool encoder = trait.kind == C2Component::KIND_ENCODER;
-        typename std::underlying_type<MediaCodecInfo::Attributes>::type attrs = 0;
+            bool encoder = trait.kind == C2Component::KIND_ENCODER;
+            typename std::underlying_type<MediaCodecInfo::Attributes>::type attrs = 0;
 
-        if (encoder) {
-            attrs |= MediaCodecInfo::kFlagIsEncoder;
-        }
-        if (trait.owner == "software") {
-            attrs |= MediaCodecInfo::kFlagIsSoftwareOnly;
-        } else {
-            attrs |= MediaCodecInfo::kFlagIsVendor;
-            if (trait.owner == "vendor-software") {
+            if (encoder) {
+                attrs |= MediaCodecInfo::kFlagIsEncoder;
+            }
+            if (trait.owner == "software") {
                 attrs |= MediaCodecInfo::kFlagIsSoftwareOnly;
-            } else if (codec.quirkSet.find("attribute::software-codec") == codec.quirkSet.end()) {
-                attrs |= MediaCodecInfo::kFlagIsHardwareAccelerated;
-            }
-        }
-        codecInfo->setAttributes(attrs);
-        codecInfo->setRank(rank);
-
-        for (const std::string &alias : codec.aliases) {
-            codecInfo->addAlias(alias.c_str());
-        }
-
-        for (auto typeIt = codec.typeMap.begin(); typeIt != codec.typeMap.end(); ++typeIt) {
-            const std::string &mediaType = typeIt->first;
-            const MediaCodecsXmlParser::AttributeMap &attrMap = typeIt->second;
-            std::unique_ptr<MediaCodecInfo::CapabilitiesWriter> caps =
-                codecInfo->addMediaType(mediaType.c_str());
-            for (auto attrIt = attrMap.begin(); attrIt != attrMap.end(); ++attrIt) {
-                std::string key, value;
-                std::tie(key, value) = *attrIt;
-                if (key.find("feature-") == 0 && key.find("feature-bitrate-modes") != 0) {
-                    caps->addDetail(key.c_str(), std::stoi(value));
-                } else {
-                    caps->addDetail(key.c_str(), value.c_str());
+            } else {
+                attrs |= MediaCodecInfo::kFlagIsVendor;
+                if (trait.owner == "vendor-software") {
+                    attrs |= MediaCodecInfo::kFlagIsSoftwareOnly;
+                } else if (codec.quirkSet.find("attribute::software-codec")
+                        == codec.quirkSet.end()) {
+                    attrs |= MediaCodecInfo::kFlagIsHardwareAccelerated;
                 }
             }
-
-            bool gotProfileLevels = false;
-            if (intf) {
-                std::shared_ptr<C2Mapper::ProfileLevelMapper> mapper =
-                    C2Mapper::GetProfileLevelMapper(trait.mediaType);
-                // if we don't know the media type, pass through all values unmapped
-
-                // TODO: we cannot find levels that are local 'maxima' without knowing the coding
-                // e.g. H.263 level 45 and level 30 could be two values for highest level as
-                // they don't include one another. For now we use the last supported value.
-                C2StreamProfileLevelInfo pl(encoder /* output */, 0u);
-                std::vector<C2FieldSupportedValuesQuery> profileQuery = {
-                    C2FieldSupportedValuesQuery::Possible(C2ParamField(&pl, &pl.profile))
-                };
-
-                c2_status_t err = intf->querySupportedValues(profileQuery, C2_DONT_BLOCK);
-                ALOGV("query supported profiles -> %s | %s",
-                        asString(err), asString(profileQuery[0].status));
-                if (err == C2_OK && profileQuery[0].status == C2_OK) {
-                    if (profileQuery[0].values.type == C2FieldSupportedValues::VALUES) {
-                        std::vector<std::shared_ptr<C2ParamDescriptor>> paramDescs;
-                        c2_status_t err1 = intf->querySupportedParams(&paramDescs);
-                        bool isHdr = false, isHdr10Plus = false;
-                        if (err1 == C2_OK) {
-                            for (const std::shared_ptr<C2ParamDescriptor> &desc : paramDescs) {
-                                if ((uint32_t)desc->index() ==
-                                        C2StreamHdr10PlusInfo::output::PARAM_TYPE) {
-                                    isHdr10Plus = true;
-                                } else if ((uint32_t)desc->index() ==
-                                        C2StreamHdrStaticInfo::output::PARAM_TYPE) {
-                                    isHdr = true;
-                                }
-                            }
-                        }
-                        // For VP9, the static info is always propagated by framework.
-                        isHdr |= (mediaType == MIMETYPE_VIDEO_VP9);
-
-                        for (C2Value::Primitive profile : profileQuery[0].values.values) {
-                            pl.profile = (C2Config::profile_t)profile.ref<uint32_t>();
-                            std::vector<std::unique_ptr<C2SettingResult>> failures;
-                            err = intf->config({&pl}, C2_DONT_BLOCK, &failures);
-                            ALOGV("set profile to %u -> %s", pl.profile, asString(err));
-                            std::vector<C2FieldSupportedValuesQuery> levelQuery = {
-                                C2FieldSupportedValuesQuery::Current(C2ParamField(&pl, &pl.level))
-                            };
-                            err = intf->querySupportedValues(levelQuery, C2_DONT_BLOCK);
-                            ALOGV("query supported levels -> %s | %s",
-                                    asString(err), asString(levelQuery[0].status));
-                            if (err == C2_OK && levelQuery[0].status == C2_OK) {
-                                if (levelQuery[0].values.type == C2FieldSupportedValues::VALUES
-                                        && levelQuery[0].values.values.size() > 0) {
-                                    C2Value::Primitive level = levelQuery[0].values.values.back();
-                                    pl.level = (C2Config::level_t)level.ref<uint32_t>();
-                                    ALOGV("supporting level: %u", pl.level);
-                                    int32_t sdkProfile, sdkLevel;
-                                    if (mapper && mapper->mapProfile(pl.profile, &sdkProfile)
-                                            && mapper->mapLevel(pl.level, &sdkLevel)) {
-                                        caps->addProfileLevel(
-                                                (uint32_t)sdkProfile, (uint32_t)sdkLevel);
-                                        gotProfileLevels = true;
-                                        if (isHdr) {
-                                            auto hdrMapper = C2Mapper::GetHdrProfileLevelMapper(
-                                                    trait.mediaType);
-                                            if (hdrMapper && hdrMapper->mapProfile(
-                                                    pl.profile, &sdkProfile)) {
-                                                caps->addProfileLevel(
-                                                        (uint32_t)sdkProfile,
-                                                        (uint32_t)sdkLevel);
-                                            }
-                                            if (isHdr10Plus) {
-                                                hdrMapper = C2Mapper::GetHdrProfileLevelMapper(
-                                                        trait.mediaType, true /*isHdr10Plus*/);
-                                                if (hdrMapper && hdrMapper->mapProfile(
-                                                        pl.profile, &sdkProfile)) {
-                                                    caps->addProfileLevel(
-                                                            (uint32_t)sdkProfile,
-                                                            (uint32_t)sdkLevel);
-                                                }
-                                            }
-                                        }
-                                    } else if (!mapper) {
-                                        caps->addProfileLevel(pl.profile, pl.level);
-                                        gotProfileLevels = true;
-                                    }
-
-                                    // for H.263 also advertise the second highest level if the
-                                    // codec supports level 45, as level 45 only covers level 10
-                                    // TODO: move this to some form of a setting so it does not
-                                    // have to be here
-                                    if (mediaType == MIMETYPE_VIDEO_H263) {
-                                        C2Config::level_t nextLevel = C2Config::LEVEL_UNUSED;
-                                        for (C2Value::Primitive v : levelQuery[0].values.values) {
-                                            C2Config::level_t level =
-                                                (C2Config::level_t)v.ref<uint32_t>();
-                                            if (level < C2Config::LEVEL_H263_45
-                                                    && level > nextLevel) {
-                                                nextLevel = level;
-                                            }
-                                        }
-                                        if (nextLevel != C2Config::LEVEL_UNUSED
-                                                && nextLevel != pl.level
-                                                && mapper
-                                                && mapper->mapProfile(pl.profile, &sdkProfile)
-                                                && mapper->mapLevel(nextLevel, &sdkLevel)) {
-                                            caps->addProfileLevel(
-                                                    (uint32_t)sdkProfile, (uint32_t)sdkLevel);
-                                        }
-                                    }
-                                }
-                            }
-                        }
-                    }
+            codecInfo->setAttributes(attrs);
+            if (!codec.rank.empty()) {
+                uint32_t xmlRank;
+                char dummy;
+                if (sscanf(codec.rank.c_str(), "%u%c", &xmlRank, &dummy) == 1) {
+                    rank = xmlRank;
                 }
             }
+            codecInfo->setRank(rank);
 
-            if (!gotProfileLevels) {
-                if (mediaType == MIMETYPE_VIDEO_VP9) {
-                    if (encoder) {
-                        caps->addProfileLevel(VP9Profile0,    VP9Level41);
-                    } else {
-                        caps->addProfileLevel(VP9Profile0,    VP9Level5);
-                        caps->addProfileLevel(VP9Profile2,    VP9Level5);
-                        caps->addProfileLevel(VP9Profile2HDR, VP9Level5);
-                    }
-                } else if (mediaType == MIMETYPE_VIDEO_AV1 && !encoder) {
-                    caps->addProfileLevel(AV1Profile0,      AV1Level2);
-                    caps->addProfileLevel(AV1Profile0,      AV1Level21);
-                    caps->addProfileLevel(AV1Profile1,      AV1Level22);
-                    caps->addProfileLevel(AV1Profile1,      AV1Level3);
-                    caps->addProfileLevel(AV1Profile2,      AV1Level31);
-                    caps->addProfileLevel(AV1Profile2,      AV1Level32);
-                } else if (mediaType == MIMETYPE_VIDEO_HEVC && !encoder) {
-                    caps->addProfileLevel(HEVCProfileMain,      HEVCMainTierLevel51);
-                    caps->addProfileLevel(HEVCProfileMainStill, HEVCMainTierLevel51);
-                } else if (mediaType == MIMETYPE_VIDEO_VP8) {
-                    if (encoder) {
-                        caps->addProfileLevel(VP8ProfileMain, VP8Level_Version0);
-                    } else {
-                        caps->addProfileLevel(VP8ProfileMain, VP8Level_Version0);
-                    }
-                } else if (mediaType == MIMETYPE_VIDEO_AVC) {
-                    if (encoder) {
-                        caps->addProfileLevel(AVCProfileBaseline,            AVCLevel41);
-//                      caps->addProfileLevel(AVCProfileConstrainedBaseline, AVCLevel41);
-                        caps->addProfileLevel(AVCProfileMain,                AVCLevel41);
-                    } else {
-                        caps->addProfileLevel(AVCProfileBaseline,            AVCLevel52);
-                        caps->addProfileLevel(AVCProfileConstrainedBaseline, AVCLevel52);
-                        caps->addProfileLevel(AVCProfileMain,                AVCLevel52);
-                        caps->addProfileLevel(AVCProfileConstrainedHigh,     AVCLevel52);
-                        caps->addProfileLevel(AVCProfileHigh,                AVCLevel52);
-                    }
-                } else if (mediaType == MIMETYPE_VIDEO_MPEG4) {
-                    if (encoder) {
-                        caps->addProfileLevel(MPEG4ProfileSimple,  MPEG4Level2);
-                    } else {
-                        caps->addProfileLevel(MPEG4ProfileSimple,  MPEG4Level3);
-                    }
-                } else if (mediaType == MIMETYPE_VIDEO_H263) {
-                    if (encoder) {
-                        caps->addProfileLevel(H263ProfileBaseline, H263Level45);
-                    } else {
-                        caps->addProfileLevel(H263ProfileBaseline, H263Level30);
-                        caps->addProfileLevel(H263ProfileBaseline, H263Level45);
-                        caps->addProfileLevel(H263ProfileISWV2,    H263Level30);
-                        caps->addProfileLevel(H263ProfileISWV2,    H263Level45);
-                    }
-                } else if (mediaType == MIMETYPE_VIDEO_MPEG2 && !encoder) {
-                    caps->addProfileLevel(MPEG2ProfileSimple, MPEG2LevelHL);
-                    caps->addProfileLevel(MPEG2ProfileMain,   MPEG2LevelHL);
-                }
+            for (const std::string &alias : codec.aliases) {
+                ALOGV("adding alias '%s'", alias.c_str());
+                codecInfo->addAlias(alias.c_str());
             }
 
-            // TODO: get this from intf() as well, but how do we map them to
-            // MediaCodec color formats?
-            if (mediaType.find("video") != std::string::npos) {
-                // vendor video codecs prefer opaque format
-                if (trait.name.find("android") == std::string::npos) {
-                    caps->addColorFormat(COLOR_FormatSurface);
+            for (auto typeIt = codec.typeMap.begin(); typeIt != codec.typeMap.end(); ++typeIt) {
+                const std::string &mediaType = typeIt->first;
+                const MediaCodecsXmlParser::AttributeMap &attrMap = typeIt->second;
+                std::unique_ptr<MediaCodecInfo::CapabilitiesWriter> caps =
+                    codecInfo->addMediaType(mediaType.c_str());
+                for (auto attrIt = attrMap.begin(); attrIt != attrMap.end(); ++attrIt) {
+                    std::string key, value;
+                    std::tie(key, value) = *attrIt;
+                    if (key.find("feature-") == 0 && key.find("feature-bitrate-modes") != 0) {
+                        int32_t intValue = 0;
+                        // Ignore trailing bad characters and default to 0.
+                        (void)sscanf(value.c_str(), "%d", &intValue);
+                        caps->addDetail(key.c_str(), intValue);
+                    } else {
+                        caps->addDetail(key.c_str(), value.c_str());
+                    }
                 }
-                caps->addColorFormat(COLOR_FormatYUV420Flexible);
-                caps->addColorFormat(COLOR_FormatYUV420Planar);
-                caps->addColorFormat(COLOR_FormatYUV420SemiPlanar);
-                caps->addColorFormat(COLOR_FormatYUV420PackedPlanar);
-                caps->addColorFormat(COLOR_FormatYUV420PackedSemiPlanar);
-                // framework video encoders must support surface format, though it is unclear
-                // that they will be able to map it if it is opaque
-                if (encoder && trait.name.find("android") != std::string::npos) {
-                    caps->addColorFormat(COLOR_FormatSurface);
-                }
+
+                addSupportedProfileLevels(intf, caps.get(), trait, mediaType);
+                addSupportedColorFormats(intf, caps.get(), trait, mediaType);
             }
         }
     }
@@ -677,4 +411,3 @@
 extern "C" android::MediaCodecListBuilderBase *CreateBuilder() {
     return new android::Codec2InfoBuilder;
 }
-
diff --git a/media/codec2/vndk/C2Store.cpp b/media/codec2/vndk/C2Store.cpp
index dc7e89c..f07d9b0 100644
--- a/media/codec2/vndk/C2Store.cpp
+++ b/media/codec2/vndk/C2Store.cpp
@@ -517,7 +517,6 @@
          *
          * \note Only used by ComponentLoader.
          *
-         * \param alias[in]   module alias
          * \param libPath[in] library path
          *
          * \retval C2_OK        the component module has been successfully loaded
@@ -527,7 +526,7 @@
          * \retval C2_REFUSED   permission denied to load the component module (unexpected)
          * \retval C2_TIMED_OUT could not load the module within the time limit (unexpected)
          */
-        c2_status_t init(std::string alias, std::string libPath);
+        c2_status_t init(std::string libPath);
 
         virtual ~ComponentModule() override;
 
@@ -570,7 +569,7 @@
             std::shared_ptr<ComponentModule> localModule = mModule.lock();
             if (localModule == nullptr) {
                 localModule = std::make_shared<ComponentModule>();
-                res = localModule->init(mAlias, mLibPath);
+                res = localModule->init(mLibPath);
                 if (res == C2_OK) {
                     mModule = localModule;
                 }
@@ -582,13 +581,12 @@
         /**
          * Creates a component loader for a specific library path (or name).
          */
-        ComponentLoader(std::string alias, std::string libPath)
-            : mAlias(alias), mLibPath(libPath) {}
+        ComponentLoader(std::string libPath)
+            : mLibPath(libPath) {}
 
     private:
         std::mutex mMutex; ///< mutex guarding the module
         std::weak_ptr<ComponentModule> mModule; ///< weak reference to the loaded module
-        std::string mAlias; ///< component alias
         std::string mLibPath; ///< library path
     };
 
@@ -624,9 +622,10 @@
     };
 
     /**
-     * Retrieves the component loader for a component.
+     * Retrieves the component module for a component.
      *
-     * \return a non-ref-holding pointer to the component loader.
+     * \param module pointer to a shared_pointer where the component module will be stored on
+     *               success.
      *
      * \retval C2_OK        the component loader has been successfully retrieved
      * \retval C2_NO_MEMORY not enough memory to locate the component loader
@@ -640,16 +639,25 @@
      *                      component but some components could not be loaded due to lack of
      *                      permissions)
      */
-    c2_status_t findComponent(C2String name, ComponentLoader **loader);
+    c2_status_t findComponent(C2String name, std::shared_ptr<ComponentModule> *module);
 
-    std::map<C2String, ComponentLoader> mComponents; ///< map of name -> components
-    std::vector<C2String> mComponentsList; ///< list of components
+    /**
+     * Loads each component module and discover its contents.
+     */
+    void visitComponents();
+
+    std::mutex mMutex; ///< mutex guarding the component lists during construction
+    bool mVisited; ///< component modules visited
+    std::map<C2String, ComponentLoader> mComponents; ///< path -> component module
+    std::map<C2String, C2String> mComponentNameToPath; ///< name -> path
+    std::vector<std::shared_ptr<const C2Component::Traits>> mComponentList;
+
     std::shared_ptr<C2ReflectorHelper> mReflector;
     Interface mInterface;
 };
 
 c2_status_t C2PlatformComponentStore::ComponentModule::init(
-        std::string alias, std::string libPath) {
+        std::string libPath) {
     ALOGV("in %s", __func__);
     ALOGV("loading dll");
     mLibHandle = dlopen(libPath.c_str(), RTLD_NOW|RTLD_NODELETE);
@@ -684,13 +692,27 @@
 
     std::shared_ptr<C2Component::Traits> traits(new (std::nothrow) C2Component::Traits);
     if (traits) {
-        if (alias != intf->getName()) {
-            ALOGV("%s is alias to %s", alias.c_str(), intf->getName().c_str());
+        traits->name = intf->getName();
+
+        C2ComponentKindSetting kind;
+        C2ComponentDomainSetting domain;
+        res = intf->query_vb({ &kind, &domain }, {}, C2_MAY_BLOCK, nullptr);
+        bool fixDomain = res != C2_OK;
+        if (res == C2_OK) {
+            traits->kind = kind.value;
+            traits->domain = domain.value;
+        } else {
+            // TODO: remove this fall-back
+            ALOGD("failed to query interface for kind and domain: %d", res);
+
+            traits->kind =
+                (traits->name.find("encoder") != std::string::npos) ? C2Component::KIND_ENCODER :
+                (traits->name.find("decoder") != std::string::npos) ? C2Component::KIND_DECODER :
+                C2Component::KIND_OTHER;
         }
-        traits->name = alias;
-        // TODO: get this from interface properly.
-        bool encoder = (traits->name.find("encoder") != std::string::npos);
-        uint32_t mediaTypeIndex = encoder ? C2PortMimeConfig::output::PARAM_TYPE
+
+        uint32_t mediaTypeIndex =
+                traits->kind == C2Component::KIND_ENCODER ? C2PortMimeConfig::output::PARAM_TYPE
                 : C2PortMimeConfig::input::PARAM_TYPE;
         std::vector<std::unique_ptr<C2Param>> params;
         res = intf->query_vb({}, { mediaTypeIndex }, C2_MAY_BLOCK, &params);
@@ -702,29 +724,54 @@
             ALOGD("failed to query interface: unexpected vector size: %zu", params.size());
             return mInit;
         }
-        C2PortMimeConfig *mediaTypeConfig = (C2PortMimeConfig *)(params[0].get());
+        C2PortMimeConfig *mediaTypeConfig = C2PortMimeConfig::From(params[0].get());
         if (mediaTypeConfig == nullptr) {
             ALOGD("failed to query media type");
             return mInit;
         }
-        traits->mediaType = mediaTypeConfig->m.value;
-        // TODO: get this properly.
-        traits->rank = 0x200;
+        traits->mediaType =
+            std::string(mediaTypeConfig->m.value,
+                        strnlen(mediaTypeConfig->m.value, mediaTypeConfig->flexCount()));
 
-        // TODO: define these values properly
-        bool decoder = (traits->name.find("decoder") != std::string::npos);
-        traits->kind =
-                decoder ? C2Component::KIND_DECODER :
-                encoder ? C2Component::KIND_ENCODER :
-                C2Component::KIND_OTHER;
-        if (strncmp(traits->mediaType.c_str(), "audio/", 6) == 0) {
-            traits->domain = C2Component::DOMAIN_AUDIO;
-        } else if (strncmp(traits->mediaType.c_str(), "video/", 6) == 0) {
-            traits->domain = C2Component::DOMAIN_VIDEO;
-        } else if (strncmp(traits->mediaType.c_str(), "image/", 6) == 0) {
-            traits->domain = C2Component::DOMAIN_IMAGE;
-        } else {
-            traits->domain = C2Component::DOMAIN_OTHER;
+        if (fixDomain) {
+            if (strncmp(traits->mediaType.c_str(), "audio/", 6) == 0) {
+                traits->domain = C2Component::DOMAIN_AUDIO;
+            } else if (strncmp(traits->mediaType.c_str(), "video/", 6) == 0) {
+                traits->domain = C2Component::DOMAIN_VIDEO;
+            } else if (strncmp(traits->mediaType.c_str(), "image/", 6) == 0) {
+                traits->domain = C2Component::DOMAIN_IMAGE;
+            } else {
+                traits->domain = C2Component::DOMAIN_OTHER;
+            }
+        }
+
+        // TODO: get this properly from the store during emplace
+        switch (traits->domain) {
+        case C2Component::DOMAIN_AUDIO:
+            traits->rank = 8;
+            break;
+        default:
+            traits->rank = 512;
+        }
+
+        params.clear();
+        res = intf->query_vb({}, { C2ComponentAliasesSetting::PARAM_TYPE }, C2_MAY_BLOCK, &params);
+        if (res == C2_OK && params.size() == 1u) {
+            C2ComponentAliasesSetting *aliasesSetting =
+                C2ComponentAliasesSetting::From(params[0].get());
+            if (aliasesSetting) {
+                // Split aliases on ','
+                // This looks simpler in plain C and even std::string would still make a copy.
+                char *aliases = ::strndup(aliasesSetting->m.value, aliasesSetting->flexCount());
+                ALOGD("'%s' has aliases: '%s'", intf->getName().c_str(), aliases);
+
+                for (char *tok, *ptr, *str = aliases; (tok = ::strtok_r(str, ",", &ptr));
+                        str = nullptr) {
+                    traits->aliases.push_back(tok);
+                    ALOGD("adding alias: '%s'", tok);
+                }
+                free(aliases);
+            }
         }
     }
     mTraits = traits;
@@ -783,82 +830,45 @@
 }
 
 C2PlatformComponentStore::C2PlatformComponentStore()
-    : mReflector(std::make_shared<C2ReflectorHelper>()),
+    : mVisited(false),
+      mReflector(std::make_shared<C2ReflectorHelper>()),
       mInterface(mReflector) {
 
-    auto emplace = [this](const char *alias, const char *libPath) {
-        // ComponentLoader is neither copiable nor movable, so it must be
-        // constructed in-place. Now ComponentLoader takes two arguments in
-        // constructor, so we need to use piecewise_construct to achieve this
-        // behavior.
-        mComponents.emplace(
-                std::piecewise_construct,
-                std::forward_as_tuple(alias),
-                std::forward_as_tuple(alias, libPath));
-        mComponentsList.emplace_back(alias);
+    auto emplace = [this](const char *libPath) {
+        mComponents.emplace(libPath, libPath);
     };
-    // TODO: move this also into a .so so it can be updated
-    emplace("c2.android.avc.decoder", "libcodec2_soft_avcdec.so");
-    emplace("c2.android.avc.encoder", "libcodec2_soft_avcenc.so");
-    emplace("c2.android.aac.decoder", "libcodec2_soft_aacdec.so");
-    emplace("c2.android.aac.encoder", "libcodec2_soft_aacenc.so");
-    emplace("c2.android.amrnb.decoder", "libcodec2_soft_amrnbdec.so");
-    emplace("c2.android.amrnb.encoder", "libcodec2_soft_amrnbenc.so");
-    emplace("c2.android.amrwb.decoder", "libcodec2_soft_amrwbdec.so");
-    emplace("c2.android.amrwb.encoder", "libcodec2_soft_amrwbenc.so");
-    emplace("c2.android.hevc.decoder", "libcodec2_soft_hevcdec.so");
-    emplace("c2.android.g711.alaw.decoder", "libcodec2_soft_g711alawdec.so");
-    emplace("c2.android.g711.mlaw.decoder", "libcodec2_soft_g711mlawdec.so");
-    emplace("c2.android.mpeg2.decoder", "libcodec2_soft_mpeg2dec.so");
-    emplace("c2.android.h263.decoder", "libcodec2_soft_h263dec.so");
-    emplace("c2.android.h263.encoder", "libcodec2_soft_h263enc.so");
-    emplace("c2.android.mpeg4.decoder", "libcodec2_soft_mpeg4dec.so");
-    emplace("c2.android.mpeg4.encoder", "libcodec2_soft_mpeg4enc.so");
-    emplace("c2.android.mp3.decoder", "libcodec2_soft_mp3dec.so");
-    emplace("c2.android.vorbis.decoder", "libcodec2_soft_vorbisdec.so");
-    emplace("c2.android.opus.decoder", "libcodec2_soft_opusdec.so");
-    emplace("c2.android.opus.encoder", "libcodec2_soft_opusenc.so");
-    emplace("c2.android.vp8.decoder", "libcodec2_soft_vp8dec.so");
-    emplace("c2.android.vp9.decoder", "libcodec2_soft_vp9dec.so");
-    emplace("c2.android.vp8.encoder", "libcodec2_soft_vp8enc.so");
-    emplace("c2.android.vp9.encoder", "libcodec2_soft_vp9enc.so");
-    emplace("c2.android.av1.decoder", "libcodec2_soft_av1dec.so");
-    emplace("c2.android.raw.decoder", "libcodec2_soft_rawdec.so");
-    emplace("c2.android.flac.decoder", "libcodec2_soft_flacdec.so");
-    emplace("c2.android.flac.encoder", "libcodec2_soft_flacenc.so");
-    emplace("c2.android.gsm.decoder", "libcodec2_soft_gsmdec.so");
-    emplace("c2.android.xaac.decoder", "libcodec2_soft_xaacdec.so");
 
-    // "Aliases"
-    // TODO: use aliases proper from C2Component::Traits
-    emplace("OMX.google.h264.decoder", "libcodec2_soft_avcdec.so");
-    emplace("OMX.google.h264.encoder", "libcodec2_soft_avcenc.so");
-    emplace("OMX.google.aac.decoder", "libcodec2_soft_aacdec.so");
-    emplace("OMX.google.aac.encoder", "libcodec2_soft_aacenc.so");
-    emplace("OMX.google.amrnb.decoder", "libcodec2_soft_amrnbdec.so");
-    emplace("OMX.google.amrnb.encoder", "libcodec2_soft_amrnbenc.so");
-    emplace("OMX.google.amrwb.decoder", "libcodec2_soft_amrwbdec.so");
-    emplace("OMX.google.amrwb.encoder", "libcodec2_soft_amrwbenc.so");
-    emplace("OMX.google.hevc.decoder", "libcodec2_soft_hevcdec.so");
-    emplace("OMX.google.g711.alaw.decoder", "libcodec2_soft_g711alawdec.so");
-    emplace("OMX.google.g711.mlaw.decoder", "libcodec2_soft_g711mlawdec.so");
-    emplace("OMX.google.mpeg2.decoder", "libcodec2_soft_mpeg2dec.so");
-    emplace("OMX.google.h263.decoder", "libcodec2_soft_h263dec.so");
-    emplace("OMX.google.h263.encoder", "libcodec2_soft_h263enc.so");
-    emplace("OMX.google.mpeg4.decoder", "libcodec2_soft_mpeg4dec.so");
-    emplace("OMX.google.mpeg4.encoder", "libcodec2_soft_mpeg4enc.so");
-    emplace("OMX.google.mp3.decoder", "libcodec2_soft_mp3dec.so");
-    emplace("OMX.google.vorbis.decoder", "libcodec2_soft_vorbisdec.so");
-    emplace("OMX.google.opus.decoder", "libcodec2_soft_opusdec.so");
-    emplace("OMX.google.vp8.decoder", "libcodec2_soft_vp8dec.so");
-    emplace("OMX.google.vp9.decoder", "libcodec2_soft_vp9dec.so");
-    emplace("OMX.google.vp8.encoder", "libcodec2_soft_vp8enc.so");
-    emplace("OMX.google.vp9.encoder", "libcodec2_soft_vp9enc.so");
-    emplace("OMX.google.raw.decoder", "libcodec2_soft_rawdec.so");
-    emplace("OMX.google.flac.decoder", "libcodec2_soft_flacdec.so");
-    emplace("OMX.google.flac.encoder", "libcodec2_soft_flacenc.so");
-    emplace("OMX.google.gsm.decoder", "libcodec2_soft_gsmdec.so");
-    emplace("OMX.google.xaac.decoder", "libcodec2_soft_xaacdec.so");
+    // TODO: move this also into a .so so it can be updated
+    emplace("libcodec2_soft_aacdec.so");
+    emplace("libcodec2_soft_aacenc.so");
+    emplace("libcodec2_soft_amrnbdec.so");
+    emplace("libcodec2_soft_amrnbenc.so");
+    emplace("libcodec2_soft_amrwbdec.so");
+    emplace("libcodec2_soft_amrwbenc.so");
+    emplace("libcodec2_soft_av1dec.so");
+    emplace("libcodec2_soft_avcdec.so");
+    emplace("libcodec2_soft_avcenc.so");
+    emplace("libcodec2_soft_flacdec.so");
+    emplace("libcodec2_soft_flacenc.so");
+    emplace("libcodec2_soft_g711alawdec.so");
+    emplace("libcodec2_soft_g711mlawdec.so");
+    emplace("libcodec2_soft_gsmdec.so");
+    emplace("libcodec2_soft_h263dec.so");
+    emplace("libcodec2_soft_h263enc.so");
+    emplace("libcodec2_soft_hevcdec.so");
+    emplace("libcodec2_soft_mp3dec.so");
+    emplace("libcodec2_soft_mpeg2dec.so");
+    emplace("libcodec2_soft_mpeg4dec.so");
+    emplace("libcodec2_soft_mpeg4enc.so");
+    emplace("libcodec2_soft_opusdec.so");
+    emplace("libcodec2_soft_opusenc.so");
+    emplace("libcodec2_soft_rawdec.so");
+    emplace("libcodec2_soft_vorbisdec.so");
+    emplace("libcodec2_soft_vp8dec.so");
+    emplace("libcodec2_soft_vp8enc.so");
+    emplace("libcodec2_soft_vp9dec.so");
+    emplace("libcodec2_soft_vp9enc.so");
+    emplace("libcodec2_soft_xaacdec.so");
 }
 
 c2_status_t C2PlatformComponentStore::copyBuffer(
@@ -881,47 +891,56 @@
     return mInterface.config(params, C2_MAY_BLOCK, failures);
 }
 
-std::vector<std::shared_ptr<const C2Component::Traits>> C2PlatformComponentStore::listComponents() {
-    // This method SHALL return within 500ms.
-    std::vector<std::shared_ptr<const C2Component::Traits>> list;
-    for (const C2String &alias : mComponentsList) {
-        ComponentLoader &loader = mComponents.at(alias);
+void C2PlatformComponentStore::visitComponents() {
+    std::lock_guard<std::mutex> lock(mMutex);
+    if (mVisited) {
+        return;
+    }
+    for (auto &pathAndLoader : mComponents) {
+        const C2String &path = pathAndLoader.first;
+        ComponentLoader &loader = pathAndLoader.second;
         std::shared_ptr<ComponentModule> module;
-        c2_status_t res = loader.fetchModule(&module);
-        if (res == C2_OK) {
+        if (loader.fetchModule(&module) == C2_OK) {
             std::shared_ptr<const C2Component::Traits> traits = module->getTraits();
             if (traits) {
-                list.push_back(traits);
+                mComponentList.push_back(traits);
+                mComponentNameToPath.emplace(traits->name, path);
+                for (const C2String &alias : traits->aliases) {
+                    mComponentNameToPath.emplace(alias, path);
+                }
             }
         }
     }
-    return list;
+    mVisited = true;
 }
 
-c2_status_t C2PlatformComponentStore::findComponent(C2String name, ComponentLoader **loader) {
-    *loader = nullptr;
-    auto pos = mComponents.find(name);
-    // TODO: check aliases
-    if (pos == mComponents.end()) {
-        return C2_NOT_FOUND;
+std::vector<std::shared_ptr<const C2Component::Traits>> C2PlatformComponentStore::listComponents() {
+    // This method SHALL return within 500ms.
+    visitComponents();
+    return mComponentList;
+}
+
+c2_status_t C2PlatformComponentStore::findComponent(
+        C2String name, std::shared_ptr<ComponentModule> *module) {
+    (*module).reset();
+    visitComponents();
+
+    auto pos = mComponentNameToPath.find(name);
+    if (pos != mComponentNameToPath.end()) {
+        return mComponents.at(pos->second).fetchModule(module);
     }
-    *loader = &pos->second;
-    return C2_OK;
+    return C2_NOT_FOUND;
 }
 
 c2_status_t C2PlatformComponentStore::createComponent(
         C2String name, std::shared_ptr<C2Component> *const component) {
     // This method SHALL return within 100ms.
     component->reset();
-    ComponentLoader *loader;
-    c2_status_t res = findComponent(name, &loader);
+    std::shared_ptr<ComponentModule> module;
+    c2_status_t res = findComponent(name, &module);
     if (res == C2_OK) {
-        std::shared_ptr<ComponentModule> module;
-        res = loader->fetchModule(&module);
-        if (res == C2_OK) {
-            // TODO: get a unique node ID
-            res = module->createComponent(0, component);
-        }
+        // TODO: get a unique node ID
+        res = module->createComponent(0, component);
     }
     return res;
 }
@@ -930,15 +949,11 @@
         C2String name, std::shared_ptr<C2ComponentInterface> *const interface) {
     // This method SHALL return within 100ms.
     interface->reset();
-    ComponentLoader *loader;
-    c2_status_t res = findComponent(name, &loader);
+    std::shared_ptr<ComponentModule> module;
+    c2_status_t res = findComponent(name, &module);
     if (res == C2_OK) {
-        std::shared_ptr<ComponentModule> module;
-        res = loader->fetchModule(&module);
-        if (res == C2_OK) {
-            // TODO: get a unique node ID
-            res = module->createInterface(0, interface);
-        }
+        // TODO: get a unique node ID
+        res = module->createInterface(0, interface);
     }
     return res;
 }
diff --git a/media/libaaudio/examples/loopback/src/LoopbackAnalyzer.h b/media/libaaudio/examples/loopback/src/LoopbackAnalyzer.h
index 9711b86..8eb70b1 100644
--- a/media/libaaudio/examples/loopback/src/LoopbackAnalyzer.h
+++ b/media/libaaudio/examples/loopback/src/LoopbackAnalyzer.h
@@ -310,7 +310,7 @@
     }
 
     // Write SHORT data from the first channel.
-    int write(int16_t *inputData, int inputChannelCount, int numFrames) {
+    int32_t write(int16_t *inputData, int32_t inputChannelCount, int32_t numFrames) {
         // stop at end of buffer
         if ((mFrameCounter + numFrames) > mMaxFrames) {
             numFrames = mMaxFrames - mFrameCounter;
@@ -322,7 +322,7 @@
     }
 
     // Write FLOAT data from the first channel.
-    int write(float *inputData, int inputChannelCount, int numFrames) {
+    int32_t write(float *inputData, int32_t inputChannelCount, int32_t numFrames) {
         // stop at end of buffer
         if ((mFrameCounter + numFrames) > mMaxFrames) {
             numFrames = mMaxFrames - mFrameCounter;
@@ -333,7 +333,7 @@
         return numFrames;
     }
 
-    int size() {
+    int32_t size() {
         return mFrameCounter;
     }
 
@@ -443,9 +443,14 @@
     virtual ~LoopbackProcessor() = default;
 
 
+    enum process_result {
+        PROCESS_RESULT_OK,
+        PROCESS_RESULT_GLITCH
+    };
+
     virtual void reset() {}
 
-    virtual void process(float *inputData, int inputChannelCount,
+    virtual process_result process(float *inputData, int inputChannelCount,
                  float *outputData, int outputChannelCount,
                  int numFrames) = 0;
 
@@ -639,7 +644,7 @@
         return getSampleRate() / 8;
     }
 
-    void process(float *inputData, int inputChannelCount,
+    process_result process(float *inputData, int inputChannelCount,
                  float *outputData, int outputChannelCount,
                  int numFrames) override {
         int channelsValid = std::min(inputChannelCount, outputChannelCount);
@@ -750,6 +755,7 @@
 
         mState = nextState;
         mLoopCounter++;
+        return PROCESS_RESULT_OK;
     }
 
     int save(const char *fileName) override {
@@ -896,9 +902,10 @@
      * @param inputData contains microphone data with sine signal feedback
      * @param outputData contains the reference sine wave
      */
-    void process(float *inputData, int inputChannelCount,
+    process_result process(float *inputData, int inputChannelCount,
                  float *outputData, int outputChannelCount,
                  int numFrames) override {
+        process_result result = PROCESS_RESULT_OK;
         mProcessCount++;
 
         float peak = measurePeakAmplitude(inputData, inputChannelCount, numFrames);
@@ -978,6 +985,7 @@
                     mMaxGlitchDelta = std::max(mMaxGlitchDelta, absDiff);
                     if (absDiff > mTolerance) {
                         mGlitchCount++;
+                        result = PROCESS_RESULT_GLITCH;
                         //printf("%5d: Got a glitch # %d, predicted = %f, actual = %f\n",
                         //       mFrameCounter, mGlitchCount, predicted, sample);
                         mState = STATE_IMMUNE;
@@ -1018,6 +1026,7 @@
 
             mFrameCounter++;
         }
+        return result;
     }
 
     void resetAccumulator() {
diff --git a/media/libaaudio/examples/loopback/src/loopback.cpp b/media/libaaudio/examples/loopback/src/loopback.cpp
index 3de1514..75d425f 100644
--- a/media/libaaudio/examples/loopback/src/loopback.cpp
+++ b/media/libaaudio/examples/loopback/src/loopback.cpp
@@ -34,6 +34,7 @@
 #include "AAudioSimpleRecorder.h"
 #include "AAudioExampleUtils.h"
 #include "LoopbackAnalyzer.h"
+#include "../../utils/AAudioExampleUtils.h"
 
 // V0.4.00 = rectify and low-pass filter the echos, use auto-correlation on entire echo
 #define APP_VERSION             "0.4.00"
@@ -47,10 +48,14 @@
 constexpr int kLogPeriodMillis       = 1000;
 constexpr int kNumInputChannels      = 1;
 constexpr int kNumCallbacksToDrain   = 20;
+constexpr int kNumCallbacksToNotRead = 0; // let input fill back up
 constexpr int kNumCallbacksToDiscard = 20;
+constexpr int kDefaultHangTimeMillis = 50;
+constexpr int kMaxGlitchEventsToSave = 32;
 
 struct LoopbackData {
     AAudioStream      *inputStream = nullptr;
+    AAudioStream      *outputStream = nullptr;
     int32_t            inputFramesMaximum = 0;
     int16_t           *inputShortData = nullptr;
     float             *inputFloatData = nullptr;
@@ -58,6 +63,7 @@
     int32_t            actualInputChannelCount = 0;
     int32_t            actualOutputChannelCount = 0;
     int32_t            numCallbacksToDrain = kNumCallbacksToDrain;
+    int32_t            numCallbacksToNotRead = kNumCallbacksToNotRead;
     int32_t            numCallbacksToDiscard = kNumCallbacksToDiscard;
     int32_t            minNumFrames = INT32_MAX;
     int32_t            maxNumFrames = 0;
@@ -65,6 +71,9 @@
     int32_t            insufficientReadFrames = 0;
     int32_t            framesReadTotal = 0;
     int32_t            framesWrittenTotal = 0;
+    int32_t            hangPeriodMillis = 5 * 1000; // time between hangs
+    int32_t            hangCountdownFrames = 5 * 48000; // frames til next hang
+    int32_t            hangTimeMillis = 0; // 0 for no hang
     bool               isDone = false;
 
     aaudio_result_t    inputError = AAUDIO_OK;
@@ -74,6 +83,29 @@
     EchoAnalyzer       echoAnalyzer;
     AudioRecording     audioRecording;
     LoopbackProcessor *loopbackProcessor;
+
+    int32_t            glitchFrames[kMaxGlitchEventsToSave];
+    int32_t            numGlitchEvents = 0;
+
+    void hangIfRequested(int32_t numFrames) {
+        if (hangTimeMillis > 0) {
+            hangCountdownFrames -= numFrames;
+            if (hangCountdownFrames <= 0) {
+                const int64_t startNanos = getNanoseconds();
+                usleep(hangTimeMillis * 1000);
+                const int64_t endNanos = getNanoseconds();
+                const int32_t elapsedMicros = (int32_t)
+                        ((endNanos - startNanos) / 1000);
+                printf("callback hanging for %d millis, actual = %d micros\n",
+                       hangTimeMillis, elapsedMicros);
+                hangCountdownFrames = (int64_t) hangPeriodMillis
+                        * AAudioStream_getSampleRate(outputStream)
+                        / 1000;
+            }
+        }
+
+
+    }
 };
 
 static void convertPcm16ToFloat(const int16_t *source,
@@ -166,6 +198,9 @@
             myData->numCallbacksToDrain--;
         }
 
+    } else if (myData->numCallbacksToNotRead > 0) {
+        // Let the input fill up a bit so we are not so close to the write pointer.
+        myData->numCallbacksToNotRead--;
     } else if (myData->numCallbacksToDiscard > 0) {
         // Ignore. Allow the input to fill back up to equilibrium with the output.
         actualFramesRead = readFormattedData(myData, numFrames);
@@ -175,6 +210,7 @@
         myData->numCallbacksToDiscard--;
 
     } else {
+        myData->hangIfRequested(numFrames);
 
         int32_t numInputBytes = numFrames * myData->actualInputChannelCount * sizeof(float);
         memset(myData->inputFloatData, 0 /* value */, numInputBytes);
@@ -191,7 +227,7 @@
 
             if (actualFramesRead < numFrames) {
                 if(actualFramesRead < (int32_t) framesAvailable) {
-                    printf("insufficient but numFrames = %d"
+                    printf("insufficient for no reason, numFrames = %d"
                                    ", actualFramesRead = %d"
                                    ", inputFramesWritten = %d"
                                    ", inputFramesRead = %d"
@@ -212,16 +248,25 @@
             if (myData->actualInputFormat == AAUDIO_FORMAT_PCM_I16) {
                 convertPcm16ToFloat(myData->inputShortData, myData->inputFloatData, numSamples);
             }
-            // Save for later.
-            myData->audioRecording.write(myData->inputFloatData,
-                                         myData->actualInputChannelCount,
-                                         numFrames);
+
             // Analyze the data.
-            myData->loopbackProcessor->process(myData->inputFloatData,
+            LoopbackProcessor::process_result procResult = myData->loopbackProcessor->process(myData->inputFloatData,
                                                myData->actualInputChannelCount,
                                                outputData,
                                                myData->actualOutputChannelCount,
                                                numFrames);
+
+            if (procResult == LoopbackProcessor::PROCESS_RESULT_GLITCH) {
+                if (myData->numGlitchEvents < kMaxGlitchEventsToSave) {
+                    myData->glitchFrames[myData->numGlitchEvents++] = myData->audioRecording.size();
+                }
+            }
+
+            // Save for later.
+            myData->audioRecording.write(myData->inputFloatData,
+                                         myData->actualInputChannelCount,
+                                         actualFramesRead);
+
             myData->isDone = myData->loopbackProcessor->isDone();
             if (myData->isDone) {
                 result = AAUDIO_CALLBACK_RESULT_STOP;
@@ -249,6 +294,7 @@
     printf("      -C{channels}      number of input channels\n");
     printf("      -F{0,1,2}         input format, 1=I16, 2=FLOAT\n");
     printf("      -g{gain}          recirculating loopback gain\n");
+    printf("      -h{hangMillis}    occasionally hang in the callback\n");
     printf("      -P{inPerf}        set input AAUDIO_PERFORMANCE_MODE*\n");
     printf("          n for _NONE\n");
     printf("          l for _LATENCY\n");
@@ -307,9 +353,7 @@
     return testMode;
 }
 
-void printAudioGraph(AudioRecording &recording, int numSamples) {
-    int32_t start = recording.size() / 2;
-    int32_t end = start + numSamples;
+void printAudioGraphRegion(AudioRecording &recording, int32_t start, int32_t end) {
     if (end >= recording.size()) {
         end = recording.size() - 1;
     }
@@ -360,6 +404,7 @@
 
     int                   testMode                   = TEST_ECHO_LATENCY;
     double                gain                       = 1.0;
+    int                   hangTimeMillis             = 0;
 
     // Make printf print immediately so that debug info is not stuck
     // in a buffer if we hang or crash.
@@ -389,6 +434,15 @@
                     case 'g':
                         gain = atof(&arg[2]);
                         break;
+                    case 'h':
+                        // Was there a number after the "-h"?
+                        if (arg[2]) {
+                            hangTimeMillis = atoi(&arg[2]);
+                        } else {
+                            // If no number then use the default.
+                            hangTimeMillis = kDefaultHangTimeMillis;
+                        }
+                        break;
                     case 'P':
                         inputPerformanceLevel = parsePerformanceMode(arg[2]);
                         break;
@@ -453,7 +507,7 @@
         fprintf(stderr, "ERROR -  player.open() returned %d\n", result);
         exit(1);
     }
-    outputStream = player.getStream();
+    outputStream = loopbackData.outputStream = player.getStream();
 
     actualOutputFormat = AAudioStream_getFormat(outputStream);
     if (actualOutputFormat != AAUDIO_FORMAT_PCM_FLOAT) {
@@ -487,20 +541,24 @@
     }
     inputStream = loopbackData.inputStream = recorder.getStream();
 
-    {
-        int32_t actualCapacity = AAudioStream_getBufferCapacityInFrames(inputStream);
-        result = AAudioStream_setBufferSizeInFrames(inputStream, actualCapacity);
-        if (result < 0) {
-            fprintf(stderr, "ERROR -  AAudioStream_setBufferSizeInFrames() returned %d\n", result);
-            goto finish;
-        } else {}
-    }
-
     argParser.compareWithStream(inputStream);
 
-    // If the input stream is too small then we cannot satisfy the output callback.
     {
         int32_t actualCapacity = AAudioStream_getBufferCapacityInFrames(inputStream);
+        (void) AAudioStream_setBufferSizeInFrames(inputStream, actualCapacity);
+
+        if (testMode == TEST_SINE_MAGNITUDE) {
+            result = AAudioStream_setBufferSizeInFrames(outputStream, actualCapacity);
+            if (result < 0) {
+                fprintf(stderr, "ERROR -  AAudioStream_setBufferSizeInFrames(output) returned %d\n",
+                        result);
+                goto finish;
+            } else {
+                printf("Output buffer size set to match input capacity = %d frames.\n", result);
+            }
+        }
+
+        // If the input stream is too small then we cannot satisfy the output callback.
         if (actualCapacity < 2 * outputFramesPerBurst) {
             fprintf(stderr, "ERROR - input capacity < 2 * outputFramesPerBurst\n");
             goto finish;
@@ -525,6 +583,8 @@
 
     loopbackData.loopbackProcessor->reset();
 
+    loopbackData.hangTimeMillis = hangTimeMillis;
+
     // Start OUTPUT first so INPUT does not overflow.
     result = player.start();
     if (result != AAUDIO_OK) {
@@ -611,7 +671,17 @@
 
     if (loopbackData.inputError == AAUDIO_OK) {
         if (testMode == TEST_SINE_MAGNITUDE) {
-            printAudioGraph(loopbackData.audioRecording, 200);
+            if (loopbackData.numGlitchEvents > 0) {
+                // Graph around the first glitch if there is one.
+                const int32_t start = loopbackData.glitchFrames[0] - 8;
+                const int32_t end = start + outputFramesPerBurst + 8 + 8;
+                printAudioGraphRegion(loopbackData.audioRecording, start, end);
+            } else {
+                // Or graph the middle of the signal.
+                const int32_t start = loopbackData.audioRecording.size() / 2;
+                const int32_t end = start + 200;
+                printAudioGraphRegion(loopbackData.audioRecording, start, end);
+            }
         }
 
         loopbackData.loopbackProcessor->report();
@@ -661,6 +731,11 @@
     delete[] loopbackData.inputShortData;
 
 report_result:
+
+    for (int i = 0; i < loopbackData.numGlitchEvents; i++) {
+        printf("  glitch at frame %d\n", loopbackData.glitchFrames[i]);
+    }
+
     written = loopbackData.loopbackProcessor->save(FILENAME_PROCESSED);
     if (written > 0) {
         printf("main() wrote %8d processed samples to \"%s\" on Android device\n",
diff --git a/media/libaaudio/examples/utils/AAudioArgsParser.h b/media/libaaudio/examples/utils/AAudioArgsParser.h
index a5dc55f..f5ed7aa 100644
--- a/media/libaaudio/examples/utils/AAudioArgsParser.h
+++ b/media/libaaudio/examples/utils/AAudioArgsParser.h
@@ -130,12 +130,10 @@
     }
 
     int32_t getBufferCapacity() const {
-        printf("%s() returns %d\n", __func__, mBufferCapacity);
         return mBufferCapacity;
     }
 
     void setBufferCapacity(int32_t frames) {
-        printf("%s(%d)\n", __func__, frames);
         mBufferCapacity = frames;
     }
 
diff --git a/media/libaudioclient/AudioRecord.cpp b/media/libaudioclient/AudioRecord.cpp
index 72a23e3..8afb1cc 100644
--- a/media/libaudioclient/AudioRecord.cpp
+++ b/media/libaudioclient/AudioRecord.cpp
@@ -355,7 +355,10 @@
     }
 
     // create the IAudioRecord
-    status = createRecord_l(0 /*epoch*/, mOpPackageName);
+    {
+        AutoMutex lock(mLock);
+        status = createRecord_l(0 /*epoch*/, mOpPackageName);
+    }
 
     ALOGV("%s(%d): status %d", __func__, mPortId, status);
 
@@ -1358,12 +1361,14 @@
         ALOGW("%s(%d): removing NULL callback!", __func__, mPortId);
         return BAD_VALUE;
     }
-    AutoMutex lock(mLock);
-    if (mDeviceCallback.unsafe_get() != callback.get()) {
-        ALOGW("%s(%d): removing different callback!", __func__, mPortId);
-        return INVALID_OPERATION;
+    {
+        AutoMutex lock(mLock);
+        if (mDeviceCallback.unsafe_get() != callback.get()) {
+            ALOGW("%s(%d): removing different callback!", __func__, mPortId);
+            return INVALID_OPERATION;
+        }
+        mDeviceCallback.clear();
     }
-    mDeviceCallback.clear();
     if (mInput != AUDIO_IO_HANDLE_NONE) {
         AudioSystem::removeAudioDeviceCallback(this, mInput);
     }
diff --git a/media/libaudioclient/AudioSystem.cpp b/media/libaudioclient/AudioSystem.cpp
index b83a441e..52ad5a6 100644
--- a/media/libaudioclient/AudioSystem.cpp
+++ b/media/libaudioclient/AudioSystem.cpp
@@ -522,8 +522,9 @@
     if (ioDesc == 0 || ioDesc->mIoHandle == AUDIO_IO_HANDLE_NONE) return;
 
     audio_port_handle_t deviceId = AUDIO_PORT_HANDLE_NONE;
-    Vector < wp<AudioDeviceCallback> > callbacks;
-
+    AudioDeviceCallbacks callbacks;
+    bool deviceValidOrChanged = false;
+    Mutex::Autolock _l(mCallbacksLock);
     {
         Mutex::Autolock _l(mLock);
 
@@ -546,6 +547,13 @@
                     ssize_t ioIndex = mAudioDeviceCallbacks.indexOfKey(ioDesc->mIoHandle);
                     if (ioIndex >= 0) {
                         callbacks = mAudioDeviceCallbacks.valueAt(ioIndex);
+                        deviceValidOrChanged = true;
+                    }
+                }
+                if (event == AUDIO_OUTPUT_REGISTERED || event == AUDIO_INPUT_REGISTERED) {
+                    ssize_t ioIndex = mAudioDeviceCallbacks.indexOfKey(ioDesc->mIoHandle);
+                    if ((ioIndex >= 0) && !mAudioDeviceCallbacks.valueAt(ioIndex).notifiedOnce()) {
+                        callbacks = mAudioDeviceCallbacks.valueAt(ioIndex);
                     }
                 }
             }
@@ -584,6 +592,7 @@
             mIoDescriptors.replaceValueFor(ioDesc->mIoHandle, ioDesc);
 
             if (deviceId != ioDesc->getDeviceId()) {
+                deviceValidOrChanged = true;
                 deviceId = ioDesc->getDeviceId();
                 ssize_t ioIndex = mAudioDeviceCallbacks.indexOfKey(ioDesc->mIoHandle);
                 if (ioIndex >= 0) {
@@ -600,22 +609,28 @@
         } break;
         }
     }
-    bool callbackRemoved = false;
     // callbacks.size() != 0 =>  ioDesc->mIoHandle and deviceId are valid
-    for (size_t i = 0; i < callbacks.size(); ) {
-        sp<AudioDeviceCallback> callback = callbacks[i].promote();
-        if (callback.get() != nullptr) {
-            callback->onAudioDeviceUpdate(ioDesc->mIoHandle, deviceId);
-            i++;
-        } else {
-            callbacks.removeAt(i);
-            callbackRemoved = true;
+    if (callbacks.size() != 0) {
+        for (size_t i = 0; i < callbacks.size(); ) {
+            sp<AudioDeviceCallback> callback = callbacks[i].promote();
+            if (callback.get() != nullptr) {
+                // Call the callback only if the device actually changed, the input or output was
+                // opened or closed or the client was newly registered and the callback was never
+                // called
+                if (!callback->notifiedOnce() || deviceValidOrChanged) {
+                    // Must be called without mLock held. May lead to dead lock if calling for
+                    // example getRoutedDevice that updates the device and tries to acquire mLock.
+                    callback->onAudioDeviceUpdate(ioDesc->mIoHandle, deviceId);
+                    callback->setNotifiedOnce();
+                }
+                i++;
+            } else {
+                callbacks.removeAt(i);
+            }
         }
-    }
-    // clean up callback list while we are here if some clients have disappeared without
-    // unregistering their callback
-    if (callbackRemoved) {
-        Mutex::Autolock _l(mLock);
+        callbacks.setNotifiedOnce();
+        // clean up callback list while we are here if some clients have disappeared without
+        // unregistering their callback, or if cb was served for the first time since registered
         mAudioDeviceCallbacks.replaceValueFor(ioDesc->mIoHandle, callbacks);
     }
 }
@@ -671,8 +686,8 @@
 status_t AudioSystem::AudioFlingerClient::addAudioDeviceCallback(
         const wp<AudioDeviceCallback>& callback, audio_io_handle_t audioIo)
 {
-    Mutex::Autolock _l(mLock);
-    Vector < wp<AudioDeviceCallback> > callbacks;
+    Mutex::Autolock _l(mCallbacksLock);
+    AudioDeviceCallbacks callbacks;
     ssize_t ioIndex = mAudioDeviceCallbacks.indexOfKey(audioIo);
     if (ioIndex >= 0) {
         callbacks = mAudioDeviceCallbacks.valueAt(ioIndex);
@@ -684,7 +699,7 @@
         }
     }
     callbacks.add(callback);
-
+    callbacks.resetNotifiedOnce();
     mAudioDeviceCallbacks.replaceValueFor(audioIo, callbacks);
     return NO_ERROR;
 }
@@ -692,12 +707,12 @@
 status_t AudioSystem::AudioFlingerClient::removeAudioDeviceCallback(
         const wp<AudioDeviceCallback>& callback, audio_io_handle_t audioIo)
 {
-    Mutex::Autolock _l(mLock);
+    Mutex::Autolock _l(mCallbacksLock);
     ssize_t ioIndex = mAudioDeviceCallbacks.indexOfKey(audioIo);
     if (ioIndex < 0) {
         return INVALID_OPERATION;
     }
-    Vector < wp<AudioDeviceCallback> > callbacks = mAudioDeviceCallbacks.valueAt(ioIndex);
+    AudioDeviceCallbacks callbacks = mAudioDeviceCallbacks.valueAt(ioIndex);
 
     size_t cbIndex;
     for (cbIndex = 0; cbIndex < callbacks.size(); cbIndex++) {
@@ -872,13 +887,14 @@
                                         const audio_config_t *config,
                                         audio_output_flags_t flags,
                                         audio_port_handle_t *selectedDeviceId,
-                                        audio_port_handle_t *portId)
+                                        audio_port_handle_t *portId,
+                                        std::vector<audio_io_handle_t> *secondaryOutputs)
 {
     const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
     if (aps == 0) return NO_INIT;
     return aps->getOutputForAttr(attr, output, session, stream, pid, uid,
                                  config,
-                                 flags, selectedDeviceId, portId);
+                                 flags, selectedDeviceId, portId, secondaryOutputs);
 }
 
 status_t AudioSystem::startOutput(audio_port_handle_t portId)
diff --git a/media/libaudioclient/AudioTrack.cpp b/media/libaudioclient/AudioTrack.cpp
index 79abea0..b5a7ebe 100644
--- a/media/libaudioclient/AudioTrack.cpp
+++ b/media/libaudioclient/AudioTrack.cpp
@@ -621,8 +621,10 @@
     }
 
     // create the IAudioTrack
-    status = createTrack_l();
-
+    {
+        AutoMutex lock(mLock);
+        status = createTrack_l();
+    }
     if (status != NO_ERROR) {
         if (mAudioTrackThread != 0) {
             mAudioTrackThread->requestExit();   // see comment in AudioTrack.h
@@ -2957,12 +2959,14 @@
         ALOGW("%s(%d): removing NULL callback!", __func__, mPortId);
         return BAD_VALUE;
     }
-    AutoMutex lock(mLock);
-    if (mDeviceCallback.unsafe_get() != callback.get()) {
-        ALOGW("%s(%d): removing different callback!", __func__, mPortId);
-        return INVALID_OPERATION;
+    {
+        AutoMutex lock(mLock);
+        if (mDeviceCallback.unsafe_get() != callback.get()) {
+            ALOGW("%s removing different callback!", __FUNCTION__);
+            return INVALID_OPERATION;
+        }
+        mDeviceCallback.clear();
     }
-    mDeviceCallback.clear();
     if (mOutput != AUDIO_IO_HANDLE_NONE) {
         AudioSystem::removeAudioDeviceCallback(this, mOutput);
     }
diff --git a/media/libaudioclient/IAudioPolicyService.cpp b/media/libaudioclient/IAudioPolicyService.cpp
index 0db56e8..d9f6e36 100644
--- a/media/libaudioclient/IAudioPolicyService.cpp
+++ b/media/libaudioclient/IAudioPolicyService.cpp
@@ -189,16 +189,17 @@
         return static_cast <audio_io_handle_t> (reply.readInt32());
     }
 
-    virtual status_t getOutputForAttr(const audio_attributes_t *attr,
-                                        audio_io_handle_t *output,
-                                        audio_session_t session,
-                                        audio_stream_type_t *stream,
-                                        pid_t pid,
-                                        uid_t uid,
-                                        const audio_config_t *config,
-                                        audio_output_flags_t flags,
-                                        audio_port_handle_t *selectedDeviceId,
-                                        audio_port_handle_t *portId)
+    status_t getOutputForAttr(const audio_attributes_t *attr,
+                              audio_io_handle_t *output,
+                              audio_session_t session,
+                              audio_stream_type_t *stream,
+                              pid_t pid,
+                              uid_t uid,
+                              const audio_config_t *config,
+                              audio_output_flags_t flags,
+                              audio_port_handle_t *selectedDeviceId,
+                              audio_port_handle_t *portId,
+                              std::vector<audio_io_handle_t> *secondaryOutputs) override
         {
             Parcel data, reply;
             data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
@@ -224,6 +225,10 @@
                 ALOGE("getOutputForAttr NULL portId - shouldn't happen");
                 return BAD_VALUE;
             }
+            if (secondaryOutputs == NULL) {
+                ALOGE("getOutputForAttr NULL secondaryOutputs - shouldn't happen");
+                return BAD_VALUE;
+            }
             if (attr == NULL) {
                 data.writeInt32(0);
             } else {
@@ -258,7 +263,9 @@
             }
             *selectedDeviceId = (audio_port_handle_t)reply.readInt32();
             *portId = (audio_port_handle_t)reply.readInt32();
-            return status;
+            secondaryOutputs->resize(reply.readInt32());
+            return reply.read(secondaryOutputs->data(),
+                              secondaryOutputs->size() * sizeof(audio_io_handle_t));
         }
 
     virtual status_t startOutput(audio_port_handle_t portId)
@@ -1300,16 +1307,19 @@
             audio_port_handle_t selectedDeviceId = data.readInt32();
             audio_port_handle_t portId = (audio_port_handle_t)data.readInt32();
             audio_io_handle_t output = 0;
+            std::vector<audio_io_handle_t> secondaryOutputs;
             status_t status = getOutputForAttr(hasAttributes ? &attr : NULL,
                     &output, session, &stream, pid, uid,
                     &config,
-                    flags, &selectedDeviceId, &portId);
+                    flags, &selectedDeviceId, &portId, &secondaryOutputs);
             reply->writeInt32(status);
             reply->writeInt32(output);
             reply->writeInt32(stream);
             reply->writeInt32(selectedDeviceId);
             reply->writeInt32(portId);
-            return NO_ERROR;
+            reply->writeInt32(secondaryOutputs.size());
+            return reply->write(secondaryOutputs.data(),
+                                secondaryOutputs.size() * sizeof(audio_io_handle_t));
         } break;
 
         case START_OUTPUT: {
diff --git a/media/libaudioclient/include/media/AudioPolicy.h b/media/libaudioclient/include/media/AudioPolicy.h
index 786fb9a..bf8d627 100644
--- a/media/libaudioclient/include/media/AudioPolicy.h
+++ b/media/libaudioclient/include/media/AudioPolicy.h
@@ -49,8 +49,12 @@
 #define MIX_STATE_IDLE 0
 #define MIX_STATE_MIXING 1
 
+/** Control to which device some audio is rendered */
 #define MIX_ROUTE_FLAG_RENDER 0x1
+/** Loop back some audio instead of rendering it */
 #define MIX_ROUTE_FLAG_LOOP_BACK (0x1 << 1)
+/** Loop back some audio while it is rendered */
+#define MIX_ROUTE_FLAG_LOOP_BACK_AND_RENDER (MIX_ROUTE_FLAG_RENDER | MIX_ROUTE_FLAG_LOOP_BACK)
 #define MIX_ROUTE_FLAG_ALL (MIX_ROUTE_FLAG_RENDER | MIX_ROUTE_FLAG_LOOP_BACK)
 
 #define MAX_MIXES_PER_POLICY 10
@@ -119,6 +123,11 @@
 #define RECORD_CONFIG_EVENT_START 1
 #define RECORD_CONFIG_EVENT_STOP  0
 
+static inline bool is_mix_loopback_render(uint32_t routeFlags) {
+    return (routeFlags & MIX_ROUTE_FLAG_LOOP_BACK_AND_RENDER)
+           == MIX_ROUTE_FLAG_LOOP_BACK_AND_RENDER;
+}
+
 }; // namespace android
 
 #endif  // ANDROID_AUDIO_POLICY_H
diff --git a/media/libaudioclient/include/media/AudioRecord.h b/media/libaudioclient/include/media/AudioRecord.h
index 1f71844..4707c4a 100644
--- a/media/libaudioclient/include/media/AudioRecord.h
+++ b/media/libaudioclient/include/media/AudioRecord.h
@@ -677,7 +677,7 @@
     sp<IMemory>             mCblkMemory;
     audio_track_cblk_t*     mCblk;              // re-load after mLock.unlock()
     sp<IMemory>             mBufferMemory;
-    audio_io_handle_t       mInput;             // returned by AudioSystem::getInput()
+    audio_io_handle_t       mInput = AUDIO_IO_HANDLE_NONE; // from AudioSystem::getInputforAttr()
 
     int                     mPreviousPriority;  // before start()
     SchedPolicy             mPreviousSchedulingGroup;
diff --git a/media/libaudioclient/include/media/AudioSystem.h b/media/libaudioclient/include/media/AudioSystem.h
index 87a9919..3df49e6 100644
--- a/media/libaudioclient/include/media/AudioSystem.h
+++ b/media/libaudioclient/include/media/AudioSystem.h
@@ -231,7 +231,8 @@
                                      const audio_config_t *config,
                                      audio_output_flags_t flags,
                                      audio_port_handle_t *selectedDeviceId,
-                                     audio_port_handle_t *portId);
+                                     audio_port_handle_t *portId,
+                                     std::vector<audio_io_handle_t> *secondaryOutputs);
     static status_t startOutput(audio_port_handle_t portId);
     static status_t stopOutput(audio_port_handle_t portId);
     static void releaseOutput(audio_port_handle_t portId);
@@ -398,6 +399,15 @@
 
         virtual void onAudioDeviceUpdate(audio_io_handle_t audioIo,
                                          audio_port_handle_t deviceId) = 0;
+                bool notifiedOnce() const { return mNotifiedOnce; }
+                void setNotifiedOnce() { mNotifiedOnce = true; }
+    private:
+                /**
+                 * @brief mNotifiedOnce it forces the callback to be called at least once when
+                 * registered with a VALID AudioDevice, and allows not to flood other listeners
+                 * on this iohandle that already know the valid device.
+                 */
+                bool mNotifiedOnce = false;
     };
 
     static status_t addAudioDeviceCallback(const wp<AudioDeviceCallback>& callback,
@@ -443,8 +453,27 @@
     private:
         Mutex                               mLock;
         DefaultKeyedVector<audio_io_handle_t, sp<AudioIoDescriptor> >   mIoDescriptors;
-        DefaultKeyedVector<audio_io_handle_t, Vector < wp<AudioDeviceCallback> > >
-                                                                        mAudioDeviceCallbacks;
+
+        class AudioDeviceCallbacks : public Vector<wp<AudioDeviceCallback>>
+        {
+        public:
+            /**
+             * @brief notifiedOnce ensures that if a client adds a callback, it must at least be
+             * called once with the device on which it will be routed to.
+             * @return true if already notified or nobody waits for a callback, false otherwise.
+             */
+            bool notifiedOnce() const { return (size() == 0) || mNotifiedOnce; }
+            void setNotifiedOnce() { mNotifiedOnce = true; }
+            void resetNotifiedOnce() { mNotifiedOnce = false; }
+        private:
+            /**
+             * @brief mNotifiedOnce it forces each callback to be called at least once when
+             * registered with a VALID AudioDevice
+             */
+            bool mNotifiedOnce = false;
+        };
+        Mutex                               mCallbacksLock; // prevents race on Callbacks
+        DefaultKeyedVector<audio_io_handle_t, AudioDeviceCallbacks> mAudioDeviceCallbacks;
         // cached values for recording getInputBufferSize() queries
         size_t                              mInBuffSize;    // zero indicates cache is invalid
         uint32_t                            mInSamplingRate;
diff --git a/media/libaudioclient/include/media/AudioTrack.h b/media/libaudioclient/include/media/AudioTrack.h
index cbb750f..12f5d71 100644
--- a/media/libaudioclient/include/media/AudioTrack.h
+++ b/media/libaudioclient/include/media/AudioTrack.h
@@ -1021,7 +1021,7 @@
     sp<IAudioTrack>         mAudioTrack;
     sp<IMemory>             mCblkMemory;
     audio_track_cblk_t*     mCblk;                  // re-load after mLock.unlock()
-    audio_io_handle_t       mOutput;                // returned by AudioSystem::getOutputForAttr()
+    audio_io_handle_t       mOutput = AUDIO_IO_HANDLE_NONE; // from AudioSystem::getOutputForAttr()
 
     sp<AudioTrackThread>    mAudioTrackThread;
     bool                    mThreadCanCallJava;
diff --git a/media/libaudioclient/include/media/IAudioPolicyService.h b/media/libaudioclient/include/media/IAudioPolicyService.h
index b2cda32..e89a55d 100644
--- a/media/libaudioclient/include/media/IAudioPolicyService.h
+++ b/media/libaudioclient/include/media/IAudioPolicyService.h
@@ -66,7 +66,8 @@
                                       const audio_config_t *config,
                                       audio_output_flags_t flags,
                                       audio_port_handle_t *selectedDeviceId,
-                                      audio_port_handle_t *portId) = 0;
+                                      audio_port_handle_t *portId,
+                                      std::vector<audio_io_handle_t> *secondaryOutputs) = 0;
     virtual status_t startOutput(audio_port_handle_t portId) = 0;
     virtual status_t stopOutput(audio_port_handle_t portId) = 0;
     virtual void releaseOutput(audio_port_handle_t portId) = 0;
diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp
index c4015fb..6259b15 100644
--- a/media/libstagefright/MPEG4Writer.cpp
+++ b/media/libstagefright/MPEG4Writer.cpp
@@ -3595,7 +3595,7 @@
 }
 
 int64_t MPEG4Writer::Track::getDurationUs() const {
-    return mTrackDurationUs + getStartTimeOffsetTimeUs();
+    return mTrackDurationUs + getStartTimeOffsetTimeUs() + mOwner->getStartTimeOffsetBFramesUs();
 }
 
 int64_t MPEG4Writer::Track::getEstimatedTrackSizeBytes() const {
@@ -4059,7 +4059,7 @@
     // Prepone video playback.
     if (mMinCttsOffsetTicks != mMaxCttsOffsetTicks) {
         int32_t mvhdTimeScale = mOwner->getTimeScale();
-        uint32_t tkhdDuration = (mTrackDurationUs * mvhdTimeScale + 5E5) / 1E6;
+        uint32_t tkhdDuration = (getDurationUs() * mvhdTimeScale + 5E5) / 1E6;
         int64_t mediaTime = ((kMaxCttsOffsetTimeUs - getMinCttsOffsetTimeUs())
             * mTimeScale + 5E5) / 1E6;
         if (tkhdDuration > 0 && mediaTime > 0) {
diff --git a/media/libstagefright/MediaCodecList.cpp b/media/libstagefright/MediaCodecList.cpp
index 93478e9..3d58d4b 100644
--- a/media/libstagefright/MediaCodecList.cpp
+++ b/media/libstagefright/MediaCodecList.cpp
@@ -77,7 +77,8 @@
     return profilingNeeded;
 }
 
-OmxInfoBuilder sOmxInfoBuilder;
+OmxInfoBuilder sOmxInfoBuilder{true /* allowSurfaceEncoders */};
+OmxInfoBuilder sOmxNoSurfaceEncoderInfoBuilder{false /* allowSurfaceEncoders */};
 
 Mutex sCodec2InfoBuilderMutex;
 std::unique_ptr<MediaCodecListBuilderBase> sCodec2InfoBuilder;
@@ -98,7 +99,11 @@
     sp<PersistentSurface> surfaceTest =
         StagefrightPluginLoader::GetCCodecInstance()->createInputSurface();
     if (surfaceTest == nullptr) {
+        ALOGD("Allowing all OMX codecs");
         builders.push_back(&sOmxInfoBuilder);
+    } else {
+        ALOGD("Allowing only non-surface-encoder OMX codecs");
+        builders.push_back(&sOmxNoSurfaceEncoderInfoBuilder);
     }
     builders.push_back(GetCodec2InfoBuilder());
     return builders;
@@ -219,6 +224,21 @@
                 return info1 == nullptr
                         || (info2 != nullptr && info1->getRank() < info2->getRank());
             });
+
+    // remove duplicate entries
+    bool dedupe = property_get_bool("debug.stagefright.dedupe-codecs", true);
+    if (dedupe) {
+        std::set<std::string> codecsSeen;
+        for (auto it = mCodecInfos.begin(); it != mCodecInfos.end(); ) {
+            std::string codecName = (*it)->getCodecName();
+            if (codecsSeen.count(codecName) == 0) {
+                codecsSeen.emplace(codecName);
+                it++;
+            } else {
+                it = mCodecInfos.erase(it);
+            }
+        }
+    }
 }
 
 MediaCodecList::~MediaCodecList() {
@@ -268,10 +288,17 @@
 }
 
 ssize_t MediaCodecList::findCodecByName(const char *name) const {
+    Vector<AString> aliases;
     for (size_t i = 0; i < mCodecInfos.size(); ++i) {
         if (strcmp(mCodecInfos[i]->getCodecName(), name) == 0) {
             return i;
         }
+        mCodecInfos[i]->getAliases(&aliases);
+        for (const AString &alias : aliases) {
+            if (alias == name) {
+                return i;
+            }
+        }
     }
 
     return -ENOENT;
diff --git a/media/libstagefright/MediaCodecListWriter.cpp b/media/libstagefright/MediaCodecListWriter.cpp
index b32e470..c4fb199 100644
--- a/media/libstagefright/MediaCodecListWriter.cpp
+++ b/media/libstagefright/MediaCodecListWriter.cpp
@@ -37,6 +37,16 @@
             new MediaCodecInfoWriter(info.get()));
 }
 
+std::unique_ptr<MediaCodecInfoWriter>
+        MediaCodecListWriter::findMediaCodecInfo(const char *name) {
+    for (const sp<MediaCodecInfo> &info : mCodecInfos) {
+        if (!strcmp(info->getCodecName(), name)) {
+            return std::unique_ptr<MediaCodecInfoWriter>(new MediaCodecInfoWriter(info.get()));
+        }
+    }
+    return nullptr;
+}
+
 void MediaCodecListWriter::writeGlobalSettings(
         const sp<AMessage> &globalSettings) const {
     for (const std::pair<std::string, std::string> &kv : mGlobalSettings) {
diff --git a/media/libstagefright/OmxInfoBuilder.cpp b/media/libstagefright/OmxInfoBuilder.cpp
index 382c947..8910463 100644
--- a/media/libstagefright/OmxInfoBuilder.cpp
+++ b/media/libstagefright/OmxInfoBuilder.cpp
@@ -21,8 +21,8 @@
 #define OMX_ANDROID_COMPILE_AS_32BIT_ON_64BIT_PLATFORMS
 #endif
 
+#include <android-base/properties.h>
 #include <utils/Log.h>
-#include <cutils/properties.h>
 
 #include <media/stagefright/foundation/MediaDefs.h>
 #include <media/stagefright/OmxInfoBuilder.h>
@@ -53,7 +53,7 @@
 namespace /* unnamed */ {
 
 bool hasPrefix(const hidl_string& s, const char* prefix) {
-    return strncmp(s.c_str(), prefix, strlen(prefix)) == 0;
+    return strncasecmp(s.c_str(), prefix, strlen(prefix)) == 0;
 }
 
 status_t queryCapabilities(
@@ -87,7 +87,8 @@
 
 }  // unnamed namespace
 
-OmxInfoBuilder::OmxInfoBuilder() {
+OmxInfoBuilder::OmxInfoBuilder(bool allowSurfaceEncoders)
+    : mAllowSurfaceEncoders(allowSurfaceEncoders) {
 }
 
 status_t OmxInfoBuilder::buildMediaCodecList(MediaCodecListWriter* writer) {
@@ -135,81 +136,80 @@
     // Convert roles to lists of codecs
 
     // codec name -> index into swCodecs/hwCodecs
-    std::map<hidl_string, std::unique_ptr<MediaCodecInfoWriter>>
-            swCodecName2Info, hwCodecName2Info;
+    std::map<hidl_string, std::unique_ptr<MediaCodecInfoWriter>> codecName2Info;
 
-    char rank[PROPERTY_VALUE_MAX];
-    uint32_t defaultRank = 0x100;
-    if (property_get("debug.stagefright.omx_default_rank", rank, nullptr)) {
-        defaultRank = std::strtoul(rank, nullptr, 10);
-    }
+    uint32_t defaultRank =
+        ::android::base::GetUintProperty("debug.stagefright.omx_default_rank", 0x100u);
+    uint32_t defaultSwAudioRank =
+        ::android::base::GetUintProperty("debug.stagefright.omx_default_rank.sw-audio", 0x10u);
+    uint32_t defaultSwOtherRank =
+        ::android::base::GetUintProperty("debug.stagefright.omx_default_rank.sw-other", 0x210u);
+
     for (const IOmxStore::RoleInfo& role : roles) {
         const hidl_string& typeName = role.type;
         bool isEncoder = role.isEncoder;
-        bool preferPlatformNodes = role.preferPlatformNodes;
-        // If preferPlatformNodes is true, hardware nodes must be added after
-        // platform (software) nodes. hwCodecs is used to hold hardware nodes
-        // that need to be added after software nodes for the same role.
-        std::vector<const IOmxStore::NodeInfo*> hwCodecs;
-        for (const IOmxStore::NodeInfo& node : role.nodes) {
+        bool isAudio = hasPrefix(role.type, "audio/");
+        bool isVideoOrImage = hasPrefix(role.type, "video/") || hasPrefix(role.type, "image/");
+
+        for (const IOmxStore::NodeInfo &node : role.nodes) {
             const hidl_string& nodeName = node.name;
+
+            // currently image and video encoders use surface input
+            if (!mAllowSurfaceEncoders && isVideoOrImage && isEncoder) {
+                ALOGD("disabling %s for media type %s because we are not using OMX input surface",
+                        nodeName.c_str(), role.type.c_str());
+                continue;
+            }
+
             bool isSoftware = hasPrefix(nodeName, "OMX.google");
-            MediaCodecInfoWriter* info;
-            if (isSoftware) {
-                auto c2i = swCodecName2Info.find(nodeName);
-                if (c2i == swCodecName2Info.end()) {
-                    // Create a new MediaCodecInfo for a new node.
-                    c2i = swCodecName2Info.insert(std::make_pair(
-                            nodeName, writer->addMediaCodecInfo())).first;
-                    info = c2i->second.get();
-                    info->setName(nodeName.c_str());
-                    info->setOwner(node.owner.c_str());
-                    info->setAttributes(
-                            // all OMX codecs are vendor codecs (in the vendor partition), but
-                            // treat OMX.google codecs as non-hardware-accelerated and  non-vendor
-                            (isEncoder ? MediaCodecInfo::kFlagIsEncoder : 0));
-                    info->setRank(defaultRank);
-                } else {
-                    // The node has been seen before. Simply retrieve the
-                    // existing MediaCodecInfoWriter.
-                    info = c2i->second.get();
-                }
-            } else {
-                auto c2i = hwCodecName2Info.find(nodeName);
-                if (c2i == hwCodecName2Info.end()) {
-                    // Create a new MediaCodecInfo for a new node.
-                    if (!preferPlatformNodes) {
-                        c2i = hwCodecName2Info.insert(std::make_pair(
-                                nodeName, writer->addMediaCodecInfo())).first;
-                        info = c2i->second.get();
-                        info->setName(nodeName.c_str());
-                        info->setOwner(node.owner.c_str());
-                        typename std::underlying_type<MediaCodecInfo::Attributes>::type attrs =
-                            MediaCodecInfo::kFlagIsVendor;
-                        if (isEncoder) {
-                            attrs |= MediaCodecInfo::kFlagIsEncoder;
-                        }
-                        if (std::count_if(
-                                node.attributes.begin(), node.attributes.end(),
-                                [](const IOmxStore::Attribute &i) -> bool {
-                                    return i.key == "attribute::software-codec";
-                                                                          })) {
-                            attrs |= MediaCodecInfo::kFlagIsHardwareAccelerated;
-                        }
-                        info->setAttributes(attrs);
-                        info->setRank(defaultRank);
-                    } else {
-                        // If preferPlatformNodes is true, this node must be
-                        // added after all software nodes.
-                        hwCodecs.push_back(&node);
-                        continue;
+            uint32_t rank = isSoftware
+                    ? (isAudio ? defaultSwAudioRank : defaultSwOtherRank)
+                    : defaultRank;
+            // get rank from IOmxStore via attribute
+            for (const IOmxStore::Attribute& attribute : node.attributes) {
+                if (attribute.key == "rank") {
+                    uint32_t oldRank = rank;
+                    char dummy;
+                    if (sscanf(attribute.value.c_str(), "%u%c", &rank, &dummy) != 1) {
+                        rank = oldRank;
                     }
-                } else {
-                    // The node has been seen before. Simply retrieve the
-                    // existing MediaCodecInfoWriter.
-                    info = c2i->second.get();
+                    break;
                 }
             }
+
+            MediaCodecInfoWriter* info;
+            auto c2i = codecName2Info.find(nodeName);
+            if (c2i == codecName2Info.end()) {
+                // Create a new MediaCodecInfo for a new node.
+                c2i = codecName2Info.insert(std::make_pair(
+                        nodeName, writer->addMediaCodecInfo())).first;
+                info = c2i->second.get();
+                info->setName(nodeName.c_str());
+                info->setOwner(node.owner.c_str());
+                info->setRank(rank);
+
+                typename std::underlying_type<MediaCodecInfo::Attributes>::type attrs = 0;
+                // all OMX codecs are vendor codecs (in the vendor partition), but
+                // treat OMX.google codecs as non-hardware-accelerated and non-vendor
+                if (!isSoftware) {
+                    attrs |= MediaCodecInfo::kFlagIsVendor;
+                    if (std::count_if(
+                            node.attributes.begin(), node.attributes.end(),
+                            [](const IOmxStore::Attribute &i) -> bool {
+                                return i.key == "attribute::software-codec";
+                                                                      })) {
+                        attrs |= MediaCodecInfo::kFlagIsHardwareAccelerated;
+                    }
+                }
+                if (isEncoder) {
+                    attrs |= MediaCodecInfo::kFlagIsEncoder;
+                }
+                info->setAttributes(attrs);
+            } else {
+                // The node has been seen before. Simply retrieve the
+                // existing MediaCodecInfoWriter.
+                info = c2i->second.get();
+            }
             std::unique_ptr<MediaCodecInfo::CapabilitiesWriter> caps =
                     info->addMediaType(typeName.c_str());
             if (queryCapabilities(
@@ -219,54 +219,8 @@
                 info->removeMediaType(typeName.c_str());
             }
         }
-
-        // If preferPlatformNodes is true, hardware nodes will not have been
-        // added in the loop above, but rather saved in hwCodecs. They are
-        // going to be added here.
-        if (preferPlatformNodes) {
-            for (const IOmxStore::NodeInfo *node : hwCodecs) {
-                MediaCodecInfoWriter* info;
-                const hidl_string& nodeName = node->name;
-                auto c2i = hwCodecName2Info.find(nodeName);
-                if (c2i == hwCodecName2Info.end()) {
-                    // Create a new MediaCodecInfo for a new node.
-                    c2i = hwCodecName2Info.insert(std::make_pair(
-                            nodeName, writer->addMediaCodecInfo())).first;
-                    info = c2i->second.get();
-                    info->setName(nodeName.c_str());
-                    info->setOwner(node->owner.c_str());
-                    typename std::underlying_type<MediaCodecInfo::Attributes>::type attrs =
-                        MediaCodecInfo::kFlagIsVendor;
-                    if (isEncoder) {
-                        attrs |= MediaCodecInfo::kFlagIsEncoder;
-                    }
-                    if (std::count_if(
-                            node->attributes.begin(), node->attributes.end(),
-                            [](const IOmxStore::Attribute &i) -> bool {
-                                return i.key == "attribute::software-codec";
-                                                                      })) {
-                        attrs |= MediaCodecInfo::kFlagIsHardwareAccelerated;
-                    }
-                    info->setRank(defaultRank);
-                } else {
-                    // The node has been seen before. Simply retrieve the
-                    // existing MediaCodecInfoWriter.
-                    info = c2i->second.get();
-                }
-                std::unique_ptr<MediaCodecInfo::CapabilitiesWriter> caps =
-                        info->addMediaType(typeName.c_str());
-                if (queryCapabilities(
-                        *node, typeName.c_str(), isEncoder, caps.get()) != OK) {
-                    ALOGW("Fail to add media type %s to codec %s "
-                          "after software codecs",
-                          typeName.c_str(), nodeName.c_str());
-                    info->removeMediaType(typeName.c_str());
-                }
-            }
-        }
     }
     return OK;
 }
 
 }  // namespace android
-
diff --git a/media/libstagefright/data/media_codecs_google_c2_audio.xml b/media/libstagefright/data/media_codecs_google_c2_audio.xml
index 88cd08d..f664395 100644
--- a/media/libstagefright/data/media_codecs_google_c2_audio.xml
+++ b/media/libstagefright/data/media_codecs_google_c2_audio.xml
@@ -17,51 +17,61 @@
 <Included>
     <Decoders>
         <MediaCodec name="c2.android.mp3.decoder" type="audio/mpeg">
+            <Alias name="OMX.google.mp3.decoder" />
             <Limit name="channel-count" max="2" />
             <Limit name="sample-rate" ranges="8000,11025,12000,16000,22050,24000,32000,44100,48000" />
             <Limit name="bitrate" range="8000-320000" />
         </MediaCodec>
         <MediaCodec name="c2.android.amrnb.decoder" type="audio/3gpp">
+            <Alias name="OMX.google.amrnb.decoder" />
             <Limit name="channel-count" max="1" />
             <Limit name="sample-rate" ranges="8000" />
             <Limit name="bitrate" range="4750-12200" />
         </MediaCodec>
         <MediaCodec name="c2.android.amrwb.decoder" type="audio/amr-wb">
+            <Alias name="OMX.google.amrwb.decoder" />
             <Limit name="channel-count" max="1" />
             <Limit name="sample-rate" ranges="16000" />
             <Limit name="bitrate" range="6600-23850" />
         </MediaCodec>
         <MediaCodec name="c2.android.aac.decoder" type="audio/mp4a-latm">
+            <Alias name="OMX.google.aac.decoder" />
             <Limit name="channel-count" max="8" />
             <Limit name="sample-rate" ranges="7350,8000,11025,12000,16000,22050,24000,32000,44100,48000" />
             <Limit name="bitrate" range="8000-960000" />
         </MediaCodec>
         <MediaCodec name="c2.android.g711.alaw.decoder" type="audio/g711-alaw">
+            <Alias name="OMX.google.g711.alaw.decoder" />
             <Limit name="channel-count" max="1" />
             <Limit name="sample-rate" ranges="8000-48000" />
             <Limit name="bitrate" range="64000" />
         </MediaCodec>
         <MediaCodec name="c2.android.g711.mlaw.decoder" type="audio/g711-mlaw">
+            <Alias name="OMX.google.g711.mlaw.decoder" />
             <Limit name="channel-count" max="1" />
             <Limit name="sample-rate" ranges="8000-48000" />
             <Limit name="bitrate" range="64000" />
         </MediaCodec>
         <MediaCodec name="c2.android.vorbis.decoder" type="audio/vorbis">
+            <Alias name="OMX.google.vorbis.decoder" />
             <Limit name="channel-count" max="8" />
             <Limit name="sample-rate" ranges="8000-96000" />
             <Limit name="bitrate" range="32000-500000" />
         </MediaCodec>
         <MediaCodec name="c2.android.opus.decoder" type="audio/opus">
+            <Alias name="OMX.google.opus.decoder" />
             <Limit name="channel-count" max="8" />
             <Limit name="sample-rate" ranges="48000" />
             <Limit name="bitrate" range="6000-510000" />
         </MediaCodec>
         <MediaCodec name="c2.android.raw.decoder" type="audio/raw">
+            <Alias name="OMX.google.raw.decoder" />
             <Limit name="channel-count" max="8" />
             <Limit name="sample-rate" ranges="8000-96000" />
             <Limit name="bitrate" range="1-10000000" />
         </MediaCodec>
         <MediaCodec name="c2.android.flac.decoder" type="audio/flac">
+            <Alias name="OMX.google.flac.decoder" />
             <Limit name="channel-count" max="8" />
             <Limit name="sample-rate" ranges="1-655350" />
             <Limit name="bitrate" range="1-21000000" />
@@ -69,24 +79,28 @@
     </Decoders>
     <Encoders>
         <MediaCodec name="c2.android.aac.encoder" type="audio/mp4a-latm">
+            <Alias name="OMX.google.aac.decoder" />
             <Limit name="channel-count" max="6" />
             <Limit name="sample-rate" ranges="8000,11025,12000,16000,22050,24000,32000,44100,48000" />
             <!-- also may support 64000, 88200  and 96000 Hz -->
             <Limit name="bitrate" range="8000-960000" />
         </MediaCodec>
         <MediaCodec name="c2.android.amrnb.encoder" type="audio/3gpp">
+            <Alias name="OMX.google.amrnb.decoder" />
             <Limit name="channel-count" max="1" />
             <Limit name="sample-rate" ranges="8000" />
             <Limit name="bitrate" range="4750-12200" />
             <Feature name="bitrate-modes" value="CBR" />
         </MediaCodec>
         <MediaCodec name="c2.android.amrwb.encoder" type="audio/amr-wb">
+            <Alias name="OMX.google.amrwb.decoder" />
             <Limit name="channel-count" max="1" />
             <Limit name="sample-rate" ranges="16000" />
             <Limit name="bitrate" range="6600-23850" />
             <Feature name="bitrate-modes" value="CBR" />
         </MediaCodec>
         <MediaCodec name="c2.android.flac.encoder" type="audio/flac">
+            <Alias name="OMX.google.flac.decoder" />
             <Limit name="channel-count" max="2" />
             <Limit name="sample-rate" ranges="1-655350" />
             <Limit name="bitrate" range="1-21000000" />
diff --git a/media/libstagefright/data/media_codecs_google_c2_telephony.xml b/media/libstagefright/data/media_codecs_google_c2_telephony.xml
index d1055b3..950b092 100644
--- a/media/libstagefright/data/media_codecs_google_c2_telephony.xml
+++ b/media/libstagefright/data/media_codecs_google_c2_telephony.xml
@@ -17,6 +17,7 @@
 <Included>
     <Decoders>
         <MediaCodec name="c2.android.gsm.decoder" type="audio/gsm">
+            <Alias name="OMX.google.gsm.decoder" />
             <Limit name="channel-count" max="1" />
             <Limit name="sample-rate" ranges="8000" />
             <Limit name="bitrate" range="13000" />
diff --git a/media/libstagefright/data/media_codecs_google_c2_tv.xml b/media/libstagefright/data/media_codecs_google_c2_tv.xml
index fa082c7..1b00dc9 100644
--- a/media/libstagefright/data/media_codecs_google_c2_tv.xml
+++ b/media/libstagefright/data/media_codecs_google_c2_tv.xml
@@ -17,6 +17,7 @@
 <Included>
     <Decoders>
         <MediaCodec name="c2.android.mpeg2.decoder" type="video/mpeg2">
+            <Alias name="OMX.google.mpeg2.decoder" />
             <!-- profiles and levels:  ProfileMain : LevelHL -->
             <Limit name="size" min="16x16" max="1920x1088" />
             <Limit name="alignment" value="2x2" />
diff --git a/media/libstagefright/data/media_codecs_google_c2_video.xml b/media/libstagefright/data/media_codecs_google_c2_video.xml
index c49789e..5c2d96d 100644
--- a/media/libstagefright/data/media_codecs_google_c2_video.xml
+++ b/media/libstagefright/data/media_codecs_google_c2_video.xml
@@ -17,6 +17,7 @@
 <Included>
     <Decoders>
         <MediaCodec name="c2.android.mpeg4.decoder" type="video/mp4v-es">
+            <Alias name="OMX.google.mpeg4.decoder" />
             <!-- profiles and levels:  ProfileSimple : Level3 -->
             <Limit name="size" min="2x2" max="352x288" />
             <Limit name="alignment" value="2x2" />
@@ -26,6 +27,7 @@
             <Feature name="adaptive-playback" />
         </MediaCodec>
         <MediaCodec name="c2.android.h263.decoder" type="video/3gpp">
+            <Alias name="OMX.google.h263.decoder" />
             <!-- profiles and levels:  ProfileBaseline : Level30, ProfileBaseline : Level45
                     ProfileISWV2 : Level30, ProfileISWV2 : Level45 -->
             <Limit name="size" min="2x2" max="352x288" />
@@ -34,6 +36,7 @@
             <Feature name="adaptive-playback" />
         </MediaCodec>
         <MediaCodec name="c2.android.avc.decoder" type="video/avc">
+            <Alias name="OMX.google.h264.decoder" />
             <!-- profiles and levels:  ProfileHigh : Level52 -->
             <Limit name="size" min="2x2" max="4080x4080" />
             <Limit name="alignment" value="2x2" />
@@ -44,6 +47,7 @@
             <Feature name="adaptive-playback" />
         </MediaCodec>
         <MediaCodec name="c2.android.hevc.decoder" type="video/hevc">
+            <Alias name="OMX.google.hevc.decoder" />
             <!-- profiles and levels:  ProfileMain : MainTierLevel51 -->
             <Limit name="size" min="2x2" max="4096x4096" />
             <Limit name="alignment" value="2x2" />
@@ -54,6 +58,7 @@
             <Feature name="adaptive-playback" />
         </MediaCodec>
         <MediaCodec name="c2.android.vp8.decoder" type="video/x-vnd.on2.vp8">
+            <Alias name="OMX.google.vp8.decoder" />
             <Limit name="size" min="2x2" max="2048x2048" />
             <Limit name="alignment" value="2x2" />
             <Limit name="block-size" value="16x16" />
@@ -63,6 +68,7 @@
             <Feature name="adaptive-playback" />
         </MediaCodec>
         <MediaCodec name="c2.android.vp9.decoder" type="video/x-vnd.on2.vp9">
+            <Alias name="OMX.google.vp9.decoder" />
             <Limit name="size" min="2x2" max="2048x2048" />
             <Limit name="alignment" value="2x2" />
             <Limit name="block-size" value="16x16" />
@@ -84,12 +90,14 @@
 
     <Encoders>
         <MediaCodec name="c2.android.h263.encoder" type="video/3gpp">
+            <Alias name="OMX.google.h263.encoder" />
             <!-- profiles and levels:  ProfileBaseline : Level45 -->
             <Limit name="size" min="176x144" max="176x144" />
             <Limit name="alignment" value="16x16" />
             <Limit name="bitrate" range="1-128000" />
         </MediaCodec>
         <MediaCodec name="c2.android.avc.encoder" type="video/avc">
+            <Alias name="OMX.google.h264.encoder" />
             <!-- profiles and levels:  ProfileBaseline : Level41 -->
             <Limit name="size" min="16x16" max="2048x2048" />
             <Limit name="alignment" value="2x2" />
@@ -100,6 +108,7 @@
             <Feature name="intra-refresh" />
         </MediaCodec>
         <MediaCodec name="c2.android.mpeg4.encoder" type="video/mp4v-es">
+            <Alias name="OMX.google.mpeg4.encoder" />
             <!-- profiles and levels:  ProfileCore : Level2 -->
             <Limit name="size" min="16x16" max="176x144" />
             <Limit name="alignment" value="16x16" />
@@ -108,6 +117,7 @@
             <Limit name="bitrate" range="1-64000" />
         </MediaCodec>
         <MediaCodec name="c2.android.vp8.encoder" type="video/x-vnd.on2.vp8">
+            <Alias name="OMX.google.vp8.encoder" />
             <!-- profiles and levels:  ProfileMain : Level_Version0-3 -->
             <Limit name="size" min="2x2" max="2048x2048" />
             <Limit name="alignment" value="2x2" />
@@ -118,6 +128,7 @@
             <Feature name="bitrate-modes" value="VBR,CBR" />
         </MediaCodec>
         <MediaCodec name="c2.android.vp9.encoder" type="video/x-vnd.on2.vp9">
+            <Alias name="OMX.google.vp9.encoder" />
             <!-- profiles and levels:  ProfileMain : Level_Version0-3 -->
             <Limit name="size" min="2x2" max="2048x2048" />
             <Limit name="alignment" value="2x2" />
diff --git a/media/libstagefright/include/media/stagefright/MediaCodecListWriter.h b/media/libstagefright/include/media/stagefright/MediaCodecListWriter.h
index 59f57c7..f53b23e 100644
--- a/media/libstagefright/include/media/stagefright/MediaCodecListWriter.h
+++ b/media/libstagefright/include/media/stagefright/MediaCodecListWriter.h
@@ -48,6 +48,13 @@
      * added `MediaCodecInfo` object.
      */
     std::unique_ptr<MediaCodecInfoWriter> addMediaCodecInfo();
+    /**
+     * Find an existing `MediaCodecInfo` object for a codec name and return a
+     * `MediaCodecInfoWriter` object associated with the found added `MediaCodecInfo`.
+     *
+     * @return The `MediaCodecInfoWriter` object if found, or nullptr if not found.
+     */
+    std::unique_ptr<MediaCodecInfoWriter> findMediaCodecInfo(const char *codecName);
 private:
     MediaCodecListWriter() = default;
 
diff --git a/media/libstagefright/include/media/stagefright/OmxInfoBuilder.h b/media/libstagefright/include/media/stagefright/OmxInfoBuilder.h
index 28f6094..1410a16 100644
--- a/media/libstagefright/include/media/stagefright/OmxInfoBuilder.h
+++ b/media/libstagefright/include/media/stagefright/OmxInfoBuilder.h
@@ -23,8 +23,11 @@
 namespace android {
 
 class OmxInfoBuilder : public MediaCodecListBuilderBase {
+private:
+    bool mAllowSurfaceEncoders; // allow surface encoders
+
 public:
-    OmxInfoBuilder();
+    explicit OmxInfoBuilder(bool allowSurfaceEncoders);
     ~OmxInfoBuilder() override = default;
     status_t buildMediaCodecList(MediaCodecListWriter* writer) override;
 };
diff --git a/media/libstagefright/omx/1.0/OmxStore.cpp b/media/libstagefright/omx/1.0/OmxStore.cpp
index 447af6f..2e041e3 100644
--- a/media/libstagefright/omx/1.0/OmxStore.cpp
+++ b/media/libstagefright/omx/1.0/OmxStore.cpp
@@ -61,10 +61,7 @@
         role.role = rolePair.first;
         role.type = rolePair.second.type;
         role.isEncoder = rolePair.second.isEncoder;
-        // TODO: Currently, preferPlatformNodes information is not available in
-        // the xml file. Once we have a way to provide this information, it
-        // should be parsed properly.
-        role.preferPlatformNodes = rolePair.first.compare(0, 5, "audio") == 0;
+        role.preferPlatformNodes = false; // deprecated and ignored, using rank instead
         hidl_vec<NodeInfo>& nodeList = role.nodes;
         nodeList.resize(rolePair.second.nodeList.size());
         size_t j = 0;
diff --git a/media/libstagefright/xmlparser/MediaCodecsXmlParser.cpp b/media/libstagefright/xmlparser/MediaCodecsXmlParser.cpp
index 6e541ba..7046f61 100644
--- a/media/libstagefright/xmlparser/MediaCodecsXmlParser.cpp
+++ b/media/libstagefright/xmlparser/MediaCodecsXmlParser.cpp
@@ -502,6 +502,7 @@
     const char *name = nullptr;
     const char *type = nullptr;
     const char *update = nullptr;
+    const char *rank = nullptr;
 
     size_t i = 0;
     while (attrs[i] != nullptr) {
@@ -523,6 +524,12 @@
                 return BAD_VALUE;
             }
             update = attrs[i];
+        } else if (strEq(attrs[i], "rank")) {
+            if (attrs[++i] == nullptr) {
+                ALOGE("addMediaCodecFromAttributes: rank is null");
+                return BAD_VALUE;
+            }
+            rank = attrs[i];
         } else {
             ALOGE("addMediaCodecFromAttributes: unrecognized attribute: %s", attrs[i]);
             return BAD_VALUE;
@@ -579,6 +586,15 @@
         }
     }
 
+    if (rank != nullptr) {
+        if (!mCurrentCodec->second.rank.empty() && mCurrentCodec->second.rank != rank) {
+            ALOGE("addMediaCodecFromAttributes: code \"%s\" rank changed from \"%s\" to \"%s\"",
+                    name, mCurrentCodec->second.rank.c_str(), rank);
+            return BAD_VALUE;
+        }
+        mCurrentCodec->second.rank = rank;
+    }
+
     return OK;
 }
 
@@ -1035,6 +1051,7 @@
         const auto& codecName = codec.first;
         bool isEncoder = codec.second.isEncoder;
         size_t order = codec.second.order;
+        std::string rank = codec.second.rank;
         const auto& typeMap = codec.second.typeMap;
         for (const auto& type : typeMap) {
             const auto& typeName = type.first;
@@ -1090,6 +1107,9 @@
                     nodeInfo.attributeList.push_back(Attribute{quirk, "present"});
                 }
             }
+            if (!rank.empty()) {
+                nodeInfo.attributeList.push_back(Attribute{"rank", rank});
+            }
             nodeList->insert(std::make_pair(
                     std::move(order), std::move(nodeInfo)));
         }
diff --git a/media/libstagefright/xmlparser/include/media/stagefright/xmlparser/MediaCodecsXmlParser.h b/media/libstagefright/xmlparser/include/media/stagefright/xmlparser/MediaCodecsXmlParser.h
index fd949da..7a986b7 100644
--- a/media/libstagefright/xmlparser/include/media/stagefright/xmlparser/MediaCodecsXmlParser.h
+++ b/media/libstagefright/xmlparser/include/media/stagefright/xmlparser/MediaCodecsXmlParser.h
@@ -66,6 +66,7 @@
         QuirkSet quirkSet; ///< Set of quirks requested by this codec
         TypeMap typeMap;   ///< Map of types supported by this codec
         std::vector<std::string> aliases; ///< Name aliases for this codec
+        std::string rank;  ///< Rank of this codec. This is a numeric string.
     };
 
     typedef std::pair<std::string, CodecProperties> Codec;
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index bc99099..7733071 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -292,13 +292,16 @@
         fullConfig.sample_rate = config->sample_rate;
         fullConfig.channel_mask = config->channel_mask;
         fullConfig.format = config->format;
+        std::vector<audio_io_handle_t> secondaryOutputs;
         ret = AudioSystem::getOutputForAttr(attr, &io,
                                             actualSessionId,
                                             &streamType, client.clientPid, client.clientUid,
                                             &fullConfig,
                                             (audio_output_flags_t)(AUDIO_OUTPUT_FLAG_MMAP_NOIRQ |
                                                     AUDIO_OUTPUT_FLAG_DIRECT),
-                                            deviceId, &portId);
+                                            deviceId, &portId, &secondaryOutputs);
+        ALOGW_IF(!secondaryOutputs.empty(),
+                 "%s does not support secondary outputs, ignoring them", __func__);
     } else {
         ret = AudioSystem::getInputForAttr(attr, &io,
                                               actualSessionId,
@@ -678,6 +681,7 @@
     status_t lStatus;
     audio_stream_type_t streamType;
     audio_port_handle_t portId = AUDIO_PORT_HANDLE_NONE;
+    std::vector<audio_io_handle_t> secondaryOutputs;
 
     bool updatePid = (input.clientInfo.clientPid == -1);
     const uid_t callingUid = IPCThreadState::self()->getCallingUid();
@@ -712,7 +716,7 @@
 
     lStatus = AudioSystem::getOutputForAttr(&input.attr, &output.outputId, sessionId, &streamType,
                                             clientPid, clientUid, &input.config, input.flags,
-                                            &output.selectedDeviceId, &portId);
+                                            &output.selectedDeviceId, &portId, &secondaryOutputs);
 
     if (lStatus != NO_ERROR || output.outputId == AUDIO_IO_HANDLE_NONE) {
         ALOGE("createTrack() getOutputForAttr() return error %d or invalid output handle", lStatus);
@@ -785,6 +789,59 @@
         output.afLatencyMs = thread->latency();
         output.portId = portId;
 
+        if (lStatus == NO_ERROR) {
+            // Connect secondary outputs. Failure on a secondary output must not imped the primary
+            // Any secondary output setup failure will lead to a desync between the AP and AF until
+            // the track is destroyed.
+            TeePatches teePatches;
+            for (audio_io_handle_t secondaryOutput : secondaryOutputs) {
+                PlaybackThread *secondaryThread = checkPlaybackThread_l(secondaryOutput);
+                if (secondaryThread == NULL) {
+                    ALOGE("no playback thread found for secondary output %d", output.outputId);
+                    continue;
+                }
+
+                size_t frameCount = std::lcm(thread->frameCount(), secondaryThread->frameCount());
+
+                using namespace std::chrono_literals;
+                auto inChannelMask = audio_channel_mask_out_to_in(input.config.channel_mask);
+                sp patchRecord = new RecordThread::PatchRecord(nullptr /* thread */,
+                                                               output.sampleRate,
+                                                               inChannelMask,
+                                                               input.config.format,
+                                                               frameCount,
+                                                               NULL /* buffer */,
+                                                               (size_t)0 /* bufferSize */,
+                                                               AUDIO_INPUT_FLAG_DIRECT,
+                                                               0ns /* timeout */);
+                status_t status = patchRecord->initCheck();
+                if (status != NO_ERROR) {
+                    ALOGE("Secondary output patchRecord init failed: %d", status);
+                    continue;
+                }
+                sp patchTrack = new PlaybackThread::PatchTrack(secondaryThread,
+                                                               streamType,
+                                                               output.sampleRate,
+                                                               input.config.channel_mask,
+                                                               input.config.format,
+                                                               frameCount,
+                                                               patchRecord->buffer(),
+                                                               patchRecord->bufferSize(),
+                                                               output.flags,
+                                                               0ns /* timeout */);
+                status = patchTrack->initCheck();
+                if (status != NO_ERROR) {
+                    ALOGE("Secondary output patchTrack init failed: %d", status);
+                    continue;
+                }
+                teePatches.push_back({patchRecord, patchTrack});
+                secondaryThread->addPatchTrack(patchTrack);
+                patchTrack->setPeerProxy(patchRecord.get());
+                patchRecord->setPeerProxy(patchTrack.get());
+            }
+            track->setTeePatches(std::move(teePatches));
+        }
+
         // move effect chain to this output thread if an effect on same session was waiting
         // for a track to be created
         if (lStatus == NO_ERROR && effectThread != NULL) {
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index d8c0da5..1441e15 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -21,8 +21,11 @@
 #include "Configuration.h"
 #include <atomic>
 #include <mutex>
+#include <chrono>
 #include <deque>
 #include <map>
+#include <numeric>
+#include <optional>
 #include <set>
 #include <string>
 #include <vector>
@@ -526,6 +529,9 @@
     class EffectChain;
 
     struct AudioStreamIn;
+    struct TeePatch;
+    using TeePatches = std::vector<TeePatch>;
+
 
     struct  stream_type_t {
         stream_type_t()
@@ -725,6 +731,11 @@
             audioHwDev(dev), stream(in), flags(flags) {}
     };
 
+    struct TeePatch {
+        sp<RecordThread::PatchRecord> patchRecord;
+        sp<PlaybackThread::PatchTrack> patchTrack;
+    };
+
     // for mAudioSessionRefs only
     struct AudioSessionRef {
         AudioSessionRef(audio_session_t sessionid, pid_t pid) :
diff --git a/services/audioflinger/FastMixer.cpp b/services/audioflinger/FastMixer.cpp
index e78c98b..c5b9953 100644
--- a/services/audioflinger/FastMixer.cpp
+++ b/services/audioflinger/FastMixer.cpp
@@ -139,6 +139,75 @@
     }
 }
 
+void FastMixer::updateMixerTrack(int index, Reason reason) {
+    const FastMixerState * const current = (const FastMixerState *) mCurrent;
+    const FastTrack * const fastTrack = &current->mFastTracks[index];
+
+    // check and update generation
+    if (reason == REASON_MODIFY && mGenerations[index] == fastTrack->mGeneration) {
+        return; // no change on an already configured track.
+    }
+    mGenerations[index] = fastTrack->mGeneration;
+
+    // mMixer == nullptr on configuration failure (check done after generation update).
+    if (mMixer == nullptr) {
+        return;
+    }
+
+    switch (reason) {
+    case REASON_REMOVE:
+        mMixer->destroy(index);
+        break;
+    case REASON_ADD: {
+        const status_t status = mMixer->create(
+                index, fastTrack->mChannelMask, fastTrack->mFormat, AUDIO_SESSION_OUTPUT_MIX);
+        LOG_ALWAYS_FATAL_IF(status != NO_ERROR,
+                "%s: cannot create fast track index"
+                " %d, mask %#x, format %#x in AudioMixer",
+                __func__, index, fastTrack->mChannelMask, fastTrack->mFormat);
+    }
+        [[fallthrough]];  // now fallthrough to update the newly created track.
+    case REASON_MODIFY:
+        mMixer->setBufferProvider(index, fastTrack->mBufferProvider);
+
+        float vlf, vrf;
+        if (fastTrack->mVolumeProvider != nullptr) {
+            const gain_minifloat_packed_t vlr = fastTrack->mVolumeProvider->getVolumeLR();
+            vlf = float_from_gain(gain_minifloat_unpack_left(vlr));
+            vrf = float_from_gain(gain_minifloat_unpack_right(vlr));
+        } else {
+            vlf = vrf = AudioMixer::UNITY_GAIN_FLOAT;
+        }
+
+        // set volume to avoid ramp whenever the track is updated (or created).
+        // Note: this does not distinguish from starting fresh or
+        // resuming from a paused state.
+        mMixer->setParameter(index, AudioMixer::VOLUME, AudioMixer::VOLUME0, &vlf);
+        mMixer->setParameter(index, AudioMixer::VOLUME, AudioMixer::VOLUME1, &vrf);
+
+        mMixer->setParameter(index, AudioMixer::RESAMPLE, AudioMixer::REMOVE, nullptr);
+        mMixer->setParameter(index, AudioMixer::TRACK, AudioMixer::MAIN_BUFFER,
+                (void *)mMixerBuffer);
+        mMixer->setParameter(index, AudioMixer::TRACK, AudioMixer::MIXER_FORMAT,
+                (void *)(uintptr_t)mMixerBufferFormat);
+        mMixer->setParameter(index, AudioMixer::TRACK, AudioMixer::FORMAT,
+                (void *)(uintptr_t)fastTrack->mFormat);
+        mMixer->setParameter(index, AudioMixer::TRACK, AudioMixer::CHANNEL_MASK,
+                (void *)(uintptr_t)fastTrack->mChannelMask);
+        mMixer->setParameter(index, AudioMixer::TRACK, AudioMixer::MIXER_CHANNEL_MASK,
+                (void *)(uintptr_t)mSinkChannelMask);
+        mMixer->setParameter(index, AudioMixer::TRACK, AudioMixer::HAPTIC_ENABLED,
+                (void *)(uintptr_t)fastTrack->mHapticPlaybackEnabled);
+        mMixer->setParameter(index, AudioMixer::TRACK, AudioMixer::HAPTIC_INTENSITY,
+                (void *)(uintptr_t)fastTrack->mHapticIntensity);
+
+        mMixer->enable(index);
+        break;
+    default:
+        LOG_ALWAYS_FATAL("%s: invalid update reason %d", __func__, reason);
+    }
+}
+
 void FastMixer::onStateChange()
 {
     const FastMixerState * const current = (const FastMixerState *) mCurrent;
@@ -240,21 +309,16 @@
     // check for change in active track set
     const unsigned currentTrackMask = current->mTrackMask;
     dumpState->mTrackMask = currentTrackMask;
+    dumpState->mNumTracks = popcount(currentTrackMask);
     if (current->mFastTracksGen != mFastTracksGen) {
-        ALOG_ASSERT(mMixerBuffer != NULL);
 
         // process removed tracks first to avoid running out of track names
         unsigned removedTracks = previousTrackMask & ~currentTrackMask;
         while (removedTracks != 0) {
             int i = __builtin_ctz(removedTracks);
             removedTracks &= ~(1 << i);
-            const FastTrack* fastTrack = &current->mFastTracks[i];
-            ALOG_ASSERT(fastTrack->mBufferProvider == NULL);
-            if (mMixer != NULL) {
-                mMixer->destroy(i);
-            }
+            updateMixerTrack(i, REASON_REMOVE);
             // don't reset track dump state, since other side is ignoring it
-            mGenerations[i] = fastTrack->mGeneration;
         }
 
         // now process added tracks
@@ -262,40 +326,7 @@
         while (addedTracks != 0) {
             int i = __builtin_ctz(addedTracks);
             addedTracks &= ~(1 << i);
-            const FastTrack* fastTrack = &current->mFastTracks[i];
-            AudioBufferProvider *bufferProvider = fastTrack->mBufferProvider;
-            if (mMixer != NULL) {
-                const int name = i; // for clarity, choose name as fast track index.
-                status_t status = mMixer->create(
-                        name,
-                        fastTrack->mChannelMask,
-                        fastTrack->mFormat, AUDIO_SESSION_OUTPUT_MIX);
-                LOG_ALWAYS_FATAL_IF(status != NO_ERROR,
-                        "%s: cannot create track name"
-                        " %d, mask %#x, format %#x, sessionId %d in AudioMixer",
-                        __func__, name,
-                        fastTrack->mChannelMask, fastTrack->mFormat, AUDIO_SESSION_OUTPUT_MIX);
-                mMixer->setBufferProvider(name, bufferProvider);
-                mMixer->setParameter(name, AudioMixer::TRACK, AudioMixer::MAIN_BUFFER,
-                        (void *)mMixerBuffer);
-                // newly allocated track names default to full scale volume
-                mMixer->setParameter(
-                        name,
-                        AudioMixer::TRACK,
-                        AudioMixer::MIXER_FORMAT, (void *)mMixerBufferFormat);
-                mMixer->setParameter(name, AudioMixer::TRACK, AudioMixer::FORMAT,
-                        (void *)(uintptr_t)fastTrack->mFormat);
-                mMixer->setParameter(name, AudioMixer::TRACK, AudioMixer::CHANNEL_MASK,
-                        (void *)(uintptr_t)fastTrack->mChannelMask);
-                mMixer->setParameter(name, AudioMixer::TRACK, AudioMixer::MIXER_CHANNEL_MASK,
-                        (void *)(uintptr_t)mSinkChannelMask);
-                mMixer->setParameter(name, AudioMixer::TRACK, AudioMixer::HAPTIC_ENABLED,
-                        (void *)(uintptr_t)fastTrack->mHapticPlaybackEnabled);
-                mMixer->setParameter(name, AudioMixer::TRACK, AudioMixer::HAPTIC_INTENSITY,
-                        (void *)(uintptr_t)fastTrack->mHapticIntensity);
-                mMixer->enable(name);
-            }
-            mGenerations[i] = fastTrack->mGeneration;
+            updateMixerTrack(i, REASON_ADD);
         }
 
         // finally process (potentially) modified tracks; these use the same slot
@@ -304,44 +335,10 @@
         while (modifiedTracks != 0) {
             int i = __builtin_ctz(modifiedTracks);
             modifiedTracks &= ~(1 << i);
-            const FastTrack* fastTrack = &current->mFastTracks[i];
-            if (fastTrack->mGeneration != mGenerations[i]) {
-                // this track was actually modified
-                AudioBufferProvider *bufferProvider = fastTrack->mBufferProvider;
-                ALOG_ASSERT(bufferProvider != NULL);
-                if (mMixer != NULL) {
-                    const int name = i;
-                    mMixer->setBufferProvider(name, bufferProvider);
-                    if (fastTrack->mVolumeProvider == NULL) {
-                        float f = AudioMixer::UNITY_GAIN_FLOAT;
-                        mMixer->setParameter(name, AudioMixer::VOLUME, AudioMixer::VOLUME0, &f);
-                        mMixer->setParameter(name, AudioMixer::VOLUME, AudioMixer::VOLUME1, &f);
-                    }
-                    mMixer->setParameter(name, AudioMixer::RESAMPLE,
-                            AudioMixer::REMOVE, NULL);
-                    mMixer->setParameter(
-                            name,
-                            AudioMixer::TRACK,
-                            AudioMixer::MIXER_FORMAT, (void *)mMixerBufferFormat);
-                    mMixer->setParameter(name, AudioMixer::TRACK, AudioMixer::FORMAT,
-                            (void *)(uintptr_t)fastTrack->mFormat);
-                    mMixer->setParameter(name, AudioMixer::TRACK, AudioMixer::CHANNEL_MASK,
-                            (void *)(uintptr_t)fastTrack->mChannelMask);
-                    mMixer->setParameter(name, AudioMixer::TRACK, AudioMixer::MIXER_CHANNEL_MASK,
-                            (void *)(uintptr_t)mSinkChannelMask);
-                    mMixer->setParameter(name, AudioMixer::TRACK, AudioMixer::HAPTIC_ENABLED,
-                            (void *)(uintptr_t)fastTrack->mHapticPlaybackEnabled);
-                    mMixer->setParameter(name, AudioMixer::TRACK, AudioMixer::HAPTIC_INTENSITY,
-                            (void *)(uintptr_t)fastTrack->mHapticIntensity);
-                    // already enabled
-                }
-                mGenerations[i] = fastTrack->mGeneration;
-            }
+            updateMixerTrack(i, REASON_MODIFY);
         }
 
         mFastTracksGen = current->mFastTracksGen;
-
-        dumpState->mNumTracks = popcount(currentTrackMask);
     }
 }
 
@@ -408,8 +405,8 @@
                 float vlf = float_from_gain(gain_minifloat_unpack_left(vlr));
                 float vrf = float_from_gain(gain_minifloat_unpack_right(vlr));
 
-                mMixer->setParameter(name, AudioMixer::VOLUME, AudioMixer::VOLUME0, &vlf);
-                mMixer->setParameter(name, AudioMixer::VOLUME, AudioMixer::VOLUME1, &vrf);
+                mMixer->setParameter(name, AudioMixer::RAMP_VOLUME, AudioMixer::VOLUME0, &vlf);
+                mMixer->setParameter(name, AudioMixer::RAMP_VOLUME, AudioMixer::VOLUME1, &vrf);
             }
             // FIXME The current implementation of framesReady() for fast tracks
             // takes a tryLock, which can block
diff --git a/services/audioflinger/FastMixer.h b/services/audioflinger/FastMixer.h
index c31d476..97ab635 100644
--- a/services/audioflinger/FastMixer.h
+++ b/services/audioflinger/FastMixer.h
@@ -59,6 +59,14 @@
     virtual void onStateChange();
     virtual void onWork();
 
+    enum Reason {
+        REASON_REMOVE,
+        REASON_ADD,
+        REASON_MODIFY,
+    };
+    // called when a fast track of index has been removed, added, or modified
+    void updateMixerTrack(int index, Reason reason);
+
     // FIXME these former local variables need comments
     static const FastMixerState sInitial;
 
diff --git a/services/audioflinger/PlaybackTracks.h b/services/audioflinger/PlaybackTracks.h
index bad3ca8..3f62bc3 100644
--- a/services/audioflinger/PlaybackTracks.h
+++ b/services/audioflinger/PlaybackTracks.h
@@ -43,9 +43,8 @@
 
             void        appendDumpHeader(String8& result);
             void        appendDump(String8& result, bool active);
-    virtual status_t    start(AudioSystem::sync_event_t event =
-                                    AudioSystem::SYNC_EVENT_NONE,
-                             audio_session_t triggerSession = AUDIO_SESSION_NONE);
+    virtual status_t    start(AudioSystem::sync_event_t event = AudioSystem::SYNC_EVENT_NONE,
+                              audio_session_t triggerSession = AUDIO_SESSION_NONE);
     virtual void        stop();
             void        pause();
 
@@ -129,6 +128,8 @@
             }
             sp<os::ExternalVibration> getExternalVibration() const { return mExternalVibration; }
 
+            void    setTeePatches(TeePatches teePatches);
+
 protected:
     // for numerous
     friend class PlaybackThread;
@@ -139,8 +140,8 @@
     DISALLOW_COPY_AND_ASSIGN(Track);
 
     // AudioBufferProvider interface
-    virtual status_t getNextBuffer(AudioBufferProvider::Buffer* buffer);
-    // releaseBuffer() not overridden
+    status_t getNextBuffer(AudioBufferProvider::Buffer* buffer) override;
+    void releaseBuffer(AudioBufferProvider::Buffer* buffer) override;
 
     // ExtendedAudioBufferProvider interface
     virtual size_t framesReady() const;
@@ -220,6 +221,12 @@
     sp<os::ExternalVibration>    mExternalVibration;
 
 private:
+    void                interceptBuffer(const AudioBufferProvider::Buffer& buffer);
+    template <class F>
+    void                forEachTeePatchTrack(F f) {
+        for (auto& tp : mTeePatches) { f(tp.patchTrack); }
+    };
+
     // The following fields are only for fast tracks, and should be in a subclass
     int                 mFastIndex; // index within FastMixerState::mFastTracks[];
                                     // either mFastIndex == -1 if not isFastTrack()
@@ -239,6 +246,7 @@
     audio_output_flags_t mFlags;
     // If the last track change was notified to the client with readAndClearHasChanged
     std::atomic_flag     mChangeNotified = ATOMIC_FLAG_INIT;
+    TeePatches  mTeePatches;
 };  // end of Track
 
 
@@ -318,7 +326,7 @@
 };  // end of OutputTrack
 
 // playback track, used by PatchPanel
-class PatchTrack : public Track, public PatchProxyBufferProvider {
+class PatchTrack : public Track, public PatchTrackBase {
 public:
 
                         PatchTrack(PlaybackThread *playbackThread,
@@ -329,7 +337,8 @@
                                    size_t frameCount,
                                    void *buffer,
                                    size_t bufferSize,
-                                   audio_output_flags_t flags);
+                                   audio_output_flags_t flags,
+                                   const Timeout& timeout = {});
     virtual             ~PatchTrack();
 
     virtual status_t    start(AudioSystem::sync_event_t event =
@@ -345,12 +354,7 @@
                                      const struct timespec *timeOut = NULL);
     virtual void        releaseBuffer(Proxy::Buffer* buffer);
 
-            void setPeerProxy(PatchProxyBufferProvider *proxy) { mPeerProxy = proxy; }
-
 private:
             void restartIfDisabled();
 
-    sp<ClientProxy>             mProxy;
-    PatchProxyBufferProvider*   mPeerProxy;
-    struct timespec             mPeerTimeout;
 };  // end of PatchTrack
diff --git a/services/audioflinger/RecordTracks.h b/services/audioflinger/RecordTracks.h
index 32af7d5..ab4af33 100644
--- a/services/audioflinger/RecordTracks.h
+++ b/services/audioflinger/RecordTracks.h
@@ -113,7 +113,7 @@
 };
 
 // playback track, used by PatchPanel
-class PatchRecord : virtual public RecordTrack, public PatchProxyBufferProvider {
+class PatchRecord : public RecordTrack, public PatchTrackBase {
 public:
 
     PatchRecord(RecordThread *recordThread,
@@ -123,7 +123,8 @@
                 size_t frameCount,
                 void *buffer,
                 size_t bufferSize,
-                audio_input_flags_t flags);
+                audio_input_flags_t flags,
+                const Timeout& timeout = {});
     virtual             ~PatchRecord();
 
     // AudioBufferProvider interface
@@ -134,11 +135,4 @@
     virtual status_t    obtainBuffer(Proxy::Buffer *buffer,
                                      const struct timespec *timeOut = NULL);
     virtual void        releaseBuffer(Proxy::Buffer *buffer);
-
-    void setPeerProxy(PatchProxyBufferProvider *proxy) { mPeerProxy = proxy; }
-
-private:
-    sp<ClientProxy>             mProxy;
-    PatchProxyBufferProvider*   mPeerProxy;
-    struct timespec             mPeerTimeout;
 };  // end of PatchRecord
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index 5a70864..dd1eabf 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -8464,6 +8464,7 @@
         audio_output_flags_t flags =
                 (audio_output_flags_t)(AUDIO_OUTPUT_FLAG_MMAP_NOIRQ | AUDIO_OUTPUT_FLAG_DIRECT);
         audio_port_handle_t deviceId = mDeviceId;
+        std::vector<audio_io_handle_t> secondaryOutputs;
         ret = AudioSystem::getOutputForAttr(&mAttr, &io,
                                             mSessionId,
                                             &stream,
@@ -8472,7 +8473,10 @@
                                             &config,
                                             flags,
                                             &deviceId,
-                                            &portId);
+                                            &portId,
+                                            &secondaryOutputs);
+        ALOGD_IF(!secondaryOutputs.empty(),
+                 "MmapThread::start does not support secondary outputs, ignoring them");
     } else {
         audio_config_base_t config;
         config.sample_rate = mSampleRate;
diff --git a/services/audioflinger/TrackBase.h b/services/audioflinger/TrackBase.h
index c94639b..0ba0ab4 100644
--- a/services/audioflinger/TrackBase.h
+++ b/services/audioflinger/TrackBase.h
@@ -329,3 +329,19 @@
                                      const struct timespec *requested = NULL) = 0;
     virtual void        releaseBuffer(Proxy::Buffer* buffer) = 0;
 };
+
+class PatchTrackBase : public PatchProxyBufferProvider
+{
+public:
+    using Timeout = std::optional<std::chrono::nanoseconds>;
+                        PatchTrackBase(sp<ClientProxy> proxy, const ThreadBase& thread,
+                                       const Timeout& timeout);
+            void        setPeerTimeout(std::chrono::nanoseconds timeout);
+            void        setPeerProxy(PatchProxyBufferProvider *proxy) { mPeerProxy = proxy; }
+
+protected:
+    const sp<ClientProxy>       mProxy;
+    PatchProxyBufferProvider*   mPeerProxy = nullptr;
+    struct timespec             mPeerTimeout{};
+
+};
diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp
index e4af656..57dd568 100644
--- a/services/audioflinger/Tracks.cpp
+++ b/services/audioflinger/Tracks.cpp
@@ -99,7 +99,7 @@
         mId(android_atomic_inc(&nextTrackId)),
         mTerminated(false),
         mType(type),
-        mThreadIoHandle(thread->id()),
+        mThreadIoHandle(thread ? thread->id() : AUDIO_IO_HANDLE_NONE),
         mPortId(portId),
         mIsInvalid(false)
 {
@@ -277,6 +277,27 @@
     return NO_ERROR;
 }
 
+AudioFlinger::ThreadBase::PatchTrackBase::PatchTrackBase(sp<ClientProxy> proxy,
+                                                         const ThreadBase& thread,
+                                                         const Timeout& timeout)
+    : mProxy(proxy)
+{
+    if (timeout) {
+        setPeerTimeout(*timeout);
+    } else {
+        // Double buffer mixer
+        uint64_t mixBufferNs = ((uint64_t)2 * thread.frameCount() * 1000000000) /
+                                              thread.sampleRate();
+        setPeerTimeout(std::chrono::nanoseconds{mixBufferNs});
+    }
+}
+
+void AudioFlinger::ThreadBase::PatchTrackBase::setPeerTimeout(std::chrono::nanoseconds timeout) {
+    mPeerTimeout.tv_sec = timeout.count() / std::nano::den;
+    mPeerTimeout.tv_nsec = timeout.count() % std::nano::den;
+}
+
+
 // ----------------------------------------------------------------------------
 //      Playback
 // ----------------------------------------------------------------------------
@@ -504,6 +525,7 @@
             AudioSystem::releaseOutput(mPortId);
         }
     }
+    forEachTeePatchTrack([](auto patchTrack) { patchTrack->destroy(); });
 }
 
 void AudioFlinger::PlaybackThread::Track::appendDumpHeader(String8& result)
@@ -649,8 +671,7 @@
 }
 
 // AudioBufferProvider interface
-status_t AudioFlinger::PlaybackThread::Track::getNextBuffer(
-        AudioBufferProvider::Buffer* buffer)
+status_t AudioFlinger::PlaybackThread::Track::getNextBuffer(AudioBufferProvider::Buffer* buffer)
 {
     ServerProxy::Buffer buf;
     size_t desiredFrames = buffer->frameCount;
@@ -665,10 +686,39 @@
     } else {
         mAudioTrackServerProxy->tallyUnderrunFrames(0);
     }
-
     return status;
 }
 
+void AudioFlinger::PlaybackThread::Track::releaseBuffer(AudioBufferProvider::Buffer* buffer)
+{
+    interceptBuffer(*buffer);
+    TrackBase::releaseBuffer(buffer);
+}
+
+// TODO: compensate for time shift between HW modules.
+void AudioFlinger::PlaybackThread::Track::interceptBuffer(
+        const AudioBufferProvider::Buffer& buffer) {
+    for (auto& sink : mTeePatches) {
+        RecordThread::PatchRecord& patchRecord = *sink.patchRecord;
+        AudioBufferProvider::Buffer patchBuffer;
+        patchBuffer.frameCount = buffer.frameCount;
+        auto status = patchRecord.getNextBuffer(&patchBuffer);
+        if (status != NO_ERROR) {
+           ALOGW("%s PathRecord getNextBuffer failed with error %d: %s",
+                 __func__, status, strerror(-status));
+           continue;
+        }
+        // FIXME: On buffer wrap, the frame count will be less then requested,
+        //        retry to write the rest. (unlikely due to lcm buffer sizing)
+        ALOGW_IF(patchBuffer.frameCount != buffer.frameCount,
+                 "%s PatchRecord can not provide big enough buffer %zu/%zu, dropping %zu frames",
+                 __func__, patchBuffer.frameCount, buffer.frameCount,
+                 buffer.frameCount - patchBuffer.frameCount);
+        memcpy(patchBuffer.raw, buffer.raw, patchBuffer.frameCount * mFrameSize);
+        patchRecord.releaseBuffer(&patchBuffer);
+    }
+}
+
 // releaseBuffer() is not overridden
 
 // ExtendedAudioBufferProvider interface
@@ -816,6 +866,9 @@
     } else {
         status = BAD_VALUE;
     }
+    if (status == NO_ERROR) {
+        forEachTeePatchTrack([](auto patchTrack) { patchTrack->start(); });
+    }
     return status;
 }
 
@@ -849,6 +902,7 @@
                     __func__, mId, (int)mThreadIoHandle);
         }
     }
+    forEachTeePatchTrack([](auto patchTrack) { patchTrack->stop(); });
 }
 
 void AudioFlinger::PlaybackThread::Track::pause()
@@ -881,6 +935,8 @@
             break;
         }
     }
+    // Pausing the TeePatch to avoid a glitch on underrun, at the cost of buffered audio loss.
+    forEachTeePatchTrack([](auto patchTrack) { patchTrack->pause(); });
 }
 
 void AudioFlinger::PlaybackThread::Track::flush()
@@ -942,6 +998,8 @@
         // because the hardware buffer could hold a large amount of audio
         playbackThread->broadcast_l();
     }
+    // Flush the Tee to avoid on resume playing old data and glitching on the transition to new data
+    forEachTeePatchTrack([](auto patchTrack) { patchTrack->flush(); });
 }
 
 // must be called with thread lock held
@@ -1060,6 +1118,11 @@
     };
 }
 
+void AudioFlinger::PlaybackThread::Track::setTeePatches(TeePatches teePatches) {
+    forEachTeePatchTrack([](auto patchTrack) { patchTrack->destroy(); });
+    mTeePatches = std::move(teePatches);
+}
+
 status_t AudioFlinger::PlaybackThread::Track::getTimestamp(AudioTimestamp& timestamp)
 {
     if (!isOffloaded() && !isDirect()) {
@@ -1615,19 +1678,16 @@
                                                      size_t frameCount,
                                                      void *buffer,
                                                      size_t bufferSize,
-                                                     audio_output_flags_t flags)
+                                                     audio_output_flags_t flags,
+                                                     const Timeout& timeout)
     :   Track(playbackThread, NULL, streamType,
               audio_attributes_t{} /* currently unused for patch track */,
               sampleRate, format, channelMask, frameCount,
               buffer, bufferSize, nullptr /* sharedBuffer */,
               AUDIO_SESSION_NONE, AID_AUDIOSERVER, flags, TYPE_PATCH),
-              mProxy(new ClientProxy(mCblk, mBuffer, frameCount, mFrameSize, true, true))
+        PatchTrackBase(new ClientProxy(mCblk, mBuffer, frameCount, mFrameSize, true, true),
+                       *playbackThread, timeout)
 {
-    uint64_t mixBufferNs = ((uint64_t)2 * playbackThread->frameCount() * 1000000000) /
-                                                                    playbackThread->sampleRate();
-    mPeerTimeout.tv_sec = mixBufferNs / 1000000000;
-    mPeerTimeout.tv_nsec = (int) (mixBufferNs % 1000000000);
-
     ALOGV("%s(%d): sampleRate %d mPeerTimeout %d.%03d sec",
                                       __func__, mId, sampleRate,
                                       (int)mPeerTimeout.tv_sec,
@@ -2088,19 +2148,16 @@
                                                      size_t frameCount,
                                                      void *buffer,
                                                      size_t bufferSize,
-                                                     audio_input_flags_t flags)
+                                                     audio_input_flags_t flags,
+                                                     const Timeout& timeout)
     :   RecordTrack(recordThread, NULL,
                 audio_attributes_t{} /* currently unused for patch track */,
                 sampleRate, format, channelMask, frameCount,
                 buffer, bufferSize, AUDIO_SESSION_NONE, AID_AUDIOSERVER,
                 flags, TYPE_PATCH),
-                mProxy(new ClientProxy(mCblk, mBuffer, frameCount, mFrameSize, false, true))
+        PatchTrackBase(new ClientProxy(mCblk, mBuffer, frameCount, mFrameSize, false, true),
+                       *recordThread, timeout)
 {
-    uint64_t mixBufferNs = ((uint64_t)2 * recordThread->frameCount() * 1000000000) /
-                                                                recordThread->sampleRate();
-    mPeerTimeout.tv_sec = mixBufferNs / 1000000000;
-    mPeerTimeout.tv_nsec = (int) (mixBufferNs % 1000000000);
-
     ALOGV("%s(%d): sampleRate %d mPeerTimeout %d.%03d sec",
                                       __func__, mId, sampleRate,
                                       (int)mPeerTimeout.tv_sec,
diff --git a/services/audiopolicy/AudioPolicyInterface.h b/services/audiopolicy/AudioPolicyInterface.h
index d7030f9..bb5441d 100644
--- a/services/audiopolicy/AudioPolicyInterface.h
+++ b/services/audiopolicy/AudioPolicyInterface.h
@@ -110,7 +110,8 @@
                                         const audio_config_t *config,
                                         audio_output_flags_t *flags,
                                         audio_port_handle_t *selectedDeviceId,
-                                        audio_port_handle_t *portId) = 0;
+                                        audio_port_handle_t *portId,
+                                        std::vector<audio_io_handle_t> *secondaryOutputs) = 0;
     // indicates to the audio policy manager that the output starts being used by corresponding stream.
     virtual status_t startOutput(audio_port_handle_t portId) = 0;
     // indicates to the audio policy manager that the output stops being used by corresponding stream.
diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h b/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h
index 7296c95..1abce6f 100644
--- a/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h
+++ b/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h
@@ -68,13 +68,12 @@
      * Try to find an output descriptor for the given attributes.
      *
      * @param[in] attributes to consider fowr the research of output descriptor.
-     * @param[out] desc to return if an output could be found.
-     *
-     * @return NO_ERROR if an output was found for the given attribute (in this case, the
-     *                  descriptor output param is initialized), error code otherwise.
+     * @param[out] desc to return if an primary output could be found.
+     * @param[out] secondaryDesc other desc that the audio should be routed to.
      */
     status_t getOutputForAttr(audio_attributes_t attributes, uid_t uid,
-            sp<SwAudioOutputDescriptor> &desc);
+                sp<SwAudioOutputDescriptor> &primaryDesc,
+                std::vector<sp<SwAudioOutputDescriptor>> *secondaryDescs);
 
     sp<DeviceDescriptor> getDeviceAndMixForInputSource(audio_source_t inputSource,
                                                        const DeviceVector &availableDeviceTypes,
@@ -99,6 +98,11 @@
     status_t getDevicesForUid(uid_t uid, Vector<AudioDeviceTypeAddr>& devices) const;
 
     void dump(String8 *dst) const;
+
+private:
+    enum class MixMatchStatus { MATCH, NO_MATCH, INVALID_MIX };
+    MixMatchStatus mixMatch(const AudioMix* mix, size_t mixIndex,
+                            audio_attributes_t attributes, uid_t uid);
 };
 
 } // namespace android
diff --git a/services/audiopolicy/common/managerdefinitions/include/ClientDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/ClientDescriptor.h
index 4c069e4..2e44a60 100644
--- a/services/audiopolicy/common/managerdefinitions/include/ClientDescriptor.h
+++ b/services/audiopolicy/common/managerdefinitions/include/ClientDescriptor.h
@@ -86,10 +86,12 @@
                           audio_attributes_t attributes, audio_config_base_t config,
                           audio_port_handle_t preferredDeviceId, audio_stream_type_t stream,
                           product_strategy_t strategy, audio_output_flags_t flags,
-                          bool isPreferredDeviceForExclusiveUse) :
+                          bool isPreferredDeviceForExclusiveUse,
+                          std::vector<wp<SwAudioOutputDescriptor>> secondaryOutputs) :
         ClientDescriptor(portId, uid, sessionId, attributes, config, preferredDeviceId,
                          isPreferredDeviceForExclusiveUse),
-        mStream(stream), mStrategy(strategy), mFlags(flags) {}
+        mStream(stream), mStrategy(strategy), mFlags(flags),
+        mSecondaryOutputs(std::move(secondaryOutputs)) {}
     ~TrackClientDescriptor() override = default;
 
     using ClientDescriptor::dump;
@@ -99,11 +101,15 @@
     audio_output_flags_t flags() const { return mFlags; }
     audio_stream_type_t stream() const { return mStream; }
     product_strategy_t strategy() const { return mStrategy; }
+    const std::vector<wp<SwAudioOutputDescriptor>>& getSecondaryOutputs() const {
+        return mSecondaryOutputs;
+    };
 
 private:
     const audio_stream_type_t mStream;
     const product_strategy_t mStrategy;
     const audio_output_flags_t mFlags;
+    const std::vector<wp<SwAudioOutputDescriptor>> mSecondaryOutputs;
 };
 
 class RecordClientDescriptor: public ClientDescriptor
diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp
index 2489e76..6b6d9d2 100644
--- a/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp
@@ -156,128 +156,163 @@
     }
 }
 
-status_t AudioPolicyMixCollection::getOutputForAttr(audio_attributes_t attributes, uid_t uid,
-                                                    sp<SwAudioOutputDescriptor> &desc)
+status_t AudioPolicyMixCollection::getOutputForAttr(
+        audio_attributes_t attributes, uid_t uid, sp<SwAudioOutputDescriptor> &primaryDesc,
+        std::vector<sp<SwAudioOutputDescriptor>> *secondaryDescs)
 {
     ALOGV("getOutputForAttr() querying %zu mixes:", size());
-    desc = 0;
+    primaryDesc = 0;
     for (size_t i = 0; i < size(); i++) {
         sp<AudioPolicyMix> policyMix = valueAt(i);
+        sp<SwAudioOutputDescriptor> policyDesc = policyMix->getOutput();
+        if (!policyDesc) {
+            ALOGV("%s: Skiping %zu: Mix has no output", __func__, i);
+            continue;
+        }
+
         AudioMix *mix = policyMix->getMix();
+        const bool primaryOutputMix = !is_mix_loopback_render(mix->mRouteFlags);
 
-        if (mix->mMixType == MIX_TYPE_PLAYERS) {
-            // TODO if adding more player rules (currently only 2), make rule handling "generic"
-            //      as there is no difference in the treatment of usage- or uid-based rules
-            bool hasUsageMatchRules = false;
-            bool hasUsageExcludeRules = false;
-            bool usageMatchFound = false;
-            bool usageExclusionFound = false;
+        if (primaryOutputMix && primaryDesc != 0) {
+            ALOGV("%s: Skiping %zu: Primary output already found", __func__, i);
+            continue; // Primary output already found
+        }
 
-            bool hasUidMatchRules = false;
-            bool hasUidExcludeRules = false;
-            bool uidMatchFound = false;
-            bool uidExclusionFound = false;
+        switch (mixMatch(mix, i, attributes, uid)) {
+            case MixMatchStatus::INVALID_MIX: return BAD_VALUE; // TODO: Do we really want to abort?
+            case MixMatchStatus::NO_MATCH:
+                ALOGV("%s: Mix %zu: does not match", __func__, i);
+                continue; // skip the mix
+            case MixMatchStatus::MATCH:;
+        }
 
-            bool hasAddrMatch = false;
-
-            // iterate over all mix criteria to list what rules this mix contains
-            for (size_t j = 0; j < mix->mCriteria.size(); j++) {
-                ALOGV(" getOutputForAttr: mix %zu: inspecting mix criteria %zu of %zu",
-                        i, j, mix->mCriteria.size());
-
-                // if there is an address match, prioritize that match
-                if (strncmp(attributes.tags, "addr=", strlen("addr=")) == 0 &&
-                        strncmp(attributes.tags + strlen("addr="),
-                                mix->mDeviceAddress.string(),
-                                AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - strlen("addr=") - 1) == 0) {
-                    hasAddrMatch = true;
-                    break;
-                }
-
-                switch (mix->mCriteria[j].mRule) {
-                case RULE_MATCH_ATTRIBUTE_USAGE:
-                    ALOGV("\tmix has RULE_MATCH_ATTRIBUTE_USAGE for usage %d",
-                                                mix->mCriteria[j].mValue.mUsage);
-                    hasUsageMatchRules = true;
-                    if (mix->mCriteria[j].mValue.mUsage == attributes.usage) {
-                        // found one match against all allowed usages
-                        usageMatchFound = true;
-                    }
-                    break;
-                case RULE_EXCLUDE_ATTRIBUTE_USAGE:
-                    ALOGV("\tmix has RULE_EXCLUDE_ATTRIBUTE_USAGE for usage %d",
-                            mix->mCriteria[j].mValue.mUsage);
-                    hasUsageExcludeRules = true;
-                    if (mix->mCriteria[j].mValue.mUsage == attributes.usage) {
-                        // found this usage is to be excluded
-                        usageExclusionFound = true;
-                    }
-                    break;
-                case RULE_MATCH_UID:
-                    ALOGV("\tmix has RULE_MATCH_UID for uid %d", mix->mCriteria[j].mValue.mUid);
-                    hasUidMatchRules = true;
-                    if (mix->mCriteria[j].mValue.mUid == uid) {
-                        // found one UID match against all allowed UIDs
-                        uidMatchFound = true;
-                    }
-                    break;
-                case RULE_EXCLUDE_UID:
-                    ALOGV("\tmix has RULE_EXCLUDE_UID for uid %d", mix->mCriteria[j].mValue.mUid);
-                    hasUidExcludeRules = true;
-                    if (mix->mCriteria[j].mValue.mUid == uid) {
-                        // found this UID is to be excluded
-                        uidExclusionFound = true;
-                    }
-                    break;
-                default:
-                    break;
-                }
-
-                // consistency checks: for each "dimension" of rules (usage, uid...), we can
-                // only have MATCH rules, or EXCLUDE rules in each dimension, not a combination
-                if (hasUsageMatchRules && hasUsageExcludeRules) {
-                    ALOGE("getOutputForAttr: invalid combination of RULE_MATCH_ATTRIBUTE_USAGE"
-                            " and RULE_EXCLUDE_ATTRIBUTE_USAGE in mix %zu", i);
-                    return BAD_VALUE;
-                }
-                if (hasUidMatchRules && hasUidExcludeRules) {
-                    ALOGE("getOutputForAttr: invalid combination of RULE_MATCH_UID"
-                            " and RULE_EXCLUDE_UID in mix %zu", i);
-                    return BAD_VALUE;
-                }
-
-                if ((hasUsageExcludeRules && usageExclusionFound)
-                        || (hasUidExcludeRules && uidExclusionFound)) {
-                    break; // stop iterating on criteria because an exclusion was found (will fail)
-                }
-
-            }//iterate on mix criteria
-
-            // determine if exiting on success (or implicit failure as desc is 0)
-            if (hasAddrMatch ||
-                    !((hasUsageExcludeRules && usageExclusionFound) ||
-                      (hasUsageMatchRules && !usageMatchFound)  ||
-                      (hasUidExcludeRules && uidExclusionFound) ||
-                      (hasUidMatchRules && !uidMatchFound))) {
-                ALOGV("\tgetOutputForAttr will use mix %zu", i);
-                desc = policyMix->getOutput();
+        policyDesc->mPolicyMix = mix;
+        if (primaryOutputMix) {
+            primaryDesc = policyDesc;
+            ALOGV("%s: Mix %zu: set primary desc", __func__, i);
+        } else {
+            if (policyDesc->mIoHandle == AUDIO_IO_HANDLE_NONE) {
+                ALOGV("%s: Mix %zu ignored as secondaryOutput because not opened yet", __func__, i);
+            } else {
+                ALOGV("%s: Add a secondary desc %zu", __func__, i);
+                secondaryDescs->push_back(policyDesc);
             }
+        }
+    }
+    return (primaryDesc == nullptr && secondaryDescs->empty()) ? BAD_VALUE : NO_ERROR;
+}
 
-        } else if (mix->mMixType == MIX_TYPE_RECORDERS) {
-            if (attributes.usage == AUDIO_USAGE_VIRTUAL_SOURCE &&
-                    strncmp(attributes.tags, "addr=", strlen("addr=")) == 0 &&
+AudioPolicyMixCollection::MixMatchStatus AudioPolicyMixCollection::mixMatch(
+        const AudioMix* mix, size_t mixIndex, audio_attributes_t attributes, uid_t uid) {
+
+    if (mix->mMixType == MIX_TYPE_PLAYERS) {
+        // TODO if adding more player rules (currently only 2), make rule handling "generic"
+        //      as there is no difference in the treatment of usage- or uid-based rules
+        bool hasUsageMatchRules = false;
+        bool hasUsageExcludeRules = false;
+        bool usageMatchFound = false;
+        bool usageExclusionFound = false;
+
+        bool hasUidMatchRules = false;
+        bool hasUidExcludeRules = false;
+        bool uidMatchFound = false;
+        bool uidExclusionFound = false;
+
+        bool hasAddrMatch = false;
+
+        // iterate over all mix criteria to list what rules this mix contains
+        for (size_t j = 0; j < mix->mCriteria.size(); j++) {
+            ALOGV(" getOutputForAttr: mix %zu: inspecting mix criteria %zu of %zu",
+                    mixIndex, j, mix->mCriteria.size());
+
+            // if there is an address match, prioritize that match
+            if (strncmp(attributes.tags, "addr=", strlen("addr=")) == 0 &&
                     strncmp(attributes.tags + strlen("addr="),
                             mix->mDeviceAddress.string(),
                             AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - strlen("addr=") - 1) == 0) {
-                desc = policyMix->getOutput();
+                hasAddrMatch = true;
+                break;
             }
+
+            switch (mix->mCriteria[j].mRule) {
+            case RULE_MATCH_ATTRIBUTE_USAGE:
+                ALOGV("\tmix has RULE_MATCH_ATTRIBUTE_USAGE for usage %d",
+                                            mix->mCriteria[j].mValue.mUsage);
+                hasUsageMatchRules = true;
+                if (mix->mCriteria[j].mValue.mUsage == attributes.usage) {
+                    // found one match against all allowed usages
+                    usageMatchFound = true;
+                }
+                break;
+            case RULE_EXCLUDE_ATTRIBUTE_USAGE:
+                ALOGV("\tmix has RULE_EXCLUDE_ATTRIBUTE_USAGE for usage %d",
+                        mix->mCriteria[j].mValue.mUsage);
+                hasUsageExcludeRules = true;
+                if (mix->mCriteria[j].mValue.mUsage == attributes.usage) {
+                    // found this usage is to be excluded
+                    usageExclusionFound = true;
+                }
+                break;
+            case RULE_MATCH_UID:
+                ALOGV("\tmix has RULE_MATCH_UID for uid %d", mix->mCriteria[j].mValue.mUid);
+                hasUidMatchRules = true;
+                if (mix->mCriteria[j].mValue.mUid == uid) {
+                    // found one UID match against all allowed UIDs
+                    uidMatchFound = true;
+                }
+                break;
+            case RULE_EXCLUDE_UID:
+                ALOGV("\tmix has RULE_EXCLUDE_UID for uid %d", mix->mCriteria[j].mValue.mUid);
+                hasUidExcludeRules = true;
+                if (mix->mCriteria[j].mValue.mUid == uid) {
+                    // found this UID is to be excluded
+                    uidExclusionFound = true;
+                }
+                break;
+            default:
+                break;
+            }
+
+            // consistency checks: for each "dimension" of rules (usage, uid...), we can
+            // only have MATCH rules, or EXCLUDE rules in each dimension, not a combination
+            if (hasUsageMatchRules && hasUsageExcludeRules) {
+                ALOGE("getOutputForAttr: invalid combination of RULE_MATCH_ATTRIBUTE_USAGE"
+                        " and RULE_EXCLUDE_ATTRIBUTE_USAGE in mix %zu", mixIndex);
+                return MixMatchStatus::INVALID_MIX;
+            }
+            if (hasUidMatchRules && hasUidExcludeRules) {
+                ALOGE("getOutputForAttr: invalid combination of RULE_MATCH_UID"
+                        " and RULE_EXCLUDE_UID in mix %zu", mixIndex);
+                return MixMatchStatus::INVALID_MIX;
+            }
+
+            if ((hasUsageExcludeRules && usageExclusionFound)
+                    || (hasUidExcludeRules && uidExclusionFound)) {
+                break; // stop iterating on criteria because an exclusion was found (will fail)
+            }
+
+        }//iterate on mix criteria
+
+        // determine if exiting on success (or implicit failure as desc is 0)
+        if (hasAddrMatch ||
+                !((hasUsageExcludeRules && usageExclusionFound) ||
+                  (hasUsageMatchRules && !usageMatchFound)  ||
+                  (hasUidExcludeRules && uidExclusionFound) ||
+                  (hasUidMatchRules && !uidMatchFound))) {
+            ALOGV("\tgetOutputForAttr will use mix %zu", mixIndex);
+            return MixMatchStatus::MATCH;
         }
-        if (desc != 0) {
-            desc->mPolicyMix = mix;
-            return NO_ERROR;
+
+    } else if (mix->mMixType == MIX_TYPE_RECORDERS) {
+        if (attributes.usage == AUDIO_USAGE_VIRTUAL_SOURCE &&
+                strncmp(attributes.tags, "addr=", strlen("addr=")) == 0 &&
+                strncmp(attributes.tags + strlen("addr="),
+                        mix->mDeviceAddress.string(),
+                        AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - strlen("addr=") - 1) == 0) {
+            return MixMatchStatus::MATCH;
         }
     }
-    return BAD_VALUE;
+    return MixMatchStatus::NO_MATCH;
 }
 
 sp<DeviceDescriptor> AudioPolicyMixCollection::getDeviceAndMixForOutput(
diff --git a/services/audiopolicy/common/managerdefinitions/src/ClientDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/ClientDescriptor.cpp
index a6f6c3b..633c40e 100644
--- a/services/audiopolicy/common/managerdefinitions/src/ClientDescriptor.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/ClientDescriptor.cpp
@@ -85,7 +85,8 @@
          product_strategy_t strategy) :
     TrackClientDescriptor::TrackClientDescriptor(portId, uid, AUDIO_SESSION_NONE, attributes,
         AUDIO_CONFIG_BASE_INITIALIZER, AUDIO_PORT_HANDLE_NONE,
-        stream, strategy, AUDIO_OUTPUT_FLAG_NONE, false),
+        stream, strategy, AUDIO_OUTPUT_FLAG_NONE, false,
+        {} /* Sources do not support secondary outputs*/),
         mPatchDesc(patchDesc), mSrcDevice(srcDevice)
 {
 }
diff --git a/services/audiopolicy/common/managerdefinitions/src/TypeConverter.cpp b/services/audiopolicy/common/managerdefinitions/src/TypeConverter.cpp
index 6f48eae..7c76d8a 100644
--- a/services/audiopolicy/common/managerdefinitions/src/TypeConverter.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/TypeConverter.cpp
@@ -45,6 +45,7 @@
 const RouteFlagTypeConverter::Table RouteFlagTypeConverter::mTable[] = {
     MAKE_STRING_FROM_ENUM(MIX_ROUTE_FLAG_RENDER),
     MAKE_STRING_FROM_ENUM(MIX_ROUTE_FLAG_LOOP_BACK),
+    MAKE_STRING_FROM_ENUM(MIX_ROUTE_FLAG_LOOP_BACK_AND_RENDER),
     MAKE_STRING_FROM_ENUM(MIX_ROUTE_FLAG_ALL),
     TERMINATOR
 };
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index 0eee3f2..ccec93f 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -33,9 +33,12 @@
 #define AUDIO_POLICY_XML_CONFIG_FILE_NAME "audio_policy_configuration.xml"
 #define AUDIO_POLICY_A2DP_OFFLOAD_DISABLED_XML_CONFIG_FILE_NAME \
         "audio_policy_configuration_a2dp_offload_disabled.xml"
+#define AUDIO_POLICY_BLUETOOTH_HAL_ENABLED_XML_CONFIG_FILE_NAME \
+        "audio_policy_configuration_bluetooth_hal_enabled.xml"
 
 #include <inttypes.h>
 #include <math.h>
+#include <set>
 #include <unordered_set>
 #include <vector>
 
@@ -907,16 +910,18 @@
     return NO_ERROR;
 }
 
-status_t AudioPolicyManager::getOutputForAttrInt(audio_attributes_t *resultAttr,
-                                                 audio_io_handle_t *output,
-                                                 audio_session_t session,
-                                                 const audio_attributes_t *attr,
-                                                 audio_stream_type_t *stream,
-                                                 uid_t uid,
-                                                 const audio_config_t *config,
-                                                 audio_output_flags_t *flags,
-                                                 audio_port_handle_t *selectedDeviceId,
-                                                 bool *isRequestedDeviceForExclusiveUse)
+status_t AudioPolicyManager::getOutputForAttrInt(
+        audio_attributes_t *resultAttr,
+        audio_io_handle_t *output,
+        audio_session_t session,
+        const audio_attributes_t *attr,
+        audio_stream_type_t *stream,
+        uid_t uid,
+        const audio_config_t *config,
+        audio_output_flags_t *flags,
+        audio_port_handle_t *selectedDeviceId,
+        bool *isRequestedDeviceForExclusiveUse,
+        std::vector<sp<SwAudioOutputDescriptor>> *secondaryDescs)
 {
     DeviceVector outputDevices;
     const audio_port_handle_t requestedPortId = *selectedDeviceId;
@@ -935,19 +940,26 @@
     ALOGV("%s() attributes=%s stream=%s session %d selectedDeviceId %d", __func__,
           toString(*resultAttr).c_str(), toString(*stream).c_str(), session, requestedPortId);
 
-    // 1/ First check for explicit routing (eg. setPreferredDevice): NOTE: now handled by engine
-    // 2/ If no explict route, is there a matching dynamic policy that applies?
-    //    NOTE: new engine product strategy does not make use of dynamic routing, keep it for
-    //          remote-submix and legacy
-    sp<SwAudioOutputDescriptor> desc;
-    if (requestedDevice == nullptr &&
-            mPolicyMixes.getOutputForAttr(*resultAttr, uid, desc) == NO_ERROR) {
-        ALOG_ASSERT(desc != 0, "Invalid desc returned by getOutputForAttr");
-        if (!audio_has_proportional_frames(config->format)) {
-            return BAD_VALUE;
-        }
-        *output = desc->mIoHandle;
-        AudioMix *mix = desc->mPolicyMix;
+    // The primary output is the explicit routing (eg. setPreferredDevice) if specified,
+    //       otherwise, fallback to the dynamic policies, if none match, query the engine.
+    // Secondary outputs are always found by dynamic policies as the engine do not support them
+    sp<SwAudioOutputDescriptor> policyDesc;
+    if (mPolicyMixes.getOutputForAttr(*resultAttr, uid, policyDesc, secondaryDescs) != NO_ERROR) {
+        policyDesc = nullptr; // reset getOutputForAttr in case of failure
+        secondaryDescs->clear();
+    }
+    // Explicit routing is higher priority then any dynamic policy primary output
+    bool usePrimaryOutputFromPolicyMixes = requestedDevice == nullptr && policyDesc != nullptr;
+
+    // FIXME: in case of RENDER policy, the output capabilities should be checked
+    if ((usePrimaryOutputFromPolicyMixes || !secondaryDescs->empty())
+        && !audio_has_proportional_frames(config->format)) {
+        ALOGW("%s: audio loopback only supports proportional frames", __func__);
+        return BAD_VALUE;
+    }
+    if (usePrimaryOutputFromPolicyMixes) {
+        *output = policyDesc->mIoHandle;
+        AudioMix *mix = policyDesc->mPolicyMix;
         sp<DeviceDescriptor> deviceDesc =
                 mAvailableOutputDevices.getDevice(mix->mDeviceType,
                                                   mix->mDeviceAddress,
@@ -1022,7 +1034,8 @@
                                               const audio_config_t *config,
                                               audio_output_flags_t *flags,
                                               audio_port_handle_t *selectedDeviceId,
-                                              audio_port_handle_t *portId)
+                                              audio_port_handle_t *portId,
+                                              std::vector<audio_io_handle_t> *secondaryOutputs)
 {
     // The supplied portId must be AUDIO_PORT_HANDLE_NONE
     if (*portId != AUDIO_PORT_HANDLE_NONE) {
@@ -1031,11 +1044,18 @@
     const audio_port_handle_t requestedPortId = *selectedDeviceId;
     audio_attributes_t resultAttr;
     bool isRequestedDeviceForExclusiveUse = false;
+    std::vector<sp<SwAudioOutputDescriptor>> secondaryOutputDescs;
     status_t status = getOutputForAttrInt(&resultAttr, output, session, attr, stream, uid,
-            config, flags, selectedDeviceId, &isRequestedDeviceForExclusiveUse);
+            config, flags, selectedDeviceId, &isRequestedDeviceForExclusiveUse,
+            &secondaryOutputDescs);
     if (status != NO_ERROR) {
         return status;
     }
+    std::vector<wp<SwAudioOutputDescriptor>> weakSecondaryOutputDescs;
+    for (auto& secondaryDesc : secondaryOutputDescs) {
+        secondaryOutputs->push_back(secondaryDesc->mIoHandle);
+        weakSecondaryOutputDescs.push_back(secondaryDesc);
+    }
 
     audio_config_base_t clientConfig = {.sample_rate = config->sample_rate,
         .format = config->format,
@@ -1046,7 +1066,8 @@
         new TrackClientDescriptor(*portId, uid, session, resultAttr, clientConfig,
                                   requestedPortId, *stream,
                                   mEngine->getProductStrategyForAttributes(resultAttr),
-                                  *flags, isRequestedDeviceForExclusiveUse);
+                                  *flags, isRequestedDeviceForExclusiveUse,
+                                  std::move(weakSecondaryOutputDescs));
     sp<SwAudioOutputDescriptor> outputDesc = mOutputs.valueFor(*output);
     outputDesc->addClient(clientDesc);
 
@@ -1562,13 +1583,15 @@
         policyMix = outputDesc->mPolicyMix;
         audio_devices_t newDeviceType;
         address = policyMix->mDeviceAddress.string();
-        if ((policyMix->mRouteFlags & MIX_ROUTE_FLAG_RENDER) == MIX_ROUTE_FLAG_RENDER) {
-            newDeviceType = policyMix->mDeviceType;
-        } else {
+        if ((policyMix->mRouteFlags & MIX_ROUTE_FLAG_LOOP_BACK) == MIX_ROUTE_FLAG_LOOP_BACK) {
             newDeviceType = AUDIO_DEVICE_OUT_REMOTE_SUBMIX;
+        } else {
+            newDeviceType = policyMix->mDeviceType;
         }
-        devices.add(mAvailableOutputDevices.getDevice(newDeviceType,
-                                                      String8(address), AUDIO_FORMAT_DEFAULT));
+        sp device = mAvailableOutputDevices.getDevice(newDeviceType, String8(address),
+                                                        AUDIO_FORMAT_DEFAULT);
+        ALOG_ASSERT(device, "%s: no device found t=%u, a=%s", __func__, newDeviceType, address);
+        devices.add(device);
     }
 
     // requiresMuteCheck is false when we can bypass mute strategy.
@@ -2609,18 +2632,24 @@
     // examine each mix's route type
     for (size_t i = 0; i < mixes.size(); i++) {
         AudioMix mix = mixes[i];
-        // we only support MIX_ROUTE_FLAG_LOOP_BACK or MIX_ROUTE_FLAG_RENDER, not the combination
-        if ((mix.mRouteFlags & MIX_ROUTE_FLAG_ALL) == MIX_ROUTE_FLAG_ALL) {
+        // Only capture of playback is allowed in LOOP_BACK & RENDER mode
+        if (is_mix_loopback_render(mix.mRouteFlags) && mix.mMixType != MIX_TYPE_PLAYERS) {
+            ALOGE("Unsupported Policy Mix %zu of %zu: "
+                  "Only capture of playback is allowed in LOOP_BACK & RENDER mode",
+                   i, mixes.size());
             res = INVALID_OPERATION;
             break;
         }
+        // LOOP_BACK and LOOP_BACK | RENDER have the same remote submix backend and are handled
+        // in the same way.
         if ((mix.mRouteFlags & MIX_ROUTE_FLAG_LOOP_BACK) == MIX_ROUTE_FLAG_LOOP_BACK) {
-            ALOGV("registerPolicyMixes() mix %zu of %zu is LOOP_BACK", i, mixes.size());
+            ALOGV("registerPolicyMixes() mix %zu of %zu is LOOP_BACK %d", i, mixes.size(),
+                  mix.mRouteFlags);
             if (rSubmixModule == 0) {
                 rSubmixModule = mHwModules.getModuleFromName(
                         AUDIO_HARDWARE_MODULE_ID_REMOTE_SUBMIX);
                 if (rSubmixModule == 0) {
-                    ALOGE(" Unable to find audio module for submix, aborting mix %zu registration",
+                    ALOGE("Unable to find audio module for submix, aborting mix %zu registration",
                             i);
                     res = INVALID_OPERATION;
                     break;
@@ -2635,7 +2664,7 @@
             }
 
             if (mPolicyMixes.registerMix(address, mix, 0 /*output desc*/) != NO_ERROR) {
-                ALOGE(" Error registering mix %zu for address %s", i, address.string());
+                ALOGE("Error registering mix %zu for address %s", i, address.string());
                 res = INVALID_OPERATION;
                 break;
             }
@@ -2679,6 +2708,8 @@
 
                 if (desc->supportedDevices().contains(device)) {
                     if (mPolicyMixes.registerMix(address, mix, desc) != NO_ERROR) {
+                        ALOGE("Could not register mix RENDER,  dev=0x%X addr=%s", type,
+                              address.string());
                         res = INVALID_OPERATION;
                     } else {
                         foundOutput = true;
@@ -2746,7 +2777,7 @@
             rSubmixModule->removeOutputProfile(address);
             rSubmixModule->removeInputProfile(address);
 
-        } if ((mix.mRouteFlags & MIX_ROUTE_FLAG_RENDER) == MIX_ROUTE_FLAG_RENDER) {
+        } else if ((mix.mRouteFlags & MIX_ROUTE_FLAG_RENDER) == MIX_ROUTE_FLAG_RENDER) {
             if (mPolicyMixes.unregisterMix(mix.mDeviceAddress) != NO_ERROR) {
                 res = INVALID_OPERATION;
                 continue;
@@ -3635,9 +3666,11 @@
         audio_output_flags_t flags = AUDIO_OUTPUT_FLAG_NONE;
         audio_port_handle_t selectedDeviceId = AUDIO_PORT_HANDLE_NONE;
         bool isRequestedDeviceForExclusiveUse = false;
+        std::vector<sp<SwAudioOutputDescriptor>> secondaryOutputs;
         getOutputForAttrInt(&resultAttr, &output, AUDIO_SESSION_NONE,
                 &attributes, &stream, sourceDesc->uid(), &config, &flags,
-                &selectedDeviceId, &isRequestedDeviceForExclusiveUse);
+                &selectedDeviceId, &isRequestedDeviceForExclusiveUse,
+                &secondaryOutputs);
         if (output == AUDIO_IO_HANDLE_NONE) {
             ALOGV("%s no output for device %08x", __FUNCTION__, sinkDevices.types());
             return INVALID_OPERATION;
@@ -4004,7 +4037,11 @@
     if (property_get_bool("ro.bluetooth.a2dp_offload.supported", false) &&
         property_get_bool("persist.bluetooth.a2dp_offload.disabled", false)) {
         // A2DP offload supported but disabled: try to use special XML file
-        fileNames.push_back(AUDIO_POLICY_A2DP_OFFLOAD_DISABLED_XML_CONFIG_FILE_NAME);
+        if (property_get_bool("persist.bluetooth.bluetooth_audio_hal.enabled", false)) {
+            fileNames.push_back(AUDIO_POLICY_BLUETOOTH_HAL_ENABLED_XML_CONFIG_FILE_NAME);
+        } else {
+            fileNames.push_back(AUDIO_POLICY_A2DP_OFFLOAD_DISABLED_XML_CONFIG_FILE_NAME);
+        }
     }
     fileNames.push_back(AUDIO_POLICY_XML_CONFIG_FILE_NAME);
 
@@ -4782,6 +4819,7 @@
     // output is suspended before any tracks are moved to it
     checkA2dpSuspend();
     checkOutputForAllStrategies();
+    checkSecondaryOutputs();
     if (onOutputsChecked != nullptr && onOutputsChecked()) checkA2dpSuspend();
     updateDevicesAndOutputs();
     if (mHwModules.getModuleFromName(AUDIO_HARDWARE_MODULE_ID_MSD) != 0) {
@@ -4870,6 +4908,29 @@
     }
 }
 
+void AudioPolicyManager::checkSecondaryOutputs() {
+    std::set<audio_stream_type_t> streamsToInvalidate;
+    for (size_t i = 0; i < mOutputs.size(); i++) {
+        const sp<SwAudioOutputDescriptor>& outputDescriptor = mOutputs[i];
+        for (const sp<TrackClientDescriptor>& client : outputDescriptor->getClientIterable()) {
+            // FIXME code duplicated from getOutputForAttrInt
+            sp<SwAudioOutputDescriptor> desc;
+            std::vector<sp<SwAudioOutputDescriptor>> secondaryDescs;
+            mPolicyMixes.getOutputForAttr(client->attributes(), client->uid(), desc,
+                                          &secondaryDescs);
+            if (!std::equal(client->getSecondaryOutputs().begin(),
+                            client->getSecondaryOutputs().end(),
+                            secondaryDescs.begin(), secondaryDescs.end())) {
+                streamsToInvalidate.insert(client->stream());
+            }
+        }
+    }
+    for (audio_stream_type_t stream : streamsToInvalidate) {
+        ALOGD("%s Invalidate stream %d due to secondary output change", __func__, stream);
+        mpClientInterface->invalidateStream(stream);
+    }
+}
+
 void AudioPolicyManager::checkA2dpSuspend()
 {
     audio_io_handle_t a2dpOutput = mOutputs.getA2dpOutput();
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.h b/services/audiopolicy/managerdefault/AudioPolicyManager.h
index 73c3b56..70ad6ac 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.h
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.h
@@ -113,15 +113,16 @@
         virtual void setSystemProperty(const char* property, const char* value);
         virtual status_t initCheck();
         virtual audio_io_handle_t getOutput(audio_stream_type_t stream);
-        virtual status_t getOutputForAttr(const audio_attributes_t *attr,
-                                          audio_io_handle_t *output,
-                                          audio_session_t session,
-                                          audio_stream_type_t *stream,
-                                          uid_t uid,
-                                          const audio_config_t *config,
-                                          audio_output_flags_t *flags,
-                                          audio_port_handle_t *selectedDeviceId,
-                                          audio_port_handle_t *portId);
+        status_t getOutputForAttr(const audio_attributes_t *attr,
+                                  audio_io_handle_t *output,
+                                  audio_session_t session,
+                                  audio_stream_type_t *stream,
+                                  uid_t uid,
+                                  const audio_config_t *config,
+                                  audio_output_flags_t *flags,
+                                  audio_port_handle_t *selectedDeviceId,
+                                  audio_port_handle_t *portId,
+                                  std::vector<audio_io_handle_t> *secondaryOutputs) override;
         virtual status_t startOutput(audio_port_handle_t portId);
         virtual status_t stopOutput(audio_port_handle_t portId);
         virtual void releaseOutput(audio_port_handle_t portId);
@@ -431,6 +432,10 @@
          */
         void checkOutputForAllStrategies();
 
+        // Same as checkOutputForStrategy but for secondary outputs. Make sure if a secondary
+        // output condition changes, the track is properly rerouted
+        void checkSecondaryOutputs();
+
         // manages A2DP output suspend/restore according to phone state and BT SCO usage
         void checkA2dpSuspend();
 
@@ -711,7 +716,8 @@
                 const audio_config_t *config,
                 audio_output_flags_t *flags,
                 audio_port_handle_t *selectedDeviceId,
-                bool *isRequestedDeviceForExclusiveUse);
+                bool *isRequestedDeviceForExclusiveUse,
+                std::vector<sp<SwAudioOutputDescriptor>> *secondaryDescs);
         // internal method to return the output handle for the given device and format
         audio_io_handle_t getOutputForDevices(
                 const DeviceVector &devices,
diff --git a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
index 7768ea3..8ddf824 100644
--- a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
+++ b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
@@ -175,7 +175,8 @@
                                               const audio_config_t *config,
                                               audio_output_flags_t flags,
                                               audio_port_handle_t *selectedDeviceId,
-                                              audio_port_handle_t *portId)
+                                              audio_port_handle_t *portId,
+                                              std::vector<audio_io_handle_t> *secondaryOutputs)
 {
     if (mAudioPolicyManager == NULL) {
         return NO_INIT;
@@ -193,7 +194,8 @@
     AutoCallerClear acc;
     status_t result = mAudioPolicyManager->getOutputForAttr(attr, output, session, stream, uid,
                                                  config,
-                                                 &flags, selectedDeviceId, portId);
+                                                 &flags, selectedDeviceId, portId,
+                                                 secondaryOutputs);
 
     // FIXME: Introduce a way to check for the the telephony device before opening the output
     if ((result == NO_ERROR) &&
@@ -205,9 +207,10 @@
         flags = originalFlags;
         *selectedDeviceId = AUDIO_PORT_HANDLE_NONE;
         *portId = AUDIO_PORT_HANDLE_NONE;
-        result = mAudioPolicyManager->getOutputForAttr(attr, output, session, stream, uid,
-                                                 config,
-                                                 &flags, selectedDeviceId, portId);
+        secondaryOutputs->clear();
+        result = mAudioPolicyManager->getOutputForAttr(attr, output, session, stream, uid, config,
+                                                       &flags, selectedDeviceId, portId,
+                                                       secondaryOutputs);
     }
 
     if (result == NO_ERROR) {
diff --git a/services/audiopolicy/service/AudioPolicyService.h b/services/audiopolicy/service/AudioPolicyService.h
index ee293a7..8cd6e81 100644
--- a/services/audiopolicy/service/AudioPolicyService.h
+++ b/services/audiopolicy/service/AudioPolicyService.h
@@ -74,16 +74,17 @@
     virtual status_t setForceUse(audio_policy_force_use_t usage, audio_policy_forced_cfg_t config);
     virtual audio_policy_forced_cfg_t getForceUse(audio_policy_force_use_t usage);
     virtual audio_io_handle_t getOutput(audio_stream_type_t stream);
-    virtual status_t getOutputForAttr(const audio_attributes_t *attr,
-                                      audio_io_handle_t *output,
-                                      audio_session_t session,
-                                      audio_stream_type_t *stream,
-                                      pid_t pid,
-                                      uid_t uid,
-                                      const audio_config_t *config,
-                                      audio_output_flags_t flags,
-                                      audio_port_handle_t *selectedDeviceId,
-                                      audio_port_handle_t *portId);
+    status_t getOutputForAttr(const audio_attributes_t *attr,
+                              audio_io_handle_t *output,
+                              audio_session_t session,
+                              audio_stream_type_t *stream,
+                              pid_t pid,
+                              uid_t uid,
+                              const audio_config_t *config,
+                              audio_output_flags_t flags,
+                              audio_port_handle_t *selectedDeviceId,
+                              audio_port_handle_t *portId,
+                              std::vector<audio_io_handle_t> *secondaryOutputs) override;
     virtual status_t startOutput(audio_port_handle_t portId);
     virtual status_t stopOutput(audio_port_handle_t portId);
     virtual void releaseOutput(audio_port_handle_t portId);
diff --git a/services/audiopolicy/tests/audiopolicymanager_tests.cpp b/services/audiopolicy/tests/audiopolicymanager_tests.cpp
index e9f4657..de5670c 100644
--- a/services/audiopolicy/tests/audiopolicymanager_tests.cpp
+++ b/services/audiopolicy/tests/audiopolicymanager_tests.cpp
@@ -214,7 +214,7 @@
     *portId = AUDIO_PORT_HANDLE_NONE;
     ASSERT_EQ(OK, mManager->getOutputForAttr(
                     &attr, &output, AUDIO_SESSION_NONE, &stream, 0 /*uid*/, &config, &flags,
-                    selectedDeviceId, portId));
+                    selectedDeviceId, portId, {}));
     ASSERT_NE(AUDIO_PORT_HANDLE_NONE, *portId);
 }
 
diff --git a/services/mediacodec/Android.mk b/services/mediacodec/Android.mk
index 6a71d7d..f78c671 100644
--- a/services/mediacodec/Android.mk
+++ b/services/mediacodec/Android.mk
@@ -108,6 +108,9 @@
     libutils \
     libziparchive \
 
+LOCAL_HEADER_LIBRARIES := \
+    libnativeloader-dummy-headers \
+
 LOCAL_MODULE := mediaswcodec
 LOCAL_INIT_RC := mediaswcodec.rc
 LOCAL_SANITIZE := scudo
diff --git a/services/mediacodec/MediaCodecUpdateService.cpp b/services/mediacodec/MediaCodecUpdateService.cpp
index 0e6892d..50ccbce 100644
--- a/services/mediacodec/MediaCodecUpdateService.cpp
+++ b/services/mediacodec/MediaCodecUpdateService.cpp
@@ -20,28 +20,12 @@
 #include <android/dlext.h>
 #include <dlfcn.h>
 #include <media/CodecServiceRegistrant.h>
+#include <nativeloader/dlext_namespaces.h>
 #include <utils/Log.h>
 #include <utils/String8.h>
 
 #include "MediaCodecUpdateService.h"
 
-// Copied from GraphicsEnv.cpp
-// TODO(b/37049319) Get this from a header once one exists
-extern "C" {
-  android_namespace_t* android_create_namespace(const char* name,
-                                                const char* ld_library_path,
-                                                const char* default_library_path,
-                                                uint64_t type,
-                                                const char* permitted_when_isolated_path,
-                                                android_namespace_t* parent);
-  bool android_link_namespaces(android_namespace_t* from,
-                               android_namespace_t* to,
-                               const char* shared_libs_sonames);
-  enum {
-     ANDROID_NAMESPACE_TYPE_ISOLATED = 1,
-  };
-}
-
 namespace android {
 
 void loadFromApex(const char *libDirPath) {