Merge "Audio effects: track CPU and memory use separately"
diff --git a/include/media/AudioSystem.h b/include/media/AudioSystem.h
index eb22e32..e0d7898 100644
--- a/include/media/AudioSystem.h
+++ b/include/media/AudioSystem.h
@@ -183,6 +183,7 @@
                                     int session,
                                     int id);
     static status_t unregisterEffect(int id);
+    static status_t setEffectEnabled(int id, bool enabled);
 
     static const sp<IAudioPolicyService>& get_audio_policy_service();
 
diff --git a/include/media/IAudioPolicyService.h b/include/media/IAudioPolicyService.h
index ed265e1..9807cbe 100644
--- a/include/media/IAudioPolicyService.h
+++ b/include/media/IAudioPolicyService.h
@@ -84,6 +84,7 @@
                                     int session,
                                     int id) = 0;
     virtual status_t unregisterEffect(int id) = 0;
+    virtual status_t setEffectEnabled(int id, bool enabled) = 0;
     virtual bool     isStreamActive(int stream, uint32_t inPastMs = 0) const = 0;
     virtual status_t queryDefaultPreProcessing(int audioSession,
                                               effect_descriptor_t *descriptors,
diff --git a/media/libmedia/AudioSystem.cpp b/media/libmedia/AudioSystem.cpp
index b26ed71..bb91fa9 100644
--- a/media/libmedia/AudioSystem.cpp
+++ b/media/libmedia/AudioSystem.cpp
@@ -710,6 +710,13 @@
     return aps->unregisterEffect(id);
 }
 
+status_t AudioSystem::setEffectEnabled(int id, bool enabled)
+{
+    const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
+    if (aps == 0) return PERMISSION_DENIED;
+    return aps->setEffectEnabled(id, enabled);
+}
+
 status_t AudioSystem::isStreamActive(int stream, bool* state, uint32_t inPastMs)
 {
     const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
diff --git a/media/libmedia/IAudioPolicyService.cpp b/media/libmedia/IAudioPolicyService.cpp
index 15f4be0..50b4855 100644
--- a/media/libmedia/IAudioPolicyService.cpp
+++ b/media/libmedia/IAudioPolicyService.cpp
@@ -53,7 +53,8 @@
     UNREGISTER_EFFECT,
     IS_STREAM_ACTIVE,
     GET_DEVICES_FOR_STREAM,
-    QUERY_DEFAULT_PRE_PROCESSING
+    QUERY_DEFAULT_PRE_PROCESSING,
+    SET_EFFECT_ENABLED
 };
 
 class BpAudioPolicyService : public BpInterface<IAudioPolicyService>
@@ -313,6 +314,16 @@
         return static_cast <status_t> (reply.readInt32());
     }
 
+    virtual status_t setEffectEnabled(int id, bool enabled)
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
+        data.writeInt32(id);
+        data.writeInt32(enabled);
+        remote()->transact(SET_EFFECT_ENABLED, data, &reply);
+        return static_cast <status_t> (reply.readInt32());
+    }
+
     virtual bool isStreamActive(int stream, uint32_t inPastMs) const
     {
         Parcel data, reply;
@@ -577,6 +588,14 @@
             return NO_ERROR;
         } break;
 
+        case SET_EFFECT_ENABLED: {
+            CHECK_INTERFACE(IAudioPolicyService, data, reply);
+            int id = data.readInt32();
+            bool enabled = static_cast <bool>(data.readInt32());
+            reply->writeInt32(static_cast <int32_t>(setEffectEnabled(id, enabled)));
+            return NO_ERROR;
+        } break;
+
         case IS_STREAM_ACTIVE: {
             CHECK_INTERFACE(IAudioPolicyService, data, reply);
             int stream = data.readInt32();
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index d6bfda6..95c469d 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -1232,18 +1232,6 @@
     }
 }
 
-void AudioFlinger::ThreadBase::updateSuspendedSessionsOnRemoveEffectChain_l(
-        const sp<EffectChain>& chain)
-{
-    int index = mSuspendedSessions.indexOfKey(chain->sessionId());
-    if (index < 0) {
-        return;
-    }
-    LOGV("updateSuspendedSessionsOnRemoveEffectChain_l() removed suspended session %d",
-         chain->sessionId());
-    mSuspendedSessions.removeItemsAt(index);
-}
-
 void AudioFlinger::ThreadBase::updateSuspendedSessions_l(const effect_uuid_t *type,
                                                          bool suspend,
                                                          int sessionId)
