Merge "audio policy: concurrent capture effects" into qt-dev
am: df8638b4c4

Change-Id: I57d9f902c979426e4a12439ece4430fd7168c253
diff --git a/media/libaudioclient/IAudioFlinger.cpp b/media/libaudioclient/IAudioFlinger.cpp
index 825cd4e..dd95e34 100644
--- a/media/libaudioclient/IAudioFlinger.cpp
+++ b/media/libaudioclient/IAudioFlinger.cpp
@@ -89,6 +89,7 @@
     GET_MICROPHONES,
     SET_MASTER_BALANCE,
     GET_MASTER_BALANCE,
+    SET_EFFECT_SUSPENDED,
 };
 
 #define MAX_ITEMS_PER_LIST 1024
@@ -718,6 +719,18 @@
         return reply.readInt32();
     }
 
+    virtual void setEffectSuspended(int effectId,
+                                    audio_session_t sessionId,
+                                    bool suspended)
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
+        data.writeInt32(effectId);
+        data.writeInt32(sessionId);
+        data.writeInt32(suspended ? 1 : 0);
+        remote()->transact(SET_EFFECT_SUSPENDED, data, &reply);
+    }
+
     virtual audio_module_handle_t loadHwModule(const char *name)
     {
         Parcel data, reply;
@@ -913,6 +926,7 @@
         case INVALIDATE_STREAM:
         case SET_VOICE_VOLUME:
         case MOVE_EFFECTS:
+        case SET_EFFECT_SUSPENDED:
         case LOAD_HW_MODULE:
         case LIST_AUDIO_PORTS:
         case GET_AUDIO_PORT:
@@ -926,6 +940,7 @@
             // return status only for non void methods
             switch (code) {
                 case SET_RECORD_SILENCED:
+                case SET_EFFECT_SUSPENDED:
                     break;
                 default:
                     reply->writeInt32(static_cast<int32_t> (INVALID_OPERATION));
@@ -1371,6 +1386,14 @@
             reply->writeInt32(moveEffects(session, srcOutput, dstOutput));
             return NO_ERROR;
         } break;
+        case SET_EFFECT_SUSPENDED: {
+            CHECK_INTERFACE(IAudioFlinger, data, reply);
+            int effectId = data.readInt32();
+            audio_session_t sessionId = (audio_session_t) data.readInt32();
+            bool suspended = data.readInt32() == 1;
+            setEffectSuspended(effectId, sessionId, suspended);
+            return NO_ERROR;
+        } break;
         case LOAD_HW_MODULE: {
             CHECK_INTERFACE(IAudioFlinger, data, reply);
             reply->writeInt32(loadHwModule(data.readCString()));
diff --git a/media/libaudioclient/include/media/IAudioFlinger.h b/media/libaudioclient/include/media/IAudioFlinger.h
index ef0ed0c..dcc18b6 100644
--- a/media/libaudioclient/include/media/IAudioFlinger.h
+++ b/media/libaudioclient/include/media/IAudioFlinger.h
@@ -457,6 +457,10 @@
     virtual status_t moveEffects(audio_session_t session, audio_io_handle_t srcOutput,
                                     audio_io_handle_t dstOutput) = 0;
 
+    virtual void setEffectSuspended(int effectId,
+                                    audio_session_t sessionId,
+                                    bool suspended) = 0;
+
     virtual audio_module_handle_t loadHwModule(const char *name) = 0;
 
     // helpers for android.media.AudioManager.getProperty(), see description there for meaning
diff --git a/media/libeffects/preprocessing/PreProcessing.cpp b/media/libeffects/preprocessing/PreProcessing.cpp
index 50c33c6..5fab5be 100644
--- a/media/libeffects/preprocessing/PreProcessing.cpp
+++ b/media/libeffects/preprocessing/PreProcessing.cpp
@@ -926,7 +926,7 @@
         delete session->revBuf;
         session->revBuf = NULL;
 
-        session->io = 0;
+        session->id = 0;
     }
 
     return 0;
@@ -1155,7 +1155,7 @@
 {
     size_t i;
     for (i = 0; i < PREPROC_NUM_SESSIONS; i++) {
-        if (sSessions[i].io == ioId) {
+        if (sSessions[i].id == sessionId) {
             if (sSessions[i].createdMsk & (1 << procId)) {
                 return NULL;
             }
@@ -1163,7 +1163,7 @@
         }
     }
     for (i = 0; i < PREPROC_NUM_SESSIONS; i++) {
-        if (sSessions[i].io == 0) {
+        if (sSessions[i].id == 0) {
             sSessions[i].id = sessionId;
             sSessions[i].io = ioId;
             return &sSessions[i];
@@ -1915,7 +1915,7 @@
     status = Session_CreateEffect(session, procId, pInterface);
 
     if (status < 0 && session->createdMsk == 0) {
-        session->io = 0;
+        session->id = 0;
     }
     return status;
 }
@@ -1929,7 +1929,7 @@
 
     preproc_effect_t *fx = (preproc_effect_t *)interface;
 
-    if (fx->session->io == 0) {
+    if (fx->session->id == 0) {
         return -EINVAL;
     }
     return Session_ReleaseEffect(fx->session, fx);
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 9527195..55db699 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -1699,18 +1699,35 @@
 }
 
 // getEffectThread_l() must be called with AudioFlinger::mLock held
-sp<AudioFlinger::PlaybackThread> AudioFlinger::getEffectThread_l(audio_session_t sessionId,
-        int EffectId)
+sp<AudioFlinger::ThreadBase> AudioFlinger::getEffectThread_l(audio_session_t sessionId,
+        int effectId)
 {
-    sp<PlaybackThread> thread;
+    sp<ThreadBase> thread;
 
     for (size_t i = 0; i < mPlaybackThreads.size(); i++) {
-        if (mPlaybackThreads.valueAt(i)->getEffect(sessionId, EffectId) != 0) {
+        if (mPlaybackThreads.valueAt(i)->getEffect(sessionId, effectId) != 0) {
             ALOG_ASSERT(thread == 0);
             thread = mPlaybackThreads.valueAt(i);
         }
     }
-
+    if (thread != nullptr) {
+        return thread;
+    }
+    for (size_t i = 0; i < mRecordThreads.size(); i++) {
+        if (mRecordThreads.valueAt(i)->getEffect(sessionId, effectId) != 0) {
+            ALOG_ASSERT(thread == 0);
+            thread = mRecordThreads.valueAt(i);
+        }
+    }
+    if (thread != nullptr) {
+        return thread;
+    }
+    for (size_t i = 0; i < mMmapThreads.size(); i++) {
+        if (mMmapThreads.valueAt(i)->getEffect(sessionId, effectId) != 0) {
+            ALOG_ASSERT(thread == 0);
+            thread = mMmapThreads.valueAt(i);
+        }
+    }
     return thread;
 }
 
@@ -3464,6 +3481,23 @@
     return moveEffectChain_l(sessionId, srcThread, dstThread);
 }
 
+
+void AudioFlinger::setEffectSuspended(int effectId,
+                                audio_session_t sessionId,
+                                bool suspended)
+{
+    Mutex::Autolock _l(mLock);
+
+    sp<ThreadBase> thread = getEffectThread_l(sessionId, effectId);
+    if (thread == nullptr) {
+      return;
+    }
+    Mutex::Autolock _sl(thread->mLock);
+    sp<EffectModule> effect = thread->getEffect_l(sessionId, effectId);
+    thread->setEffectSuspended_l(&effect->desc().type, suspended, sessionId);
+}
+
+
 // moveEffectChain_l must be called with both srcThread and dstThread mLocks held
 status_t AudioFlinger::moveEffectChain_l(audio_session_t sessionId,
                                    AudioFlinger::PlaybackThread *srcThread,
@@ -3541,7 +3575,8 @@
 {
     status_t status = NO_ERROR;
     Mutex::Autolock _l(mLock);
-    sp<PlaybackThread> thread = getEffectThread_l(AUDIO_SESSION_OUTPUT_MIX, EffectId);
+    sp<PlaybackThread> thread =
+        static_cast<PlaybackThread *>(getEffectThread_l(AUDIO_SESSION_OUTPUT_MIX, EffectId).get());
 
     if (EffectId != 0 && thread != 0 && dstThread != thread.get()) {
         Mutex::Autolock _dl(dstThread->mLock);
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index 8bf89c8..72e669a 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -239,6 +239,10 @@
     virtual status_t moveEffects(audio_session_t sessionId, audio_io_handle_t srcOutput,
                         audio_io_handle_t dstOutput);
 
+            void setEffectSuspended(int effectId,
+                                    audio_session_t sessionId,
+                                    bool suspended) override;
+
     virtual audio_module_handle_t loadHwModule(const char *name);
 
     virtual uint32_t getPrimaryOutputSamplingRate();
@@ -708,7 +712,7 @@
               // return the playback thread with smallest HAL buffer size, and prefer fast
               PlaybackThread *fastPlaybackThread_l() const;
 
-              sp<PlaybackThread> getEffectThread_l(audio_session_t sessionId, int EffectId);
+              sp<ThreadBase> getEffectThread_l(audio_session_t sessionId, int effectId);
 
 
                 void        removeClient_l(pid_t pid);
diff --git a/services/audioflinger/Effects.cpp b/services/audioflinger/Effects.cpp
index 50ab634..3c4fbba 100644
--- a/services/audioflinger/Effects.cpp
+++ b/services/audioflinger/Effects.cpp
@@ -1488,9 +1488,10 @@
         result.append("\t\tCould not lock Fx mutex:\n");
     }
 
-    result.append("\t\tSession Status State Engine:\n");
-    result.appendFormat("\t\t%05d   %03d    %03d   %p\n",
-            mSessionId, mStatus, mState, mEffectInterface.get());
+    result.append("\t\tSession Status State Registered Enabled Suspended Engine:\n");
+    result.appendFormat("\t\t%05d   %03d    %03d   %s          %s       %s         %p\n",
+            mSessionId, mStatus, mState, mPolicyRegistered ? "y" : "n", mPolicyEnabled ? "y" : "n",
+            mSuspended ? "y" : "n", mEffectInterface.get());
 
     result.append("\t\tDescriptor:\n");
     char uuidStr[64];
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index e71f0c6..59ced26 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -8248,11 +8248,6 @@
 
 status_t AudioFlinger::RecordThread::addEffectChain_l(const sp<EffectChain>& chain)
 {
-    // only one chain per input thread
-    if (!mEffectChains.isEmpty()) {
-        ALOGW("addEffectChain_l() already one chain %p on thread %p", chain.get(), this);
-        return INVALID_OPERATION;
-    }
     ALOGV("addEffectChain_l() %p on thread %p", chain.get(), this);
     chain->setThread(this);
     chain->setInBuffer(NULL);
@@ -8272,13 +8267,14 @@
 size_t AudioFlinger::RecordThread::removeEffectChain_l(const sp<EffectChain>& chain)
 {
     ALOGV("removeEffectChain_l() %p from thread %p", chain.get(), this);
-    ALOGW_IF(mEffectChains.size() != 1,
-            "removeEffectChain_l() %p invalid chain size %zu on thread %p",
-            chain.get(), mEffectChains.size(), this);
-    if (mEffectChains.size() == 1) {
-        mEffectChains.removeAt(0);
+
+    for (size_t i = 0; i < mEffectChains.size(); i++) {
+        if (chain == mEffectChains[i]) {
+            mEffectChains.removeAt(i);
+            break;
+        }
     }
-    return 0;
+    return mEffectChains.size();
 }
 
 status_t AudioFlinger::RecordThread::createAudioPatch_l(const struct audio_patch *patch,
diff --git a/services/audiopolicy/AudioPolicyInterface.h b/services/audiopolicy/AudioPolicyInterface.h
index caf0d7e..4b56a46 100644
--- a/services/audiopolicy/AudioPolicyInterface.h
+++ b/services/audiopolicy/AudioPolicyInterface.h
@@ -348,6 +348,10 @@
                                      audio_io_handle_t srcOutput,
                                      audio_io_handle_t dstOutput) = 0;
 
+    virtual void setEffectSuspended(int effectId,
+                                    audio_session_t sessionId,
+                                    bool suspended) = 0;
+
     /* Create a patch between several source and sink ports */
     virtual status_t createAudioPatch(const struct audio_patch *patch,
                                        audio_patch_handle_t *handle,
diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h
index e071fe0..37f9d14 100644
--- a/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h
+++ b/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h
@@ -68,11 +68,13 @@
     bool isSourceActive(audio_source_t source) const;
     audio_source_t source() const;
     bool isSoundTrigger() const;
+    sp<RecordClientDescriptor> getHighestPriorityClient() const;
     audio_attributes_t getHighestPriorityAttributes() const;
     void setClientActive(const sp<RecordClientDescriptor>& client, bool active);
     int32_t activeCount() { return mGlobalActiveCount; }
     void trackEffectEnabled(const sp<EffectDescriptor> &effect, bool enabled);
     EffectDescriptorCollection getEnabledEffects() const;
+    EffectDescriptorCollection getActiveEffects() const; // enabled and not suspended
     // implementation of AudioIODescriptorInterface
     audio_config_base_t getConfig() const override;
     audio_patch_handle_t getPatchHandle() const override;
@@ -100,6 +102,10 @@
     // implementation of ClientMapHandler<RecordClientDescriptor>
     void addClient(const sp<RecordClientDescriptor> &client) override;
 
+    // Go over all active clients and suspend or restore effects according highest priority
+    // active use case
+    void checkSuspendEffects();
+
  private:
 
     void updateClientRecordingConfiguration(int event, const sp<RecordClientDescriptor>& client);
diff --git a/services/audiopolicy/common/managerdefinitions/include/EffectDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/EffectDescriptor.h
index c729ef9..c4eab30 100644
--- a/services/audiopolicy/common/managerdefinitions/include/EffectDescriptor.h
+++ b/services/audiopolicy/common/managerdefinitions/include/EffectDescriptor.h
@@ -29,8 +29,8 @@
 {
 public:
     EffectDescriptor(const effect_descriptor_t *desc, bool isMusicEffect,
-                     int id, int io, int session) :
-        mId(id), mIo(io), mSession(session), mEnabled(false),
+                     int id, audio_io_handle_t io, audio_session_t session) :
+        mId(id), mIo(io), mSession(session), mEnabled(false), mSuspended(false),
         mIsMusicEffect(isMusicEffect)
     {
         memcpy (&mDesc, desc, sizeof(effect_descriptor_t));
@@ -38,11 +38,12 @@
 
     void dump(String8 *dst, int spaces = 0) const;
 
-    int mId;                // effect unique ID
-    int mIo;                // io the effect is attached to
-    int mSession;               // audio session the effect is on
-    effect_descriptor_t mDesc;  // effect descriptor
-    bool mEnabled;              // enabled state: CPU load being used or not
+    int mId;                   // effect unique ID
+    audio_io_handle_t mIo;     // io the effect is attached to
+    audio_session_t mSession;  // audio session the effect is on
+    effect_descriptor_t mDesc; // effect descriptor
+    bool mEnabled;             // enabled state: CPU load being used or not
+    bool mSuspended;           // enabled but suspended by concurent capture policy
 
     bool isMusicEffect() const { return mIsMusicEffect; }
 
@@ -59,6 +60,7 @@
                             int session, int id, bool isMusicEffect);
     status_t unregisterEffect(int id);
     sp<EffectDescriptor> getEffect(int id) const;
+    EffectDescriptorCollection getEffectsForIo(audio_io_handle_t io) const;
     status_t setEffectEnabled(int id, bool enabled);
     bool     isEffectEnabled(int id) const;
     uint32_t getMaxEffectsCpuLoad() const;
diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp
index 5b4e2eb..58683be 100644
--- a/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp
@@ -128,6 +128,13 @@
 audio_attributes_t AudioInputDescriptor::getHighestPriorityAttributes() const
 {
     audio_attributes_t attributes = { .source = AUDIO_SOURCE_DEFAULT };
+    sp<RecordClientDescriptor> topClient = getHighestPriorityClient();
+    return topClient ? topClient->attributes() : attributes;
+}
+
+sp<RecordClientDescriptor> AudioInputDescriptor::getHighestPriorityClient() const
+{
+    sp<RecordClientDescriptor> topClient;
 
     for (bool activeOnly : { true, false }) {
         int32_t topPriority = -1;
@@ -139,18 +146,18 @@
             app_state_t curState = client->appState();
             if (curState >= topState) {
                 int32_t curPriority = source_priority(client->source());
-                if (curPriority > topPriority) {
-                    attributes = client->attributes();
+                if (curPriority >= topPriority) {
+                    topClient = client;
                     topPriority = curPriority;
                 }
                 topState = curState;
             }
         }
-        if (attributes.source != AUDIO_SOURCE_DEFAULT) {
+        if (topClient != nullptr) {
             break;
         }
     }
-    return attributes;
+    return topClient;
 }
 
 bool AudioInputDescriptor::isSoundTrigger() const {
@@ -326,9 +333,10 @@
 
     client->setActive(active);
 
+    checkSuspendEffects();
+
     int event = active ? RECORD_CONFIG_EVENT_START : RECORD_CONFIG_EVENT_STOP;
     updateClientRecordingConfiguration(event, client);
-
 }
 
 void AudioInputDescriptor::updateClientRecordingConfiguration(
@@ -397,44 +405,90 @@
         mEnabledEffects.replaceValueFor(effect->mId, effect);
     } else {
         mEnabledEffects.removeItem(effect->mId);
+        // always exit from suspend when disabling an effect as only enabled effects
+        // are managed by checkSuspendEffects()
+        if (effect->mSuspended) {
+            effect->mSuspended = false;
+            mClientInterface->setEffectSuspended(effect->mId, effect->mSession, effect->mSuspended);
+        }
     }
 
     RecordClientVector clients = getClientsForSession((audio_session_t)effect->mSession);
+    RecordClientVector updatedClients;
+
     for (const auto& client : clients) {
         sp<EffectDescriptor> clientEffect = client->getEnabledEffects().getEffect(effect->mId);
         bool changed = (enabled && clientEffect == nullptr)
                 || (!enabled && clientEffect != nullptr);
         client->trackEffectEnabled(effect, enabled);
         if (changed && client->active()) {
-            updateClientRecordingConfiguration(RECORD_CONFIG_EVENT_START, client);
+            updatedClients.push_back(client);
         }
     }
+
+    checkSuspendEffects();
+
+    for (const auto& client : updatedClients) {
+        updateClientRecordingConfiguration(RECORD_CONFIG_EVENT_START, client);
+    }
 }
 
 EffectDescriptorCollection AudioInputDescriptor::getEnabledEffects() const
 {
-    EffectDescriptorCollection enabledEffects;
     // report effects for highest priority active source as applied to all clients
-    RecordClientVector clients =
-        clientsList(true /*activeOnly*/, source(), false /*preferredDeviceOnly*/);
-    if (clients.size() > 0) {
-        enabledEffects = clients[0]->getEnabledEffects();
+    EffectDescriptorCollection enabledEffects;
+    sp<RecordClientDescriptor> topClient = getHighestPriorityClient();
+    if (topClient != nullptr) {
+        enabledEffects = topClient->getEnabledEffects();
     }
     return enabledEffects;
 }
 
-void AudioInputDescriptor::setAppState(uid_t uid, app_state_t state) {
+void AudioInputDescriptor::setAppState(uid_t uid, app_state_t state)
+{
     RecordClientVector clients = clientsList(false /*activeOnly*/);
+    RecordClientVector updatedClients;
 
     for (const auto& client : clients) {
         if (uid == client->uid()) {
             bool wasSilenced = client->isSilenced();
             client->setAppState(state);
             if (client->active() && wasSilenced != client->isSilenced()) {
-                updateClientRecordingConfiguration(RECORD_CONFIG_EVENT_START, client);
+                updatedClients.push_back(client);
             }
         }
     }
+
+    checkSuspendEffects();
+
+    for (const auto& client : updatedClients) {
+        updateClientRecordingConfiguration(RECORD_CONFIG_EVENT_START, client);
+    }
+}
+
+void AudioInputDescriptor::checkSuspendEffects()
+{
+    sp<RecordClientDescriptor> topClient = getHighestPriorityClient();
+    if (topClient == nullptr) {
+        return;
+    }
+
+    for (size_t i = 0; i < mEnabledEffects.size(); i++) {
+        sp<EffectDescriptor> effect = mEnabledEffects.valueAt(i);
+        if (effect->mSession == topClient->session()) {
+            if (effect->mSuspended) {
+                effect->mSuspended = false;
+                mClientInterface->setEffectSuspended(effect->mId,
+                                                     effect->mSession,
+                                                     effect->mSuspended);
+            }
+        } else if (!effect->mSuspended) {
+            effect->mSuspended = true;
+            mClientInterface->setEffectSuspended(effect->mId,
+                                                 effect->mSession,
+                                                 effect->mSuspended);
+        }
+    }
 }
 
 void AudioInputDescriptor::dump(String8 *dst) const
@@ -444,7 +498,7 @@
     dst->appendFormat(" Format: %d\n", mFormat);
     dst->appendFormat(" Channels: %08x\n", mChannelMask);
     dst->appendFormat(" Devices %s\n", mDevice->toString().c_str());
-    getEnabledEffects().dump(dst, 1 /*spaces*/, false /*verbose*/);
+    mEnabledEffects.dump(dst, 1 /*spaces*/, false /*verbose*/);
     dst->append(" AudioRecord Clients:\n");
     ClientMapHandler<RecordClientDescriptor>::dump(dst);
     dst->append("\n");
diff --git a/services/audiopolicy/common/managerdefinitions/src/EffectDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/EffectDescriptor.cpp
index 627fa8d..415962a 100644
--- a/services/audiopolicy/common/managerdefinitions/src/EffectDescriptor.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/EffectDescriptor.cpp
@@ -30,6 +30,7 @@
     dst->appendFormat("%*sSession: %d\n", spaces, "", mSession);
     dst->appendFormat("%*sName: %s\n", spaces, "",  mDesc.name);
     dst->appendFormat("%*s%s\n", spaces, "",  mEnabled ? "Enabled" : "Disabled");
+    dst->appendFormat("%*s%s\n", spaces, "",  mSuspended ? "Suspended" : "Active");
 }
 
 EffectDescriptorCollection::EffectDescriptorCollection() :
@@ -63,7 +64,8 @@
             desc->name, io, session, id);
     ALOGV("registerEffect() memory %d, total memory %d", desc->memoryUsage, mTotalEffectsMemory);
 
-    sp<EffectDescriptor> effectDesc = new EffectDescriptor(desc, isMusicEffect, id, io, session);
+    sp<EffectDescriptor> effectDesc =
+        new EffectDescriptor(desc, isMusicEffect, id, io, (audio_session_t)session);
     add(id, effectDesc);
 
     return NO_ERROR;
@@ -200,6 +202,17 @@
     }
 }
 
+EffectDescriptorCollection EffectDescriptorCollection::getEffectsForIo(audio_io_handle_t io) const
+{
+    EffectDescriptorCollection effects;
+    for (size_t i = 0; i < size(); i++) {
+        if (valueAt(i)->mIo == io) {
+            effects.add(keyAt(i), valueAt(i));
+        }
+    }
+    return effects;
+}
+
 void EffectDescriptorCollection::dump(String8 *dst, int spaces, bool verbose) const
 {
     if (verbose) {
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index 4992fd4..6bd64d6 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -2343,25 +2343,8 @@
 }
 
 void AudioPolicyManager::closeAllInputs() {
-    bool patchRemoved = false;
-
-    for (size_t input_index = 0; input_index < mInputs.size(); input_index++) {
-        sp<AudioInputDescriptor> inputDesc = mInputs.valueAt(input_index);
-        ssize_t patch_index = mAudioPatches.indexOfKey(inputDesc->getPatchHandle());
-        if (patch_index >= 0) {
-            sp<AudioPatch> patchDesc = mAudioPatches.valueAt(patch_index);
-            (void) /*status_t status*/ mpClientInterface->releaseAudioPatch(patchDesc->mAfPatchHandle, 0);
-            mAudioPatches.removeItemsAt(patch_index);
-            patchRemoved = true;
-        }
-        inputDesc->close();
-    }
-    mInputs.clear();
-    SoundTrigger::setCaptureState(false);
-    nextAudioPortGeneration();
-
-    if (patchRemoved) {
-        mpClientInterface->onAudioPatchListUpdate();
+    while (mInputs.size() != 0) {
+        closeInput(mInputs.keyAt(0));
     }
 }
 
@@ -2695,7 +2678,6 @@
     if (mEffects.getEffect(id) == nullptr) {
         return INVALID_OPERATION;
     }
-
     if (mEffects.isEffectEnabled(id)) {
         ALOGW("%s effect %d enabled", __FUNCTION__, id);
         setEffectEnabled(id, false);
@@ -2703,6 +2685,16 @@
     return mEffects.unregisterEffect(id);
 }
 
+void AudioPolicyManager::cleanUpEffectsForIo(audio_io_handle_t io)
+{
+    EffectDescriptorCollection effects = mEffects.getEffectsForIo(io);
+    for (size_t i = 0; i < effects.size(); i++) {
+        ALOGW("%s removing stale effect %s, id %d on closed IO %d",
+              __func__, effects.valueAt(i)->mDesc.name, effects.keyAt(i), io);
+        unregisterEffect(effects.keyAt(i));
+    }
+}
+
 status_t AudioPolicyManager::setEffectEnabled(int id, bool enabled)
 {
     sp<EffectDescriptor> effect = mEffects.getEffect(id);
@@ -4095,15 +4087,8 @@
 void AudioPolicyManager::setAppState(uid_t uid, app_state_t state)
 {
     ALOGV("%s(uid:%d, state:%d)", __func__, uid, state);
-
     for (size_t i = 0; i < mInputs.size(); i++) {
-        sp<AudioInputDescriptor> inputDesc = mInputs.valueAt(i);
-        RecordClientVector clients = inputDesc->clientsList(false /*activeOnly*/);
-        for (const auto& client : clients) {
-            if (uid == client->uid()) {
-                client->setAppState(state);
-            }
-        }
+        mInputs.valueAt(i)->setAppState(uid, state);
     }
 }
 
@@ -4914,6 +4899,8 @@
             setMsdPatch();
         }
     }
+
+    cleanUpEffectsForIo(output);
 }
 
 void AudioPolicyManager::closeInput(audio_io_handle_t input)
@@ -4945,6 +4932,8 @@
             mInputs.activeInputsCountOnDevices(primaryInputDevices) == 0) {
         SoundTrigger::setCaptureState(false);
     }
+
+    cleanUpEffectsForIo(input);
 }
 
 SortedVector<audio_io_handle_t> AudioPolicyManager::getOutputsForDevices(
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.h b/services/audiopolicy/managerdefault/AudioPolicyManager.h
index 43637f6..3376965 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.h
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.h
@@ -870,6 +870,8 @@
                 int delayMs,
                 uid_t uid,
                 sp<AudioPatch> *patchDescPtr);
+
+        void cleanUpEffectsForIo(audio_io_handle_t io);
 };
 
 };
diff --git a/services/audiopolicy/service/AudioPolicyClientImpl.cpp b/services/audiopolicy/service/AudioPolicyClientImpl.cpp
index 5748334..d51cc6e 100644
--- a/services/audiopolicy/service/AudioPolicyClientImpl.cpp
+++ b/services/audiopolicy/service/AudioPolicyClientImpl.cpp
@@ -172,6 +172,13 @@
     return af->moveEffects(session, src_output, dst_output);
 }
 
+void AudioPolicyService::AudioPolicyClient::setEffectSuspended(int effectId,
+                                audio_session_t sessionId,
+                                bool suspended)
+{
+    mAudioPolicyService->setEffectSuspended(effectId, sessionId, suspended);
+}
+
 status_t AudioPolicyService::AudioPolicyClient::createAudioPatch(const struct audio_patch *patch,
                                                                   audio_patch_handle_t *handle,
                                                                   int delayMs)
diff --git a/services/audiopolicy/service/AudioPolicyService.cpp b/services/audiopolicy/service/AudioPolicyService.cpp
index f63fa81..5389c08 100644
--- a/services/audiopolicy/service/AudioPolicyService.cpp
+++ b/services/audiopolicy/service/AudioPolicyService.cpp
@@ -1196,6 +1196,17 @@
                             data->mPatchHandle, data->mSource);
                     mLock.lock();
                     } break;
+                case SET_EFFECT_SUSPENDED: {
+                    SetEffectSuspendedData *data = (SetEffectSuspendedData *)command->mParam.get();
+                    ALOGV("AudioCommandThread() processing set effect suspended");
+                    sp<IAudioFlinger> af = AudioSystem::get_audio_flinger();
+                    if (af != 0) {
+                        mLock.unlock();
+                        af->setEffectSuspended(data->mEffectId, data->mSessionId, data->mSuspended);
+                        mLock.lock();
+                    }
+                    } break;
+
                 default:
                     ALOGW("AudioCommandThread() unknown command %d", command->mCommand);
                 }
