Refine EffectProxy logic

To allow create multiple EffectProxy instances for same type
Implement dump with all sub-effects

Bug: 271500140
Test: Enable AIDL and flash to pixel
Test: Play Youtube music with effect on/off
Test: dumpsys media.audio_flinger

Change-Id: I468d7e8712d7b098d869f147a4b40881ef11cabb
Merged-In: I468d7e8712d7b098d869f147a4b40881ef11cabb
diff --git a/media/libaudiohal/impl/EffectConversionHelperAidl.cpp b/media/libaudiohal/impl/EffectConversionHelperAidl.cpp
index 4c58fe9..8a582a5 100644
--- a/media/libaudiohal/impl/EffectConversionHelperAidl.cpp
+++ b/media/libaudiohal/impl/EffectConversionHelperAidl.cpp
@@ -194,11 +194,9 @@
                 statusTFromBinderStatus(mEffect->open(common, std::nullopt, &openReturn)));
 
         if (mIsProxyEffect) {
-            const auto& ret =
-                    std::static_pointer_cast<EffectProxy>(mEffect)->getEffectReturnParam();
-            mStatusQ = std::make_shared<StatusMQ>(ret->statusMQ);
-            mInputQ = std::make_shared<DataMQ>(ret->inputDataMQ);
-            mOutputQ = std::make_shared<DataMQ>(ret->outputDataMQ);
+            mStatusQ = std::static_pointer_cast<EffectProxy>(mEffect)->getStatusMQ();
+            mInputQ = std::static_pointer_cast<EffectProxy>(mEffect)->getInputMQ();
+            mOutputQ = std::static_pointer_cast<EffectProxy>(mEffect)->getOutputMQ();
         } else {
             mStatusQ = std::make_shared<StatusMQ>(openReturn.statusMQ);
             mInputQ = std::make_shared<DataMQ>(openReturn.inputDataMQ);
@@ -206,6 +204,7 @@
         }
 
         if (status_t status = updateEventFlags(); status != OK) {
+            ALOGV("%s closing at status %d", __func__, status);
             mEffect->close();
             return status;
         }
@@ -353,14 +352,15 @@
         ALOGI("%s offload param offload %s ioHandle %d", __func__,
               offload->isOffload ? "true" : "false", offload->ioHandle);
         mCommon.ioHandle = offload->ioHandle;
-        RETURN_STATUS_IF_ERROR(statusTFromBinderStatus(
-                std::static_pointer_cast<EffectProxy>(mEffect)->setOffloadParam(offload)));
-        // update FMQs
-        const auto& ret = std::static_pointer_cast<EffectProxy>(mEffect)->getEffectReturnParam();
-        mStatusQ = std::make_shared<StatusMQ>(ret->statusMQ);
-        mInputQ = std::make_shared<DataMQ>(ret->inputDataMQ);
-        mOutputQ = std::make_shared<DataMQ>(ret->outputDataMQ);
-        RETURN_STATUS_IF_ERROR(updateEventFlags());
+        const auto& effectProxy = std::static_pointer_cast<EffectProxy>(mEffect);
+        RETURN_STATUS_IF_ERROR(statusTFromBinderStatus(effectProxy->setOffloadParam(offload)));
+        // update FMQs if the effect instance already open
+        if (State state; effectProxy->getState(&state).isOk() && state != State::INIT) {
+            mStatusQ = effectProxy->getStatusMQ();
+            mInputQ = effectProxy->getInputMQ();
+            mOutputQ = effectProxy->getOutputMQ();
+            updateEventFlags();
+        }
     }
     return *static_cast<int32_t*>(pReplyData) = OK;
 }
@@ -408,17 +408,27 @@
 status_t EffectConversionHelperAidl::updateEventFlags() {
     status_t status = BAD_VALUE;
     EventFlag* efGroup = nullptr;
-    if (mStatusQ->isValid()) {
+    if (mStatusQ && mStatusQ->isValid()) {
         status = EventFlag::createEventFlag(mStatusQ->getEventFlagWord(), &efGroup);
         if (status != OK || !efGroup) {
             ALOGE("%s: create EventFlagGroup failed, ret %d, egGroup %p", __func__, status,
                   efGroup);
             status = (status == OK) ? BAD_VALUE : status;
         }
+    } else if (isBypassing()) {
+        // for effect with bypass (no processing) flag, it's okay to not have statusQ
+        return OK;
     }
+
     mEfGroup.reset(efGroup, EventFlagDeleter());
     return status;
 }
 
+bool EffectConversionHelperAidl::isBypassing() const {
+    return mEffect &&
+           (mDesc.common.flags.bypass ||
+            (mIsProxyEffect && std::static_pointer_cast<EffectProxy>(mEffect)->isBypassing()));
+}
+
 }  // namespace effect
 }  // namespace android
diff --git a/media/libaudiohal/impl/EffectConversionHelperAidl.h b/media/libaudiohal/impl/EffectConversionHelperAidl.h
index e2cf87f..7c8f11b 100644
--- a/media/libaudiohal/impl/EffectConversionHelperAidl.h
+++ b/media/libaudiohal/impl/EffectConversionHelperAidl.h
@@ -41,6 +41,7 @@
     std::shared_ptr<DataMQ> getInputMQ() { return mInputQ; }
     std::shared_ptr<DataMQ> getOutputMQ() { return mOutputQ; }
     std::shared_ptr<android::hardware::EventFlag> getEventFlagGroup() { return mEfGroup; }
+    bool isBypassing() const;
 
   protected:
     const int32_t mSessionId;
diff --git a/media/libaudiohal/impl/EffectHalAidl.cpp b/media/libaudiohal/impl/EffectHalAidl.cpp
index a5f2c61..36abfa4 100644
--- a/media/libaudiohal/impl/EffectHalAidl.cpp
+++ b/media/libaudiohal/impl/EffectHalAidl.cpp
@@ -76,8 +76,11 @@
 
 EffectHalAidl::~EffectHalAidl() {
     if (mEffect) {
-        mIsProxyEffect ? std::static_pointer_cast<EffectProxy>(mEffect)->destroy()
-                       : mFactory->destroyEffect(mEffect);
+        if (mIsProxyEffect) {
+            std::static_pointer_cast<EffectProxy>(mEffect)->destroy();
+        } else if (mFactory) {
+            mFactory->destroyEffect(mEffect);
+        }
     }
 }
 
@@ -167,6 +170,9 @@
     auto inputQ = mConversion->getInputMQ();
     auto outputQ = mConversion->getOutputMQ();
     auto efGroup = mConversion->getEventFlagGroup();