@@ -1311,7 +1299,14 @@
 {
     Mutex::Autolock _l(mLock);
 
-    // TODO: implement PlaybackThread or RecordThread specific behavior here
+    if (mType != RECORD) {
+        // suspend all effects in AUDIO_SESSION_OUTPUT_MIX when enabling any effect on
+        // another session. This gives the priority to well behaved effect control panels
+        // and applications not using global effects.
+        if (sessionId != AUDIO_SESSION_OUTPUT_MIX) {
+            setEffectSuspended_l(NULL, enabled, AUDIO_SESSION_OUTPUT_MIX);
+        }
+    }
 
     sp<EffectChain> chain = getEffectChain_l(sessionId);
     if (chain != 0) {
@@ -5847,7 +5842,6 @@
 
     for (size_t i = 0; i < mEffectChains.size(); i++) {
         if (chain == mEffectChains[i]) {
-            updateSuspendedSessionsOnRemoveEffectChain_l(chain);
             mEffectChains.removeAt(i);
             // detach all active tracks from the chain
             for (size_t i = 0 ; i < mActiveTracks.size() ; ++i) {
@@ -5939,7 +5933,6 @@
             "removeEffectChain_l() %p invalid chain size %d on thread %p",
             chain.get(), mEffectChains.size(), this);
     if (mEffectChains.size() == 1) {
-        updateSuspendedSessionsOnRemoveEffectChain_l(chain);
         mEffectChains.removeAt(0);
     }
     return 0;
@@ -6393,10 +6386,16 @@
 
 status_t AudioFlinger::EffectModule::setEnabled(bool enabled)
 {
+
     Mutex::Autolock _l(mLock);
     LOGV("setEnabled %p enabled %d", this, enabled);
 
     if (enabled != isEnabled()) {
+        status_t status = AudioSystem::setEffectEnabled(mId, enabled);
+        if (enabled && status != NO_ERROR) {
+            return status;
+        }
+
         switch (mState) {
         // going from disabled to enabled
         case IDLE:
@@ -6704,6 +6703,10 @@
     if (!mHasControl) return INVALID_OPERATION;
     if (mEffect == 0) return DEAD_OBJECT;
 
+    if (mEnabled) {
+        return NO_ERROR;
+    }
+
     mEnabled = true;
 
     sp<ThreadBase> thread = mEffect->thread().promote();
@@ -6716,7 +6719,14 @@
         return NO_ERROR;
     }
 
-    return mEffect->setEnabled(true);
+    status_t status = mEffect->setEnabled(true);
+    if (status != NO_ERROR) {
+        if (thread != 0) {
+            thread->checkSuspendOnEffectEnabled(mEffect, false, mEffect->sessionId());
+        }
+        mEnabled = false;
+    }
+    return status;
 }
 
 status_t AudioFlinger::EffectHandle::disable()
@@ -6725,6 +6735,9 @@
     if (!mHasControl) return INVALID_OPERATION;
     if (mEffect == 0) return DEAD_OBJECT;
 
+    if (!mEnabled) {
+        return NO_ERROR;
+    }
     mEnabled = false;
 
     if (mEffect->suspended()) {
@@ -6754,9 +6767,11 @@
     }
     mEffect->disconnect(this, unpiniflast);
 
-    sp<ThreadBase> thread = mEffect->thread().promote();
-    if (thread != 0) {
-        thread->checkSuspendOnEffectEnabled(mEffect, false, mEffect->sessionId());
+    if (mEnabled) {
+        sp<ThreadBase> thread = mEffect->thread().promote();
+        if (thread != 0) {
+            thread->checkSuspendOnEffectEnabled(mEffect, false, mEffect->sessionId());
+        }
     }
 
     // release sp on module => module destructor can be called now
@@ -7367,15 +7382,22 @@
     }
 }
 
+bool AudioFlinger::EffectChain::isEffectEligibleForSuspend(const effect_descriptor_t& desc)
+{
+    // auxiliary effects and visualizer are never suspended on output mix
+    if ((mSessionId == AUDIO_SESSION_OUTPUT_MIX) &&
+        (((desc.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY) ||
+         (memcmp(&desc.type, SL_IID_VISUALIZATION, sizeof(effect_uuid_t)) == 0))) {
+        return false;
+    }
+    return true;
+}
+
 Vector< sp<AudioFlinger::EffectModule> > AudioFlinger::EffectChain::getSuspendEligibleEffects()
 {
     Vector< sp<EffectModule> > effects;
     for (size_t i = 0; i < mEffects.size(); i++) {
-        effect_descriptor_t desc = mEffects[i]->desc();
-        // auxiliary effects and vizualizer are never suspended on output mix
-        if ((mSessionId == AUDIO_SESSION_OUTPUT_MIX) && (
-            ((desc.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY) ||
-             (memcmp(&desc.type, SL_IID_VISUALIZATION, sizeof(effect_uuid_t)) == 0))) {
+        if (!isEffectEligibleForSuspend(mEffects[i]->desc())) {
             continue;
         }
         effects.add(mEffects[i]);
@@ -7405,8 +7427,15 @@
             if (index < 0) {
                 return;
             }
+            if (!isEffectEligibleForSuspend(effect->desc())) {
+                return;
+            }
             setEffectSuspended_l(&effect->desc().type, enabled);
             index = mSuspendedEffects.indexOfKey(effect->desc().type.timeLow);
+            if (index < 0) {
+                LOGW("checkSuspendOnEffectEnabled() Fx should be suspended here!");
+                return;
+            }
         }
         LOGV("checkSuspendOnEffectEnabled() enable suspending fx %08x",
              effect->desc().type.timeLow);
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index 3a0aac9..1141f6c 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -522,8 +522,6 @@
                                                           int sessionId);
                     // check if some effects must be suspended when an effect chain is added
                     void checkSuspendOnAddEffectChain_l(const sp<EffectChain>& chain);
-                    // updated mSuspendedSessions when an effect chain is removed
-                    void updateSuspendedSessionsOnRemoveEffectChain_l(const sp<EffectChain>& chain);
 
         friend class AudioFlinger;
         friend class Track;
@@ -1320,6 +1318,10 @@
         Vector< sp<EffectModule> > getSuspendEligibleEffects();
         // get an effect module if it is currently enable
         sp<EffectModule> getEffectIfEnabled(const effect_uuid_t *type);
+        // true if the effect whose descriptor is passed can be suspended
+        // OEMs can modify the rules implemented in this method to exclude specific effect
+        // types or implementations from the suspend/restore mechanism.
+        bool isEffectEligibleForSuspend(const effect_descriptor_t& desc);
 
         wp<ThreadBase> mThread;     // parent mixer thread
         Mutex mLock;                // mutex protecting effect list
diff --git a/services/audioflinger/AudioPolicyService.cpp b/services/audioflinger/AudioPolicyService.cpp
index 6d06d83..d747b5ad 100644
--- a/services/audioflinger/AudioPolicyService.cpp
+++ b/services/audioflinger/AudioPolicyService.cpp
@@ -488,6 +488,14 @@
     return mpAudioPolicy->unregister_effect(mpAudioPolicy, id);
 }
 
+status_t AudioPolicyService::setEffectEnabled(int id, bool enabled)
+{
+    if (mpAudioPolicy == NULL) {
+        return NO_INIT;
+    }
+    return mpAudioPolicy->set_effect_enabled(mpAudioPolicy, id, enabled);
+}
+
 bool AudioPolicyService::isStreamActive(int stream, uint32_t inPastMs) const
 {
     if (mpAudioPolicy == NULL) {
diff --git a/services/audioflinger/AudioPolicyService.h b/services/audioflinger/AudioPolicyService.h
index 834b794..d898a53 100644
--- a/services/audioflinger/AudioPolicyService.h
+++ b/services/audioflinger/AudioPolicyService.h
@@ -102,6 +102,7 @@
                                     int session,
                                     int id);
     virtual status_t unregisterEffect(int id);
+    virtual status_t setEffectEnabled(int id, bool enabled);
     virtual bool isStreamActive(int stream, uint32_t inPastMs = 0) const;
 
     virtual status_t queryDefaultPreProcessing(int audioSession,