@@ -1327,6 +1338,23 @@
     return sendCommand(command, delayMs);
 }
 
+void AudioPolicyService::AudioCommandThread::setEffectSuspendedCommand(int effectId,
+                                                                       audio_session_t sessionId,
+                                                                       bool suspended)
+{
+    sp<AudioCommand> command = new AudioCommand();
+    command->mCommand = SET_EFFECT_SUSPENDED;
+    sp<SetEffectSuspendedData> data = new SetEffectSuspendedData();
+    data->mEffectId = effectId;
+    data->mSessionId = sessionId;
+    data->mSuspended = suspended;
+    command->mParam = data;
+    ALOGV("AudioCommandThread() adding set suspended effectId %d sessionId %d suspended %d",
+        effectId, sessionId, suspended);
+    sendCommand(command);
+}
+
+
 void AudioPolicyService::AudioCommandThread::stopOutputCommand(audio_port_handle_t portId)
 {
     sp<AudioCommand> command = new AudioCommand();
@@ -1707,6 +1735,14 @@
     return (int)mAudioCommandThread->voiceVolumeCommand(volume, delayMs);
 }
 
+void AudioPolicyService::setEffectSuspended(int effectId,
+                                            audio_session_t sessionId,
+                                            bool suspended)
+{
+    mAudioCommandThread->setEffectSuspendedCommand(effectId, sessionId, suspended);
+}
+
+
 extern "C" {
 audio_module_handle_t aps_load_hw_module(void *service __unused,
                                              const char *name);
diff --git a/services/audiopolicy/service/AudioPolicyService.h b/services/audiopolicy/service/AudioPolicyService.h
index 0842649..6c19912 100644
--- a/services/audiopolicy/service/AudioPolicyService.h
+++ b/services/audiopolicy/service/AudioPolicyService.h
@@ -293,6 +293,9 @@
 
             void onAudioVolumeGroupChanged(volume_group_t group, int flags);
             void doOnAudioVolumeGroupChanged(volume_group_t group, int flags);
+            void setEffectSuspended(int effectId,
+                                    audio_session_t sessionId,
+                                    bool suspended);
 
 private:
                         AudioPolicyService() ANDROID_API;
@@ -427,7 +430,8 @@
             CHANGED_AUDIOVOLUMEGROUP,
             SET_AUDIOPORT_CONFIG,
             DYN_POLICY_MIX_STATE_UPDATE,
-            RECORDING_CONFIGURATION_UPDATE
+            RECORDING_CONFIGURATION_UPDATE,
+            SET_EFFECT_SUSPENDED,
         };
 
         AudioCommandThread (String8 name, const wp<AudioPolicyService>& service);