+    if (mConversion->isBypassing()) {
+        return OK;
+    }
     if (!statusQ || !statusQ->isValid() || !inputQ || !inputQ->isValid() || !outputQ ||
         !outputQ->isValid() || !efGroup) {
         ALOGE("%s invalid FMQ [Status %d I %d O %d] efGroup %p", __func__,
diff --git a/media/libaudiohal/impl/EffectProxy.cpp b/media/libaudiohal/impl/EffectProxy.cpp
index 3bb045b..fac03b7 100644
--- a/media/libaudiohal/impl/EffectProxy.cpp
+++ b/media/libaudiohal/impl/EffectProxy.cpp
@@ -15,9 +15,10 @@
  */
 
 #include <algorithm>
+#include <iterator>
 #include <memory>
 #define LOG_TAG "EffectProxy"
-//#define LOG_NDEBUG 0
+// #define LOG_NDEBUG 0
 
 #include <fmq/AidlMessageQueue.h>
 #include <system/audio_aidl_utils.h>
@@ -25,6 +26,7 @@
 
 #include "EffectProxy.h"
 
+using ::aidl::android::hardware::audio::effect::Capability;
 using ::aidl::android::hardware::audio::effect::CommandId;
 using ::aidl::android::hardware::audio::effect::Descriptor;
 using ::aidl::android::hardware::audio::effect::Flags;
@@ -33,19 +35,29 @@
 using ::aidl::android::hardware::audio::effect::Parameter;
 using ::aidl::android::hardware::audio::effect::State;
 using ::aidl::android::media::audio::common::AudioUuid;
-using ::android::audio::utils::toString;
 
-namespace android {
-namespace effect {
+namespace android::effect {
 
-EffectProxy::EffectProxy(const Descriptor::Identity& id, const std::shared_ptr<IFactory>& factory)
-    : mIdentity([](const Descriptor::Identity& subId) {
-          // update EffectProxy implementation UUID to the sub-effect proxy UUID
-          ALOG_ASSERT(subId.proxy.has_value(), "Sub-effect Identity must have valid proxy UUID");
-          Descriptor::Identity tempId = subId;
-          tempId.uuid = subId.proxy.value();
-          return tempId;
-      }(id)),
+EffectProxy::EffectProxy(const AudioUuid& uuid, const std::vector<Descriptor>& descriptors,
+                         const std::shared_ptr<IFactory>& factory)
+    : mDescriptorCommon(buildDescriptorCommon(uuid, descriptors)),
+      mSubEffects(
+              [](const std::vector<Descriptor>& descs, const std::shared_ptr<IFactory>& factory) {
+                  std::vector<SubEffect> subEffects;
+                  ALOG_ASSERT(factory, "invalid EffectFactory handle");
+                  ndk::ScopedAStatus status = ndk::ScopedAStatus::ok();
+                  for (const auto& desc : descs) {
+                      SubEffect sub({.descriptor = desc});
+                      status = factory->createEffect(desc.common.id.uuid, &sub.handle);
+                      if (!status.isOk() || !sub.handle) {
+                          sub.handle = nullptr;
+                          ALOGW("%s create sub-effect %s failed", __func__,
+                                ::android::audio::utils::toString(desc.common.id.uuid).c_str());
+                      }
+                      subEffects.emplace_back(sub);
+                  }
+                  return subEffects;
+              }(descriptors, factory)),
       mFactory(factory) {}
 
 EffectProxy::~EffectProxy() {
@@ -54,68 +66,9 @@
     mSubEffects.clear();
 }
 
-// sub effect must have same proxy UUID as EffectProxy, and the type UUID must match.
-ndk::ScopedAStatus EffectProxy::addSubEffect(const Descriptor& sub) {
-    ALOGV("%s: %s", __func__, toString(mIdentity.type).c_str());
-    if (0 != mSubEffects.count(sub.common.id) || !sub.common.id.proxy.has_value() ||
-        sub.common.id.proxy.value() != mIdentity.uuid) {
-        ALOGE("%s sub effect already exist or mismatch %s", __func__, sub.toString().c_str());
-        return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT,
-                                                                "illegalSubEffect");
-    }
-
-    // not create sub-effect yet
-    std::get<SubEffectTupleIndex::HANDLE>(mSubEffects[sub.common.id]) = nullptr;
-    std::get<SubEffectTupleIndex::DESCRIPTOR>(mSubEffects[sub.common.id]) = sub;
-    // set the last added sub-effect to active before setOffloadParam()
-    mActiveSub = sub.common.id;
-    ALOGI("%s add %s to proxy %s flag %s", __func__, mActiveSub.toString().c_str(),
-          mIdentity.toString().c_str(), sub.common.flags.toString().c_str());
-
-    if (sub.common.flags.hwAcceleratorMode == Flags::HardwareAccelerator::TUNNEL) {
-        mSubFlags.hwAcceleratorMode = Flags::HardwareAccelerator::TUNNEL;
-    }
-
-    // initial flag values before we know which sub-effect to active (with setOffloadParam)
-    // same as HIDL EffectProxy flags
-    mSubFlags.type = Flags::Type::INSERT;
-    mSubFlags.insert = Flags::Insert::LAST;
-    mSubFlags.volume = Flags::Volume::CTRL;
-
-    // set indication if any sub-effect indication was set
-    mSubFlags.offloadIndication |= sub.common.flags.offloadIndication;
-    mSubFlags.deviceIndication |= sub.common.flags.deviceIndication;
-    mSubFlags.audioModeIndication |= sub.common.flags.audioModeIndication;
-    mSubFlags.audioSourceIndication |= sub.common.flags.audioSourceIndication;
-
-    // set bypass when all sub-effects are bypassing
-    mSubFlags.bypass &= sub.common.flags.bypass;
-    return ndk::ScopedAStatus::ok();
-}
-
-ndk::ScopedAStatus EffectProxy::create() {
-    ALOGV("%s: %s", __func__, toString(mIdentity.type).c_str());
-    ndk::ScopedAStatus status = ndk::ScopedAStatus::ok();
-
-    for (auto& sub : mSubEffects) {
-        auto& effectHandle = std::get<SubEffectTupleIndex::HANDLE>(sub.second);
-        ALOGI("%s sub-effect %s", __func__, toString(sub.first.uuid).c_str());
-        status = mFactory->createEffect(sub.first.uuid, &effectHandle);
-        if (!status.isOk() || !effectHandle) {
-            ALOGE("%s sub-effect failed %s", __func__, toString(sub.first.uuid).c_str());
-            break;
-        }
-    }
-
-    // destroy all created effects if failure
-    if (!status.isOk()) {
-        destroy();
-    }
-    return status;
-}
-
 ndk::ScopedAStatus EffectProxy::destroy() {
-    ALOGV("%s: %s", __func__, toString(mIdentity.type).c_str());
+    ALOGV("%s: %s", __func__,
+          ::android::audio::utils::toString(mDescriptorCommon.id.type).c_str());
     return runWithAllSubEffects([&](std::shared_ptr<IEffect>& effect) {
         ndk::ScopedAStatus status = mFactory->destroyEffect(effect);
         if (status.isOk()) {
@@ -125,28 +78,24 @@
     });
 }
 
-const IEffect::OpenEffectReturn* EffectProxy::getEffectReturnParam() {
-    return &std::get<SubEffectTupleIndex::RETURN>(mSubEffects[mActiveSub]);
-}
-
 ndk::ScopedAStatus EffectProxy::setOffloadParam(const effect_offload_param_t* offload) {
     const auto& itor = std::find_if(mSubEffects.begin(), mSubEffects.end(), [&](const auto& sub) {
-        const auto& desc = std::get<SubEffectTupleIndex::DESCRIPTOR>(sub.second);
-        ALOGI("%s: isOffload %d sub-effect: %s, flags %s", __func__, offload->isOffload,
-              toString(desc.common.id.uuid).c_str(), desc.common.flags.toString().c_str());
+        const auto& desc = sub.descriptor;
         return offload->isOffload ==
                (desc.common.flags.hwAcceleratorMode == Flags::HardwareAccelerator::TUNNEL);
     });
     if (itor == mSubEffects.end()) {
         ALOGE("%s no %soffload sub-effect found", __func__, offload->isOffload ? "" : "non-");
+        mActiveSubIdx = 0;
         return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_NULL_POINTER,
                                                                 "noActiveEffctFound");
     }
 
-    mActiveSub = itor->first;
-    ALOGI("%s: active %soffload sub-effect: %s, flags %s", __func__,
-          offload->isOffload ? "" : "non-", toString(mActiveSub.uuid).c_str(),
-          std::get<SubEffectTupleIndex::DESCRIPTOR>(itor->second).common.flags.toString().c_str());
+    mActiveSubIdx = std::distance(mSubEffects.begin(), itor);
+    ALOGV("%s: active %soffload sub-effect %zu descriptor: %s", __func__,
+          offload->isOffload ? "" : "non-", mActiveSubIdx,
+          ::android::audio::utils::toString(mSubEffects[mActiveSubIdx].descriptor.common.id.uuid)
+                  .c_str());
     return ndk::ScopedAStatus::ok();
 }
 
@@ -154,20 +103,24 @@
 ndk::ScopedAStatus EffectProxy::open(const Parameter::Common& common,
                                      const std::optional<Parameter::Specific>& specific,
                                      IEffect::OpenEffectReturn* ret __unused) {
-    ALOGV("%s: %s", __func__, toString(mIdentity.type).c_str());
     ndk::ScopedAStatus status = ndk::ScopedAStatus::fromExceptionCodeWithMessage(
             EX_ILLEGAL_ARGUMENT, "nullEffectHandle");
     for (auto& sub : mSubEffects) {
-        auto& effect = std::get<SubEffectTupleIndex::HANDLE>(sub.second);
-        auto& openRet = std::get<SubEffectTupleIndex::RETURN>(sub.second);
-        if (!effect || !(status = effect->open(common, specific, &openRet)).isOk()) {
-            ALOGE("%s: failed to open UUID %s", __func__, toString(sub.first.uuid).c_str());
+        IEffect::OpenEffectReturn openReturn;
+        if (!sub.handle || !(status = sub.handle->open(common, specific, &openReturn)).isOk()) {
+            ALOGE("%s: failed to open %p UUID %s", __func__, sub.handle.get(),
+                  ::android::audio::utils::toString(sub.descriptor.common.id.uuid).c_str());
             break;
         }
+        sub.effectMq.statusQ = std::make_shared<StatusMQ>(openReturn.statusMQ);
+        sub.effectMq.inputQ = std::make_shared<DataMQ>(openReturn.inputDataMQ);
+        sub.effectMq.outputQ = std::make_shared<DataMQ>(openReturn.outputDataMQ);
     }
 
     // close all opened effects if failure
     if (!status.isOk()) {
+        ALOGE("%s: closing all sub-effects with error %s", __func__,
+              status.getDescription().c_str());
         close();
     }
 
@@ -175,38 +128,69 @@
 }
 
 ndk::ScopedAStatus EffectProxy::close() {
-    ALOGV("%s: %s", __func__, toString(mIdentity.type).c_str());
     return runWithAllSubEffects([&](std::shared_ptr<IEffect>& effect) {
         return effect->close();
     });
 }
 
 ndk::ScopedAStatus EffectProxy::getDescriptor(Descriptor* desc) {
+    desc->common = mDescriptorCommon;
+    desc->capability = mSubEffects[mActiveSubIdx].descriptor.capability;
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus EffectProxy::buildDescriptor(const AudioUuid& uuid,
+                                                const std::vector<Descriptor>& subEffectDescs,
+                                                Descriptor* desc) {
     if (!desc) {
-        ALOGE("%s: nuull descriptor pointer", __func__);
+        ALOGE("%s: null descriptor pointer", __func__);
         return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_NULL_POINTER, "nullptr");
     }
 
-    auto& activeSubEffect = std::get<SubEffectTupleIndex::HANDLE>(mSubEffects[mActiveSub]);
-    // return initial descriptor if no active sub-effect exist
-    if (!activeSubEffect) {
-        desc->common.id = mIdentity;
-        desc->common.flags = mSubFlags;
-        desc->common.name = "Proxy";
-        desc->common.implementor = "AOSP";
-    } else {
-        *desc = std::get<SubEffectTupleIndex::DESCRIPTOR>(mSubEffects[mActiveSub]);
-        desc->common.id = mIdentity;
+    if (subEffectDescs.size() < 2) {
+        ALOGE("%s: proxy need at least 2 sub-effects, got %zu", __func__, subEffectDescs.size());
+        return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT,
+                                                                "needMoreSubEffects");
     }
 
-    ALOGI("%s with %s", __func__, desc->toString().c_str());
+    desc->common = buildDescriptorCommon(uuid, subEffectDescs);
     return ndk::ScopedAStatus::ok();
 }
 
+Descriptor::Common EffectProxy::buildDescriptorCommon(
+        const AudioUuid& uuid, const std::vector<Descriptor>& subEffectDescs) {
+    Descriptor::Common common;
+
+    for (const auto& desc : subEffectDescs) {
+        if (desc.common.flags.hwAcceleratorMode == Flags::HardwareAccelerator::TUNNEL) {
+            common.flags.hwAcceleratorMode = Flags::HardwareAccelerator::TUNNEL;
+        }
+
+        // set indication if any sub-effect indication was set
+        common.flags.offloadIndication |= desc.common.flags.offloadIndication;
+        common.flags.deviceIndication |= desc.common.flags.deviceIndication;
+        common.flags.audioModeIndication |= desc.common.flags.audioModeIndication;
+        common.flags.audioSourceIndication |= desc.common.flags.audioSourceIndication;
+    }
+
+    // initial flag values before we know which sub-effect to active (with setOffloadParam)
+    // same as HIDL EffectProxy flags
+    common.flags.type = Flags::Type::INSERT;
+    common.flags.insert = Flags::Insert::LAST;
+    common.flags.volume = Flags::Volume::CTRL;
+
+    // copy type UUID from any of sub-effects, all sub-effects should have same type
+    common.id.type = subEffectDescs[0].common.id.type;
+    // replace implementation UUID with proxy UUID.
+    common.id.uuid = uuid;
+    common.id.proxy = std::nullopt;
+    common.name = "Proxy";
+    common.implementor = "AOSP";
+    return common;
+}
+
 // Handle with active sub-effect first, only send to other sub-effects when success
 ndk::ScopedAStatus EffectProxy::command(CommandId id) {
-    ALOGV("%s: %s, command %s", __func__, toString(mIdentity.type).c_str(),
-          android::internal::ToString(id).c_str());
     return runWithActiveSubEffectThenOthers(
             [&](const std::shared_ptr<IEffect>& effect) -> ndk::ScopedAStatus {
                 return effect->command(id);
@@ -241,34 +225,30 @@
         std::function<ndk::ScopedAStatus(const std::shared_ptr<IEffect>&)> const& func) {
     ndk::ScopedAStatus status = runWithActiveSubEffect(func);
     if (!status.isOk()) {
-        return status;
+        ALOGE("%s active sub-effect return error %s", __func__, status.getDescription().c_str());
     }
 
-    // proceed with others if active sub-effect success
-    for (const auto& sub : mSubEffects) {
-        auto& effect = std::get<SubEffectTupleIndex::HANDLE>(sub.second);
-        if (sub.first != mActiveSub) {
-            if (!effect) {
-                ALOGE("%s null sub-effect interface for %s", __func__,
-                      sub.first.toString().c_str());
-                continue;
-            }
-            func(effect);
+    // proceed with others
+    for (size_t i = 0; i < mSubEffects.size() && i != mActiveSubIdx; i++) {
+        if (!mSubEffects[i].handle) {
+            ALOGE("%s null sub-effect interface for %s", __func__,
+                  mSubEffects[i].descriptor.common.id.uuid.toString().c_str());
+            continue;
         }
+        func(mSubEffects[i].handle);
     }
     return status;
 }
 
 ndk::ScopedAStatus EffectProxy::runWithActiveSubEffect(
         std::function<ndk::ScopedAStatus(const std::shared_ptr<IEffect>&)> const& func) {
-    auto& effect = std::get<SubEffectTupleIndex::HANDLE>(mSubEffects[mActiveSub]);
-    if (!effect) {
+    if (!mSubEffects[mActiveSubIdx].handle) {
         ALOGE("%s null active sub-effect interface, active %s", __func__,
-              mActiveSub.toString().c_str());
+              mSubEffects[mActiveSubIdx].descriptor.toString().c_str());
         return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_NULL_POINTER,
                                                                 "activeSubEffectNull");
     }
-    return func(effect);
+    return func(mSubEffects[mActiveSubIdx].handle);
 }
 
 ndk::ScopedAStatus EffectProxy::runWithAllSubEffects(
@@ -276,12 +256,11 @@
     ndk::ScopedAStatus status = ndk::ScopedAStatus::ok();
     // proceed with others if active sub-effect success
     for (auto& sub : mSubEffects) {
-        auto& effect = std::get<SubEffectTupleIndex::HANDLE>(sub.second);
-        if (!effect) {
-            ALOGW("%s null sub-effect interface for %s", __func__, sub.first.toString().c_str());
+        if (!sub.handle) {
+            ALOGW("%s null sub-effect interface %s", __func__, sub.descriptor.toString().c_str());
             continue;
         }
-        ndk::ScopedAStatus temp = func(effect);
+        ndk::ScopedAStatus temp = func(sub.handle);
         if (!temp.isOk()) {
             status = ndk::ScopedAStatus::fromStatus(temp.getStatus());
         }
@@ -289,5 +268,34 @@
     return status;
 }
 
-} // namespace effect
-} // namespace android
+bool EffectProxy::isBypassing() const {
+    return mSubEffects[mActiveSubIdx].descriptor.common.flags.bypass;
+}
+
+binder_status_t EffectProxy::dump(int fd, const char** args, uint32_t numArgs) {
+    const std::string dumpString = toString();
+    write(fd, dumpString.c_str(), dumpString.size());
+
+    return runWithAllSubEffects([&](std::shared_ptr<IEffect>& effect) {
+               return ndk::ScopedAStatus::fromStatus(effect->dump(fd, args, numArgs));
+           })
+            .getStatus();
+}
+
+std::string EffectProxy::toString(size_t level) const {
+    std::string prefixSpace(level, ' ');
+    std::string ss = prefixSpace + "EffectProxy:\n";
+    prefixSpace += " ";
+    base::StringAppendF(&ss, "%sDescriptorCommon: %s\n", prefixSpace.c_str(),
+                        mDescriptorCommon.toString().c_str());
+    base::StringAppendF(&ss, "%sActiveSubIdx: %zu\n", prefixSpace.c_str(), mActiveSubIdx);
+    base::StringAppendF(&ss, "%sAllSubEffects:\n", prefixSpace.c_str());
+    for (size_t i = 0; i < mSubEffects.size(); i++) {
+        base::StringAppendF(&ss, "%s[%zu] - Handle: %p, %s\n", prefixSpace.c_str(), i,
+                            mSubEffects[i].handle.get(),
+                            mSubEffects[i].descriptor.toString().c_str());
+    }
+    return ss;
+}
+
+} // namespace android::effect
diff --git a/media/libaudiohal/impl/EffectProxy.h b/media/libaudiohal/impl/EffectProxy.h
index ffb8a19..18e1567 100644
--- a/media/libaudiohal/impl/EffectProxy.h
+++ b/media/libaudiohal/impl/EffectProxy.h
@@ -40,27 +40,10 @@
  */
 class EffectProxy final : public ::aidl::android::hardware::audio::effect::BnEffect {
   public:
-    EffectProxy(const ::aidl::android::hardware::audio::effect::Descriptor::Identity& id,
-                const std::shared_ptr<::aidl::android::hardware::audio::effect::IFactory>& factory);
-
-    /**
-     * Add a sub effect into the proxy, the descriptor of candidate sub-effect need to have same
-     * proxy UUID as mUuid.
-     */
-    ndk::ScopedAStatus addSubEffect(
-            const ::aidl::android::hardware::audio::effect::Descriptor& sub);
-
-    /**
-     * Create all sub-effects via AIDL IFactory, always call create() after all sub-effects added
-     * successfully with addSubEffect.
-     */
-    ndk::ScopedAStatus create();
-
-    /**
-     * Destroy all sub-effects via AIDL IFactory, always call create() after all sub-effects added
-     * successfully with addSubEffect.
-     */
-    ndk::ScopedAStatus destroy();
+    EffectProxy(
+            const ::aidl::android::media::audio::common::AudioUuid& uuid,
+            const std::vector<::aidl::android::hardware::audio::effect::Descriptor>& descriptors,
+            const std::shared_ptr<::aidl::android::hardware::audio::effect::IFactory>& factory);
 
     /**
      * Handle offload parameter setting from framework.
@@ -68,11 +51,9 @@
     ndk::ScopedAStatus setOffloadParam(const effect_offload_param_t* offload);
 
     /**
-     * Get the const reference of the active sub-effect return parameters.
-     * Always use this interface to get the effect open return parameters (FMQs) after a success
-     * setOffloadParam() call.
+     * Destroy all sub-effects via AIDL IFactory.
      */
-    const IEffect::OpenEffectReturn* getEffectReturnParam();
+    ndk::ScopedAStatus destroy();
 
     // IEffect interfaces override
     ndk::ScopedAStatus open(
@@ -91,25 +72,59 @@
             const ::aidl::android::hardware::audio::effect::Parameter::Id& id,
             ::aidl::android::hardware::audio::effect::Parameter* param) override;
 
+    static ndk::ScopedAStatus buildDescriptor(
+            const ::aidl::android::media::audio::common::AudioUuid& uuid,
+            const std::vector<::aidl::android::hardware::audio::effect::Descriptor>& subEffectDescs,
+            ::aidl::android::hardware::audio::effect::Descriptor* desc);
+
+    /**
+     * Get the const reference of the active sub-effect return parameters.
+     * Always use this interface to get the effect open return parameters (FMQs) after a success
+     * setOffloadParam() call.
+     */
+    using StatusMQ = ::android::AidlMessageQueue<
+            ::aidl::android::hardware::audio::effect::IEffect::Status,
+            ::aidl::android::hardware::common::fmq::SynchronizedReadWrite>;
+    using DataMQ = ::android::AidlMessageQueue<
+            float, ::aidl::android::hardware::common::fmq::SynchronizedReadWrite>;
+    const std::shared_ptr<StatusMQ>& getStatusMQ() const {
+        return mSubEffects[mActiveSubIdx].effectMq.statusQ;
+    }
+    const std::shared_ptr<DataMQ>& getInputMQ() const {
+        return mSubEffects[mActiveSubIdx].effectMq.inputQ;
+    }
+    const std::shared_ptr<DataMQ>& getOutputMQ() const {
+        return mSubEffects[mActiveSubIdx].effectMq.outputQ;
+    }
+
+    bool isBypassing() const;
+
+    // call dump for all sub-effects
+    binder_status_t dump(int fd, const char** args, uint32_t numArgs) override;
+
+    std::string toString(size_t indent = 0) const;
+
   private:
-    // Proxy identity, copy from one sub-effect, and update the implementation UUID to proxy UUID
-    const ::aidl::android::hardware::audio::effect::Descriptor::Identity mIdentity;
+    // Proxy descriptor common part, copy from one sub-effect, and update the implementation UUID to
+    // proxy UUID, proxy descriptor capability part comes from the active sub-effect capability
+    const ::aidl::android::hardware::audio::effect::Descriptor::Common mDescriptorCommon;
+
+    struct EffectMQ {
+        std::shared_ptr<StatusMQ> statusQ;
+        std::shared_ptr<DataMQ> inputQ, outputQ;
+    };
+    struct SubEffect {
+        const ::aidl::android::hardware::audio::effect::Descriptor descriptor;
+        std::shared_ptr<::aidl::android::hardware::audio::effect::IEffect> handle;
+        EffectMQ effectMq;
+    };
+    std::vector<SubEffect> mSubEffects;
+
     const std::shared_ptr<::aidl::android::hardware::audio::effect::IFactory> mFactory;
 
-    // A map of sub effects descriptor to the IEffect handle and return FMQ
-    enum SubEffectTupleIndex { HANDLE, DESCRIPTOR, RETURN };
-    using EffectProxySub =
-            std::tuple<std::shared_ptr<::aidl::android::hardware::audio::effect::IEffect>,
-                       ::aidl::android::hardware::audio::effect::Descriptor,
-                       ::aidl::android::hardware::audio::effect::IEffect::OpenEffectReturn>;
-    std::map<const ::aidl::android::hardware::audio::effect::Descriptor::Identity, EffectProxySub>
-            mSubEffects;
-
-    // Descriptor of the only active effect in the mSubEffects map
-    ::aidl::android::hardware::audio::effect::Descriptor::Identity mActiveSub;
-
-    // keep the flag of sub-effects
-    ::aidl::android::hardware::audio::effect::Flags mSubFlags;
+    // index of the active sub-effects, by default use the first one (index 0)
+    // It's safe to assume there will always at least two SubEffects in mSubEffects
+    size_t mActiveSubIdx = 0;
 
     ndk::ScopedAStatus runWithActiveSubEffectThenOthers(
             std::function<ndk::ScopedAStatus(
@@ -122,6 +137,12 @@
     ndk::ScopedAStatus runWithAllSubEffects(
             std::function<ndk::ScopedAStatus(std::shared_ptr<IEffect>&)> const& func);
 
+    // build Descriptor.Common with all sub-effect descriptors
+    static ::aidl::android::hardware::audio::effect::Descriptor::Common buildDescriptorCommon(
+            const ::aidl::android::media::audio::common::AudioUuid& uuid,
+            const std::vector<::aidl::android::hardware::audio::effect::Descriptor>&
+                    subEffectDescs);
+
     // close and release all sub-effects
     ~EffectProxy();
 };
diff --git a/media/libaudiohal/impl/EffectsFactoryHalAidl.cpp b/media/libaudiohal/impl/EffectsFactoryHalAidl.cpp
index f278ca0..cf19dae 100644
--- a/media/libaudiohal/impl/EffectsFactoryHalAidl.cpp
+++ b/media/libaudiohal/impl/EffectsFactoryHalAidl.cpp
@@ -68,26 +68,28 @@
           }
           return list;
       }()),
-      mUuidProxyMap([this]() {
-          std::map<AudioUuid, std::shared_ptr<EffectProxy>> proxyMap;
+      mProxyUuidDescriptorMap([this]() {
+          std::map<AudioUuid, std::vector<Descriptor>> proxyUuidMap;
           for (const auto& desc : mHalDescList) {
-              // create EffectProxy
               if (desc.common.id.proxy.has_value()) {
                   const auto& uuid = desc.common.id.proxy.value();
-                  if (0 == proxyMap.count(uuid)) {
-                      proxyMap.insert({uuid, ndk::SharedRefBase::make<EffectProxy>(desc.common.id,
-                                                                                   mFactory)});
+                  if (proxyUuidMap.count(uuid) == 0) {
+                      proxyUuidMap.insert({uuid, {desc}});
+                  } else {
+                      proxyUuidMap[uuid].emplace_back(desc);
                   }
-                  proxyMap[uuid]->addSubEffect(desc);
-                  ALOGI("%s addSubEffect %s", __func__, desc.common.toString().c_str());
               }
           }
-          return proxyMap;
+          return proxyUuidMap;
       }()),
       mProxyDescList([this]() {
           std::vector<Descriptor> list;
-          for (const auto& proxy : mUuidProxyMap) {
-              if (Descriptor desc; proxy.second && proxy.second->getDescriptor(&desc).isOk()) {
+          for (const auto& proxy : mProxyUuidDescriptorMap) {
+              if (Descriptor desc;
+                  EffectProxy::buildDescriptor(proxy.first /* uuid */,
+                                               proxy.second /* sub-effect descriptor list */,
+                                               &desc /* proxy descriptor */)
+                          .isOk()) {
                   list.emplace_back(std::move(desc));
               }
           }
@@ -118,7 +120,8 @@
     }
 
     *pNumEffects = mEffectCount;
-    ALOGI("%s %d", __func__, *pNumEffects);
+    ALOGD("%s %u non %zu proxyMap %zu proxyDesc %zu", __func__, *pNumEffects,
+          mNonProxyDescList.size(), mProxyUuidDescriptorMap.size(), mProxyDescList.size());
     return OK;
 }
 
@@ -183,8 +186,10 @@
     // Use EffectProxy interface instead of IFactory to create
     const bool isProxy = isProxyEffect(aidlUuid);
     if (isProxy) {
-        aidlEffect = mUuidProxyMap.at(aidlUuid);
-        RETURN_STATUS_IF_ERROR(statusTFromBinderStatus(mUuidProxyMap.at(aidlUuid)->create()));
+        aidlEffect = ndk::SharedRefBase::make<EffectProxy>(
+                aidlUuid, mProxyUuidDescriptorMap.at(aidlUuid) /* sub-effect descriptor list */,
+                mFactory);
+        mProxyList.emplace_back(std::static_pointer_cast<EffectProxy>(aidlEffect));
     } else {
         RETURN_STATUS_IF_ERROR(
                 statusTFromBinderStatus(mFactory->createEffect(aidlUuid, &aidlEffect)));
@@ -209,12 +214,11 @@
 
 status_t EffectsFactoryHalAidl::dumpEffects(int fd) {
     status_t ret = OK;
+    std::lock_guard lg(mLock);
     // record the error ret and continue dump as many effects as possible
-    for (const auto& proxy : mUuidProxyMap) {
-        if (proxy.second) {
-            if (status_t temp = proxy.second->dump(fd, nullptr, 0); temp != OK) {
-                ret = temp;
-            }
+    for (const auto& proxy : mProxyList) {
+        if (status_t temp = BAD_VALUE; proxy && (temp = proxy->dump(fd, nullptr, 0)) != OK) {
+            ret = temp;
         }
     }
     RETURN_STATUS_IF_ERROR(mFactory->dump(fd, nullptr, 0));
@@ -280,7 +284,7 @@
 }
 
 bool EffectsFactoryHalAidl::isProxyEffect(const AudioUuid& uuid) const {
-    return 0 != mUuidProxyMap.count(uuid);
+    return 0 != mProxyUuidDescriptorMap.count(uuid);
 }
 
 std::shared_ptr<const effectsConfig::Processings> EffectsFactoryHalAidl::getProcessings() const {
diff --git a/media/libaudiohal/impl/EffectsFactoryHalAidl.h b/media/libaudiohal/impl/EffectsFactoryHalAidl.h
index 39beea2..b900cb4 100644
--- a/media/libaudiohal/impl/EffectsFactoryHalAidl.h
+++ b/media/libaudiohal/impl/EffectsFactoryHalAidl.h
@@ -17,6 +17,7 @@
 #pragma once
 
 #include <cstddef>
+#include <list>
 #include <memory>
 #include <mutex>
 
@@ -72,10 +73,9 @@
     const detail::AudioHalVersionInfo mHalVersion;
     // Full list of HAL effect descriptors
     const std::vector<Descriptor> mHalDescList;
-    // Map of proxy UUID (key) to the proxy object
-    const std::map<::aidl::android::media::audio::common::AudioUuid /* proxy impl UUID */,
-                   std::shared_ptr<EffectProxy>>
-            mUuidProxyMap;
+    // Map of proxy UUID (key) to the Descriptor of sub-effects
+    const std::map<::aidl::android::media::audio::common::AudioUuid, std::vector<Descriptor>>
+            mProxyUuidDescriptorMap;
     // List of effect proxy, initialize after mUuidProxyMap because it need to have all sub-effects
     const std::vector<Descriptor> mProxyDescList;
     // List of non-proxy effects
@@ -85,16 +85,19 @@
     // Query result of pre and post processing from effect factory
     const std::vector<Processing> mAidlProcessings;
 
+    // list of the EffectProxy instances
+    std::list<std::shared_ptr<EffectProxy>> mProxyList;
+
     std::mutex mLock;
     uint64_t mEffectIdCounter GUARDED_BY(mLock) = 0;  // Align with HIDL (0 is INVALID_ID)
 
     virtual ~EffectsFactoryHalAidl() = default;
     status_t getHalDescriptorWithImplUuid(
-            const aidl::android::media::audio::common::AudioUuid& uuid,
+            const ::aidl::android::media::audio::common::AudioUuid& uuid,
             effect_descriptor_t* pDescriptor);
 
     status_t getHalDescriptorWithTypeUuid(
-            const aidl::android::media::audio::common::AudioUuid& type,
+            const ::aidl::android::media::audio::common::AudioUuid& type,
             std::vector<effect_descriptor_t>* descriptors);
 
     bool isProxyEffect(const aidl::android::media::audio::common::AudioUuid& uuid) const;
diff --git a/media/libaudiohal/tests/EffectProxy_test.cpp b/media/libaudiohal/tests/EffectProxy_test.cpp
index 92e3dce..8668e85 100644
--- a/media/libaudiohal/tests/EffectProxy_test.cpp
+++ b/media/libaudiohal/tests/EffectProxy_test.cpp
@@ -60,7 +60,7 @@
         mFactory->queryEffects(std::nullopt, std::nullopt, std::nullopt, &mDescs);
         for (const auto& desc : mDescs) {
             if (desc.common.id.proxy.has_value()) {
-                mProxyDescs.insert({desc.common.id, desc});
+                mProxyDescs[desc.common.id.proxy.value()].emplace_back(desc);
             }
         }
     }
@@ -96,44 +96,24 @@
         return common;
     }
 
-    static bool isFlagSet(const ::aidl::android::hardware::audio::effect::Descriptor& desc,
-                          Flags::HardwareAccelerator flag) {
-        return desc.common.flags.hwAcceleratorMode == flag;
-    }
-
     enum TupleIndex { HANDLE, DESCRIPTOR };
     using EffectProxyTuple = std::tuple<std::shared_ptr<EffectProxy>, std::vector<Descriptor>>;
 
     std::map<AudioUuid, EffectProxyTuple> createAllProxies() {
         std::map<AudioUuid, EffectProxyTuple> proxyMap;
         for (const auto& itor : mProxyDescs) {
-            const auto& uuid = itor.first.proxy.value();
+            const auto& uuid = itor.first;
             if (proxyMap.end() == proxyMap.find(uuid)) {
                 std::get<TupleIndex::HANDLE>(proxyMap[uuid]) =
-                        ndk::SharedRefBase::make<EffectProxy>(itor.first, mFactory);
+                        ndk::SharedRefBase::make<EffectProxy>(itor.first, itor.second, mFactory);
             }
         }
         return proxyMap;
     }
 
-    bool addAllSubEffects(std::map<AudioUuid, EffectProxyTuple> proxyMap) {
-        for (auto& itor : mProxyDescs) {
-            const auto& uuid = itor.first.proxy.value();
-            if (proxyMap.end() == proxyMap.find(uuid)) {
-                return false;
-            }
-            auto& proxy = std::get<TupleIndex::HANDLE>(proxyMap[uuid]);
-            if (!proxy->addSubEffect(itor.second).isOk()) {
-                return false;
-            }
-            std::get<TupleIndex::DESCRIPTOR>(proxyMap[uuid]).emplace_back(itor.second);
-        }
-        return true;
-    }
-
     std::shared_ptr<IFactory> mFactory;
     std::vector<Descriptor> mDescs;
-    std::map<Descriptor::Identity, Descriptor> mProxyDescs;
+    std::map<const AudioUuid, std::vector<Descriptor>> mProxyDescs;
 };
 
 TEST_F(EffectProxyTest, createProxy) {
@@ -144,24 +124,20 @@
 
 TEST_F(EffectProxyTest, addSubEffectsCreateAndDestroy) {
     auto proxyMap = createAllProxies();
-    ASSERT_TRUE(addAllSubEffects(proxyMap));
 
     for (const auto& itor : proxyMap) {
         auto& proxy = std::get<TupleIndex::HANDLE>(itor.second);
-        EXPECT_TRUE(proxy->create().isOk());
         EXPECT_TRUE(proxy->destroy().isOk());
     }
 }
 
 TEST_F(EffectProxyTest, addSubEffectsCreateOpenCloseDestroy) {
     auto proxyMap = createAllProxies();
-    EXPECT_TRUE(addAllSubEffects(proxyMap));
 
     Parameter::Common common = createParamCommon();
     IEffect::OpenEffectReturn ret;
     for (const auto& itor : proxyMap) {
         auto& proxy = std::get<TupleIndex::HANDLE>(itor.second);
-        EXPECT_TRUE(proxy->create().isOk());
         EXPECT_TRUE(proxy->open(common, std::nullopt, &ret).isOk());
         EXPECT_TRUE(proxy->close().isOk());
         EXPECT_TRUE(proxy->destroy().isOk());
@@ -171,33 +147,23 @@
 // Add sub-effects, set active sub-effect with different checkers
 TEST_F(EffectProxyTest, setOffloadParam) {
     auto proxyMap = createAllProxies();
-    EXPECT_TRUE(addAllSubEffects(proxyMap));
 
     // Any flag exist should be able to set successfully
-    bool isNoneExist = false, isSimpleExist = false, isTunnelExist = false;
-    for (const auto& itor : mProxyDescs) {
-        isNoneExist = isNoneExist || isFlagSet(itor.second, Flags::HardwareAccelerator::NONE);
-        isSimpleExist = isSimpleExist || isFlagSet(itor.second, Flags::HardwareAccelerator::SIMPLE);
-        isTunnelExist = isTunnelExist || isFlagSet(itor.second, Flags::HardwareAccelerator::TUNNEL);
-    }
-
     Parameter::Common common = createParamCommon();
     IEffect::OpenEffectReturn ret;
     for (const auto& itor : proxyMap) {
         auto& proxy = std::get<TupleIndex::HANDLE>(itor.second);
-        EXPECT_TRUE(proxy->create().isOk());
         EXPECT_TRUE(proxy->open(common, std::nullopt, &ret).isOk());
         effect_offload_param_t offloadParam{false, 0};
-        EXPECT_EQ(isNoneExist || isSimpleExist, proxy->setOffloadParam(&offloadParam).isOk());
+        EXPECT_TRUE(proxy->setOffloadParam(&offloadParam).isOk());
         offloadParam.isOffload = true;
-        EXPECT_EQ(isTunnelExist, proxy->setOffloadParam(&offloadParam).isOk());
+        EXPECT_TRUE(proxy->setOffloadParam(&offloadParam).isOk());
         EXPECT_TRUE(proxy->close().isOk());
         EXPECT_TRUE(proxy->destroy().isOk());
     }
 }
 TEST_F(EffectProxyTest, destroyWithoutCreate) {
     auto proxyMap = createAllProxies();
-    ASSERT_TRUE(addAllSubEffects(proxyMap));
 
     for (const auto& itor : proxyMap) {
         auto& proxy = std::get<TupleIndex::HANDLE>(itor.second);
@@ -207,11 +173,9 @@
 
 TEST_F(EffectProxyTest, closeWithoutOpen) {
     auto proxyMap = createAllProxies();
-    ASSERT_TRUE(addAllSubEffects(proxyMap));
 
     for (const auto& itor : proxyMap) {
         auto& proxy = std::get<TupleIndex::HANDLE>(itor.second);
-        EXPECT_TRUE(proxy->create().isOk());
 
         EXPECT_TRUE(proxy->close().isOk());
         EXPECT_TRUE(proxy->destroy().isOk());
@@ -221,16 +185,6 @@
 // Add sub-effects, set active sub-effect, create, open, and send command, expect success handling
 TEST_F(EffectProxyTest, normalSequency) {
     auto proxyMap = createAllProxies();
-    ASSERT_TRUE(addAllSubEffects(proxyMap));
-
-    bool isTunnelExist = [&]() {
-        for (const auto& itor : mProxyDescs) {
-            if (isFlagSet(itor.second, Flags::HardwareAccelerator::TUNNEL)) {
-                return true;
-            }
-        }
-        return false;
-    }();
 
     Parameter::Common common = createParamCommon();
     IEffect::OpenEffectReturn ret;
@@ -242,14 +196,14 @@
         Parameter expect;
         auto& proxy = std::get<TupleIndex::HANDLE>(itor.second);
         effect_offload_param_t offloadParam{true, 0};
-        EXPECT_EQ(isTunnelExist, proxy->setOffloadParam(&offloadParam).isOk());
+        EXPECT_TRUE(proxy->setOffloadParam(&offloadParam).isOk());
 
-        EXPECT_TRUE(proxy->create().isOk());
         EXPECT_TRUE(proxy->open(common, std::nullopt, &ret).isOk());
 
         EXPECT_TRUE(proxy->setParameter(param).isOk());
         EXPECT_TRUE(proxy->getParameter(id, &expect).isOk());
-        EXPECT_EQ(expect, param);
+        EXPECT_EQ(expect, param) << " EXPECTED: " << expect.toString()
+                                 << "\nACTUAL: " << param.toString();
 
         EXPECT_TRUE(proxy->command(CommandId::START).isOk());
         EXPECT_TRUE(proxy->getState(&state).isOk());
@@ -267,14 +221,6 @@
 // setParameter, change active sub-effect, verify with getParameter
 TEST_F(EffectProxyTest, changeActiveSubAndVerifyParameter) {
     auto proxyMap = createAllProxies();
-    EXPECT_TRUE(addAllSubEffects(proxyMap));
-
-    bool isNoneExist = false, isSimpleExist = false, isTunnelExist = false;
-    for (const auto& itor : mProxyDescs) {
-        isNoneExist = isNoneExist || isFlagSet(itor.second, Flags::HardwareAccelerator::NONE);
-        isSimpleExist = isSimpleExist || isFlagSet(itor.second, Flags::HardwareAccelerator::SIMPLE);
-        isTunnelExist = isTunnelExist || isFlagSet(itor.second, Flags::HardwareAccelerator::TUNNEL);
-    }
 
     Parameter::Common common = createParamCommon();
     IEffect::OpenEffectReturn ret;
@@ -284,19 +230,18 @@
     for (const auto& itor : proxyMap) {
         Parameter expect;
         auto& proxy = std::get<TupleIndex::HANDLE>(itor.second);
-        EXPECT_TRUE(proxy->create().isOk());
         EXPECT_TRUE(proxy->open(common, std::nullopt, &ret).isOk());
         EXPECT_TRUE(proxy->setParameter(param).isOk());
         EXPECT_TRUE(proxy->getParameter(id, &expect).isOk());
         EXPECT_EQ(expect, param);
 
         effect_offload_param_t offloadParam{false, 0};
-        EXPECT_EQ(isNoneExist || isSimpleExist, proxy->setOffloadParam(&offloadParam).isOk());
+        EXPECT_TRUE(proxy->setOffloadParam(&offloadParam).isOk());
         EXPECT_TRUE(proxy->getParameter(id, &expect).isOk());
         EXPECT_EQ(expect, param);
 
         offloadParam.isOffload = true;
-        EXPECT_EQ(isTunnelExist, proxy->setOffloadParam(&offloadParam).isOk());
+        EXPECT_TRUE(proxy->setOffloadParam(&offloadParam).isOk());
         EXPECT_TRUE(proxy->getParameter(id, &expect).isOk());
         EXPECT_EQ(expect, param);
 
@@ -308,14 +253,6 @@
 // send command, change active sub-effect, then verify the state with getState
 TEST_F(EffectProxyTest, changeActiveSubAndVerifyState) {
     auto proxyMap = createAllProxies();
-    ASSERT_TRUE(addAllSubEffects(proxyMap));
-
-    bool isNoneExist = false, isSimpleExist = false, isTunnelExist = false;
-    for (const auto& itor : mProxyDescs) {
-        isNoneExist = isNoneExist || isFlagSet(itor.second, Flags::HardwareAccelerator::NONE);
-        isSimpleExist = isSimpleExist || isFlagSet(itor.second, Flags::HardwareAccelerator::SIMPLE);
-        isTunnelExist = isTunnelExist || isFlagSet(itor.second, Flags::HardwareAccelerator::TUNNEL);
-    }
 
     Parameter::Common common = createParamCommon();
     IEffect::OpenEffectReturn ret;
@@ -323,7 +260,6 @@
     for (const auto& itor : proxyMap) {
         Parameter expect;
         auto& proxy = std::get<TupleIndex::HANDLE>(itor.second);
-        EXPECT_TRUE(proxy->create().isOk());
         EXPECT_TRUE(proxy->getState(&state).isOk());
         EXPECT_EQ(State::INIT, state);
         EXPECT_TRUE(proxy->open(common, std::nullopt, &ret).isOk());
@@ -334,12 +270,12 @@
         EXPECT_EQ(State::PROCESSING, state);
 
         effect_offload_param_t offloadParam{false, 0};
-        EXPECT_EQ(isNoneExist || isSimpleExist, proxy->setOffloadParam(&offloadParam).isOk());
+        EXPECT_TRUE(proxy->setOffloadParam(&offloadParam).isOk());
         EXPECT_TRUE(proxy->getState(&state).isOk());
         EXPECT_EQ(State::PROCESSING, state);
 
         offloadParam.isOffload = true;
-        EXPECT_EQ(isTunnelExist, proxy->setOffloadParam(&offloadParam).isOk());
+        EXPECT_TRUE(proxy->setOffloadParam(&offloadParam).isOk());
         EXPECT_TRUE(proxy->getState(&state).isOk());
         EXPECT_EQ(State::PROCESSING, state);