@@ -470,6 +474,9 @@
                                                     std::vector<effect_descriptor_t> effects,
                                                     audio_patch_handle_t patchHandle,
                                                     audio_source_t source);
+                    void        setEffectSuspendedCommand(int effectId,
+                                                          audio_session_t sessionId,
+                                                          bool suspended);
                     void        insertCommand_l(AudioCommand *command, int delayMs = 0);
     private:
         class AudioCommandData;
@@ -567,6 +574,13 @@
             audio_source_t mSource;
         };
 
+        class SetEffectSuspendedData : public AudioCommandData {
+        public:
+            int mEffectId;
+            audio_session_t mSessionId;
+            bool mSuspended;
+        };
+
         Mutex   mLock;
         Condition mWaitWorkCV;
         Vector < sp<AudioCommand> > mAudioCommands; // list of pending commands
@@ -652,6 +666,10 @@
                                          audio_io_handle_t srcOutput,
                                          audio_io_handle_t dstOutput);
 
+                void setEffectSuspended(int effectId,
+                                        audio_session_t sessionId,
+                                        bool suspended) override;
+
         /* Create a patch between several source and sink ports */
         virtual status_t createAudioPatch(const struct audio_patch *patch,
                                            audio_patch_handle_t *handle,
diff --git a/services/audiopolicy/tests/AudioPolicyTestClient.h b/services/audiopolicy/tests/AudioPolicyTestClient.h
index 8854eb2..e4c64e5 100644
--- a/services/audiopolicy/tests/AudioPolicyTestClient.h
+++ b/services/audiopolicy/tests/AudioPolicyTestClient.h
@@ -84,6 +84,9 @@
                                         std::vector<effect_descriptor_t> effects __unused,
                                         audio_patch_handle_t patchHandle __unused,
                                         audio_source_t source __unused) override { }
+    void setEffectSuspended(int effectId __unused,
+                            audio_session_t sessionId __unused,
+                            bool suspended __unused) {}
 };
 
 } // namespace android