Audio policy manager changes for audio effects

Added methods for audio effects management by audio policy manager.
- control of total CPU load and memory used by effect engines
- selection of output stream for global effects
- added audio session id in parameter list for startOutput() and stopOutput().
this is not used in default audio policy manager implementation.

Modifications of audio effect framework in AudioFlinger to allow moving and reconfiguring
effect engines from one output mixer thread to another when audio tracks in the same session
are moved or when requested by audio policy manager.
Also fixed mutex deadlock problem with effect chains locks.

Change-Id: Ida43484b06e9b890d6b9e53c13958d042720ebdb
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index b38a5c8..b88e69d 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -63,6 +63,8 @@
 
 // ----------------------------------------------------------------------------
 
+extern const char * const gEffectLibPath;
+
 namespace android {
 
 static const char* kDeadlockedString = "AudioFlinger may be deadlocked\n";
@@ -127,8 +129,7 @@
 
 AudioFlinger::AudioFlinger()
     : BnAudioFlinger(),
-        mAudioHardware(0), mMasterVolume(1.0f), mMasterMute(false), mNextUniqueId(1),
-        mTotalEffectsCpuLoad(0), mTotalEffectsMemory(0)
+        mAudioHardware(0), mMasterVolume(1.0f), mMasterMute(false), mNextUniqueId(1)
 {
     mHardwareStatus = AUDIO_HW_IDLE;
 
@@ -321,13 +322,19 @@
             mClients.add(pid, client);
         }
 
-        // If no audio session id is provided, create one here
-        // TODO: enforce same stream type for all tracks in same audio session?
-        // TODO: prevent same audio session on different output threads
         LOGV("createTrack() sessionId: %d", (sessionId == NULL) ? -2 : *sessionId);
-        if (sessionId != NULL && *sessionId != 0) {
+        if (sessionId != NULL && *sessionId != AudioSystem::SESSION_OUTPUT_MIX) {
+            // prevent same audio session on different output threads
+            for (size_t i = 0; i < mPlaybackThreads.size(); i++) {
+                if (mPlaybackThreads.keyAt(i) != output &&
+                        mPlaybackThreads.valueAt(i)->hasAudioSession(*sessionId)) {
+                    lStatus = BAD_VALUE;
+                    goto Exit;
+                }
+            }
             lSessionId = *sessionId;
         } else {
+            // if no audio session id is provided, create one here
             lSessionId = nextUniqueId();
             if (sessionId != NULL) {
                 *sessionId = lSessionId;
@@ -1141,6 +1148,23 @@
 
     { // scope for mLock
         Mutex::Autolock _l(mLock);
+
+        // all tracks in same audio session must share the same routing strategy otherwise
+        // conflicts will happen when tracks are moved from one output to another by audio policy
+        // manager
+        uint32_t strategy =
+                AudioSystem::getStrategyForStream((AudioSystem::stream_type)streamType);
+        for (size_t i = 0; i < mTracks.size(); ++i) {
+            sp<Track> t = mTracks[i];
+            if (t != 0) {
+                if (sessionId == t->sessionId() &&
+                        strategy != AudioSystem::getStrategyForStream((AudioSystem::stream_type)t->type())) {
+                    lStatus = BAD_VALUE;
+                    goto Exit;
+                }
+            }
+        }
+
         track = new Track(this, client, streamType, sampleRate, format,
                 channelCount, frameCount, sharedBuffer, sessionId);
         if (track->getCblk() == NULL || track->name() < 0) {
@@ -1153,6 +1177,7 @@
         if (chain != 0) {
             LOGV("createTrack_l() setting main buffer %p", chain->inBuffer());
             track->setMainBuffer(chain->inBuffer());
+            chain->setStrategy(AudioSystem::getStrategyForStream((AudioSystem::stream_type)track->type()));
         }
     }
     lStatus = NO_ERROR;
@@ -1344,7 +1369,16 @@
     mMixBuffer = new int16_t[mFrameCount * 2];
     memset(mMixBuffer, 0, mFrameCount * 2 * sizeof(int16_t));
 
-    //TODO handle effects reconfig
+    // force reconfiguration of effect chains and engines to take new buffer size and audio
+    // parameters into account
+    // Note that mLock is not held when readOutputParameters() is called from the constructor
+    // but in this case nothing is done below as no audio sessions have effect yet so it doesn't
+    // matter.
+    // create a copy of mEffectChains as calling moveEffectChain_l() can reorder some effect chains
+    Vector< sp<EffectChain> > effectChains = mEffectChains;
+    for (size_t i = 0; i < effectChains.size(); i ++) {
+        mAudioFlinger->moveEffectChain_l(effectChains[i]->sessionId(), this, this);
+    }
 }
 
 status_t AudioFlinger::PlaybackThread::getRenderPosition(uint32_t *halFrames, uint32_t *dspFrames)
@@ -1369,7 +1403,8 @@
 
     for (size_t i = 0; i < mTracks.size(); ++i) {
         sp<Track> track = mTracks[i];
-        if (sessionId == track->sessionId()) {
+        if (sessionId == track->sessionId() &&
+                !(track->mCblk->flags & CBLK_INVALID_MSK)) {
             return true;
         }
     }
@@ -1377,6 +1412,23 @@
     return false;
 }
 
+uint32_t AudioFlinger::PlaybackThread::getStrategyForSession_l(int sessionId)
+{
+    // session AudioSystem::SESSION_OUTPUT_MIX is placed in same strategy as MUSIC stream so that
+    // it is moved to correct output by audio policy manager when A2DP is connected or disconnected
+    if (sessionId == AudioSystem::SESSION_OUTPUT_MIX) {
+        return AudioSystem::getStrategyForStream(AudioSystem::MUSIC);
+    }
+    for (size_t i = 0; i < mTracks.size(); i++) {
+        sp<Track> track = mTracks[i];
+        if (sessionId == track->sessionId() &&
+                !(track->mCblk->flags & CBLK_INVALID_MSK)) {
+            return AudioSystem::getStrategyForStream((AudioSystem::stream_type) track->type());
+        }
+    }
+    return AudioSystem::getStrategyForStream(AudioSystem::MUSIC);
+}
+
 sp<AudioFlinger::EffectChain> AudioFlinger::PlaybackThread::getEffectChain(int sessionId)
 {
     Mutex::Autolock _l(mLock);
@@ -1503,8 +1555,7 @@
             // prevent any changes in effect chain list and in each effect chain
             // during mixing and effect process as the audio buffers could be deleted
             // or modified if an effect is created or deleted
-            lockEffectChains_l();
-            effectChains = mEffectChains;
+            lockEffectChains_l(effectChains);
        }
 
         if (LIKELY(mixerStatus == MIXER_TRACKS_READY)) {
@@ -1540,7 +1591,7 @@
                  effectChains[i]->process_l();
              }
              // enable changes in effect chain
-             unlockEffectChains();
+             unlockEffectChains(effectChains);
 #ifdef LVMX
             int audioOutputType = LifeVibes::getMixerType(mId, mType);
             if (LifeVibes::audioOutputTypeIsLifeVibes(audioOutputType)) {
@@ -1571,7 +1622,7 @@
             mStandby = false;
         } else {
             // enable changes in effect chain
-            unlockEffectChains();
+            unlockEffectChains(effectChains);
             usleep(sleepTime);
         }
 
@@ -1625,7 +1676,7 @@
     }
 #endif
     // Delegate master volume control to effect in output mix effect chain if needed
-    sp<EffectChain> chain = getEffectChain_l(0);
+    sp<EffectChain> chain = getEffectChain_l(AudioSystem::SESSION_OUTPUT_MIX);
     if (chain != 0) {
         uint32_t v = (uint32_t)(masterVolume * (1 << 24));
         chain->setVolume_l(&v, &v);
@@ -1814,8 +1865,10 @@
 
 void AudioFlinger::MixerThread::invalidateTracks(int streamType)
 {
-    LOGV ("MixerThread::invalidateTracks() mixer %p, streamType %d, mTracks.size %d", this,  streamType, mTracks.size());
+    LOGV ("MixerThread::invalidateTracks() mixer %p, streamType %d, mTracks.size %d",
+            this,  streamType, mTracks.size());
     Mutex::Autolock _l(mLock);
+
     size_t size = mTracks.size();
     for (size_t i = 0; i < size; i++) {
         sp<Track> t = mTracks[i];
@@ -2070,7 +2123,6 @@
     // hardware resources as soon as possible
     nsecs_t standbyDelay = microseconds(activeSleepTime*2);
 
-
     while (!exitPending())
     {
         bool rampVolume;
@@ -2246,7 +2298,8 @@
             if (UNLIKELY(trackToRemove != 0)) {
                 mActiveTracks.remove(trackToRemove);
                 if (!effectChains.isEmpty()) {
-                    LOGV("stopping track on chain %p for session Id: %d", effectChains[0].get(), trackToRemove->sessionId());
+                    LOGV("stopping track on chain %p for session Id: %d", effectChains[0].get(),
+                            trackToRemove->sessionId());
                     effectChains[0]->stopTrack();
                 }
                 if (trackToRemove->isTerminated()) {
@@ -2255,7 +2308,7 @@
                 }
             }
 
-            lockEffectChains_l();
+            lockEffectChains_l(effectChains);
        }
 
         if (LIKELY(mixerStatus == MIXER_TRACKS_READY)) {
@@ -2301,7 +2354,7 @@
             for (size_t i = 0; i < effectChains.size(); i ++) {
                 effectChains[i]->process_l();
             }
-            unlockEffectChains();
+            unlockEffectChains(effectChains);
 
             mLastWriteTime = systemTime();
             mInWrite = true;
@@ -2312,7 +2365,7 @@
             mInWrite = false;
             mStandby = false;
         } else {
-            unlockEffectChains();
+            unlockEffectChains(effectChains);
             usleep(sleepTime);
         }
 
@@ -2505,8 +2558,7 @@
             // prevent any changes in effect chain list and in each effect chain
             // during mixing and effect process as the audio buffers could be deleted
             // or modified if an effect is created or deleted
-            lockEffectChains_l();
-            effectChains = mEffectChains;
+            lockEffectChains_l(effectChains);
         }
 
         if (LIKELY(mixerStatus == MIXER_TRACKS_READY)) {
@@ -2547,7 +2599,7 @@
                 effectChains[i]->process_l();
             }
             // enable changes in effect chain
-            unlockEffectChains();
+            unlockEffectChains(effectChains);
 
             standbyTime = systemTime() + kStandbyTimeInNsecs;
             for (size_t i = 0; i < outputTracks.size(); i++) {
@@ -2557,7 +2609,7 @@
             mBytesWritten += mixBufferSize;
         } else {
             // enable changes in effect chain
-            unlockEffectChains();
+            unlockEffectChains(effectChains);
             usleep(sleepTime);
         }
 
@@ -2859,7 +2911,9 @@
         if (thread != 0) {
             if (!isOutputTrack()) {
                 if (mState == ACTIVE || mState == RESUMING) {
-                    AudioSystem::stopOutput(thread->id(), (AudioSystem::stream_type)mStreamType);
+                    AudioSystem::stopOutput(thread->id(),
+                                            (AudioSystem::stream_type)mStreamType,
+                                            mSessionId);
                 }
                 AudioSystem::releaseOutput(thread->id());
             }
@@ -2966,7 +3020,9 @@
 
         if (!isOutputTrack() && state != ACTIVE && state != RESUMING) {
             thread->mLock.unlock();
-            status = AudioSystem::startOutput(thread->id(), (AudioSystem::stream_type)mStreamType);
+            status = AudioSystem::startOutput(thread->id(),
+                                              (AudioSystem::stream_type)mStreamType,
+                                              mSessionId);
             thread->mLock.lock();
         }
         if (status == NO_ERROR) {
@@ -2999,7 +3055,9 @@
         }
         if (!isOutputTrack() && (state == ACTIVE || state == RESUMING)) {
             thread->mLock.unlock();
-            AudioSystem::stopOutput(thread->id(), (AudioSystem::stream_type)mStreamType);
+            AudioSystem::stopOutput(thread->id(),
+                                    (AudioSystem::stream_type)mStreamType,
+                                    mSessionId);
             thread->mLock.lock();
         }
     }
@@ -3016,7 +3074,9 @@
             LOGV("ACTIVE/RESUMING => PAUSING (%d) on thread %p", mName, thread.get());
             if (!isOutputTrack()) {
                 thread->mLock.unlock();
-                AudioSystem::stopOutput(thread->id(), (AudioSystem::stream_type)mStreamType);
+                AudioSystem::stopOutput(thread->id(),
+                                        (AudioSystem::stream_type)mStreamType,
+                                        mSessionId);
                 thread->mLock.lock();
             }
         }
@@ -3585,7 +3645,7 @@
         }
 
         // If no audio session id is provided, create one here
-        if (sessionId != NULL && *sessionId != 0) {
+        if (sessionId != NULL && *sessionId != AudioSystem::SESSION_OUTPUT_MIX) {
             lSessionId = *sessionId;
         } else {
             lSessionId = nextUniqueId();
@@ -4416,8 +4476,8 @@
             thread->type() != PlaybackThread::DIRECT) {
             MixerThread *srcThread = (MixerThread *)thread;
             srcThread->invalidateTracks(stream);
-            }
         }
+    }
 
     return NO_ERROR;
 }
@@ -4472,12 +4532,26 @@
 
 status_t AudioFlinger::loadEffectLibrary(const char *libPath, int *handle)
 {
+    // check calling permissions
+    if (!settingsAllowed()) {
+        return PERMISSION_DENIED;
+    }
+    // only allow libraries loaded from /system/lib/soundfx for now
+    if (strncmp(gEffectLibPath, libPath, strlen(gEffectLibPath)) != 0) {
+        return PERMISSION_DENIED;
+    }
+
     Mutex::Autolock _l(mLock);
     return EffectLoadLibrary(libPath, handle);
 }
 
 status_t AudioFlinger::unloadEffectLibrary(int handle)
 {
+    // check calling permissions
+    if (!settingsAllowed()) {
+        return PERMISSION_DENIED;
+    }
+
     Mutex::Autolock _l(mLock);
     return EffectUnloadLibrary(handle);
 }
@@ -4522,7 +4596,8 @@
     sp<Client> client;
     wp<Client> wclient;
 
-    LOGV("createEffect pid %d, client %p, priority %d, sessionId %d, output %d", pid, effectClient.get(), priority, sessionId, output);
+    LOGV("createEffect pid %d, client %p, priority %d, sessionId %d, output %d",
+            pid, effectClient.get(), priority, sessionId, output);
 
     if (pDesc == NULL) {
         lStatus = BAD_VALUE;
@@ -4577,7 +4652,7 @@
                     // an auxiliary version of this effect type is available
                     found = true;
                     memcpy(&d, &desc, sizeof(effect_descriptor_t));
-                    if (sessionId != 0 ||
+                    if (sessionId != AudioSystem::SESSION_OUTPUT_MIX ||
                             (desc.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY) {
                         break;
                     }
@@ -4590,22 +4665,23 @@
             }
             // For same effect type, chose auxiliary version over insert version if
             // connect to output mix (Compliance to OpenSL ES)
-            if (sessionId == 0 &&
+            if (sessionId == AudioSystem::SESSION_OUTPUT_MIX &&
                     (d.flags & EFFECT_FLAG_TYPE_MASK) != EFFECT_FLAG_TYPE_AUXILIARY) {
                 memcpy(&desc, &d, sizeof(effect_descriptor_t));
             }
         }
 
         // Do not allow auxiliary effects on a session different from 0 (output mix)
-        if (sessionId != 0 &&
+        if (sessionId != AudioSystem::SESSION_OUTPUT_MIX &&
              (desc.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY) {
             lStatus = INVALID_OPERATION;
             goto Exit;
         }
 
-        // Session -1 is reserved for output stage effects that can only be created
-        // by audio policy manager (running in same process)
-        if (sessionId == -1 && getpid() != IPCThreadState::self()->getCallingPid()) {
+        // Session AudioSystem::SESSION_OUTPUT_STAGE is reserved for output stage effects
+        // that can only be created by audio policy manager (running in same process)
+        if (sessionId == AudioSystem::SESSION_OUTPUT_STAGE &&
+                getpid() != IPCThreadState::self()->getCallingPid()) {
             lStatus = INVALID_OPERATION;
             goto Exit;
         }
@@ -4617,13 +4693,14 @@
         // output threads.
         // TODO: allow attachment of effect to inputs
         if (output == 0) {
-            if (sessionId <= 0) {
-                // default to first output
-                // TODO: define criteria to choose output when not specified. Or
-                // receive output from audio policy manager
-                if (mPlaybackThreads.size() != 0) {
-                    output = mPlaybackThreads.keyAt(0);
-                }
+            if (sessionId == AudioSystem::SESSION_OUTPUT_STAGE) {
+                // output must be specified by AudioPolicyManager when using session
+                // AudioSystem::SESSION_OUTPUT_STAGE
+                lStatus = BAD_VALUE;
+                goto Exit;
+            } else if (sessionId == AudioSystem::SESSION_OUTPUT_MIX) {
+                output = AudioSystem::getOutputForEffect(&desc);
+                LOGV("createEffect() got output %d for effect %s", output, desc.name);
             } else {
                  // look for the thread where the specified audio session is present
                 for (size_t i = 0; i < mPlaybackThreads.size(); i++) {
@@ -4636,7 +4713,7 @@
         }
         PlaybackThread *thread = checkPlaybackThread_l(output);
         if (thread == NULL) {
-            LOGE("unknown output thread");
+            LOGE("createEffect() unknown output thread");
             lStatus = BAD_VALUE;
             goto Exit;
         }
@@ -4651,7 +4728,8 @@
         }
 
         // create effect on selected output trhead
-        handle = thread->createEffect_l(client, effectClient, priority, sessionId, &desc, enabled, &lStatus);
+        handle = thread->createEffect_l(client, effectClient, priority, sessionId,
+                &desc, enabled, &lStatus);
         if (handle != 0 && id != NULL) {
             *id = handle->id();
         }
@@ -4664,31 +4742,64 @@
     return handle;
 }
 
-status_t AudioFlinger::registerEffectResource_l(effect_descriptor_t *desc) {
-    if (mTotalEffectsCpuLoad + desc->cpuLoad > MAX_EFFECTS_CPU_LOAD) {
-        LOGW("registerEffectResource() CPU Load limit exceeded for Fx %s, CPU %f MIPS",
-                desc->name, (float)desc->cpuLoad/10);
-        return INVALID_OPERATION;
+status_t AudioFlinger::moveEffects(int session, int srcOutput, int dstOutput)
+{
+    LOGV("moveEffects() session %d, srcOutput %d, dstOutput %d",
+            session, srcOutput, dstOutput);
+    Mutex::Autolock _l(mLock);
+    if (srcOutput == dstOutput) {
+        LOGW("moveEffects() same dst and src outputs %d", dstOutput);
+        return NO_ERROR;
     }
-    if (mTotalEffectsMemory + desc->memoryUsage > MAX_EFFECTS_MEMORY) {
-        LOGW("registerEffectResource() memory limit exceeded for Fx %s, Memory %d KB",
-                desc->name, desc->memoryUsage);
-        return INVALID_OPERATION;
+    PlaybackThread *srcThread = checkPlaybackThread_l(srcOutput);
+    if (srcThread == NULL) {
+        LOGW("moveEffects() bad srcOutput %d", srcOutput);
+        return BAD_VALUE;
     }
-    mTotalEffectsCpuLoad += desc->cpuLoad;
-    mTotalEffectsMemory += desc->memoryUsage;
-    LOGV("registerEffectResource_l() effect %s, CPU %d, memory %d",
-            desc->name, desc->cpuLoad, desc->memoryUsage);
-    LOGV("  total CPU %d, total memory %d", mTotalEffectsCpuLoad, mTotalEffectsMemory);
+    PlaybackThread *dstThread = checkPlaybackThread_l(dstOutput);
+    if (dstThread == NULL) {
+        LOGW("moveEffects() bad dstOutput %d", dstOutput);
+        return BAD_VALUE;
+    }
+
+    Mutex::Autolock _dl(dstThread->mLock);
+    Mutex::Autolock _sl(srcThread->mLock);
+    moveEffectChain_l(session, srcThread, dstThread);
+
     return NO_ERROR;
 }
 
-void AudioFlinger::unregisterEffectResource_l(effect_descriptor_t *desc) {
-    mTotalEffectsCpuLoad -= desc->cpuLoad;
-    mTotalEffectsMemory -= desc->memoryUsage;
-    LOGV("unregisterEffectResource_l() effect %s, CPU %d, memory %d",
-            desc->name, desc->cpuLoad, desc->memoryUsage);
-    LOGV("  total CPU %d, total memory %d", mTotalEffectsCpuLoad, mTotalEffectsMemory);
+// moveEffectChain_l mustbe called with both srcThread and dstThread mLocks held
+status_t AudioFlinger::moveEffectChain_l(int session,
+                                   AudioFlinger::PlaybackThread *srcThread,
+                                   AudioFlinger::PlaybackThread *dstThread)
+{
+    LOGV("moveEffectChain_l() session %d from thread %p to thread %p",
+            session, srcThread, dstThread);
+
+    sp<EffectChain> chain = srcThread->getEffectChain_l(session);
+    if (chain == 0) {
+        LOGW("moveEffectChain_l() effect chain for session %d not on source thread %p",
+                session, srcThread);
+        return INVALID_OPERATION;
+    }
+
+    // remove chain first. This is usefull only if reconfiguring effect chain on same output thread,
+    // so that a new chain is created with correct parameters when first effect is added. This is
+    // otherwise unecessary as removeEffect_l() will remove the chain when last effect is
+    // removed.
+    srcThread->removeEffectChain_l(chain);
+
+    // transfer all effects one by one so that new effect chain is created on new thread with
+    // correct buffer sizes and audio parameters and effect engines reconfigured accordingly
+    sp<EffectModule> effect = chain->getEffectFromId_l(0);
+    while (effect != 0) {
+        srcThread->removeEffect_l(effect);
+        dstThread->addEffect_l(effect);
+        effect = chain->getEffectFromId_l(0);
+    }
+
+    return NO_ERROR;
 }
 
 // PlaybackThread::createEffect_l() must be called with AudioFlinger::mLock held
@@ -4707,6 +4818,7 @@
     status_t lStatus;
     sp<Track> track;
     sp<EffectChain> chain;
+    bool chainCreated = false;
     bool effectCreated = false;
     bool effectRegistered = false;
 
@@ -4718,16 +4830,18 @@
 
     // Do not allow auxiliary effect on session other than 0
     if ((desc->flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY &&
-        sessionId != 0) {
-        LOGW("createEffect_l() Cannot add auxiliary effect %s to session %d", desc->name, sessionId);
+        sessionId != AudioSystem::SESSION_OUTPUT_MIX) {
+        LOGW("createEffect_l() Cannot add auxiliary effect %s to session %d",
+                desc->name, sessionId);
         lStatus = BAD_VALUE;
         goto Exit;
     }
 
     // Do not allow effects with session ID 0 on direct output or duplicating threads
     // TODO: add rule for hw accelerated effects on direct outputs with non PCM format
-    if (sessionId == 0 && mType != MIXER) {
-        LOGW("createEffect_l() Cannot add auxiliary effect %s to session %d", desc->name, sessionId);
+    if (sessionId == AudioSystem::SESSION_OUTPUT_MIX && mType != MIXER) {
+        LOGW("createEffect_l() Cannot add auxiliary effect %s to session %d",
+                desc->name, sessionId);
         lStatus = BAD_VALUE;
         goto Exit;
     }
@@ -4744,6 +4858,8 @@
             LOGV("createEffect_l() new effect chain for session %d", sessionId);
             chain = new EffectChain(this, sessionId);
             addEffectChain_l(chain);
+            chain->setStrategy(getStrategyForSession_l(sessionId));
+            chainCreated = true;
         } else {
             effect = chain->getEffectFromDesc_l(desc);
         }
@@ -4751,14 +4867,15 @@
         LOGV("createEffect_l() got effect %p on chain %p", effect == 0 ? 0 : effect.get(), chain.get());
 
         if (effect == 0) {
+            int id = mAudioFlinger->nextUniqueId();
             // Check CPU and memory usage
-            lStatus = mAudioFlinger->registerEffectResource_l(desc);
+            lStatus = AudioSystem::registerEffect(desc, mId, chain->strategy(), sessionId, id);
             if (lStatus != NO_ERROR) {
                 goto Exit;
             }
             effectRegistered = true;
             // create a new effect module if none present in the chain
-            effect = new EffectModule(this, chain, desc, mAudioFlinger->nextUniqueId(), sessionId);
+            effect = new EffectModule(this, chain, desc, id, sessionId);
             lStatus = effect->status();
             if (lStatus != NO_ERROR) {
                 goto Exit;
@@ -4782,14 +4899,15 @@
 
 Exit:
     if (lStatus != NO_ERROR && lStatus != ALREADY_EXISTS) {
+        Mutex::Autolock _l(mLock);
         if (effectCreated) {
-            Mutex::Autolock _l(mLock);
-            if (chain->removeEffect_l(effect) == 0) {
-                removeEffectChain_l(chain);
-            }
+            chain->removeEffect_l(effect);
         }
         if (effectRegistered) {
-            mAudioFlinger->unregisterEffectResource_l(desc);
+            AudioSystem::unregisterEffect(effect->id());
+        }
+        if (chainCreated) {
+            removeEffectChain_l(chain);
         }
         handle.clear();
     }
@@ -4800,26 +4918,71 @@
     return handle;
 }
 
-void AudioFlinger::PlaybackThread::disconnectEffect(const sp< EffectModule>& effect,
-                                                    const wp<EffectHandle>& handle) {
+// PlaybackThread::addEffect_l() must be called with AudioFlinger::mLock and
+// PlaybackThread::mLock held
+status_t AudioFlinger::PlaybackThread::addEffect_l(const sp<EffectModule>& effect)
+{
+    // check for existing effect chain with the requested audio session
+    int sessionId = effect->sessionId();
+    sp<EffectChain> chain = getEffectChain_l(sessionId);
+    bool chainCreated = false;
+
+    if (chain == 0) {
+        // create a new chain for this session
+        LOGV("addEffect_l() new effect chain for session %d", sessionId);
+        chain = new EffectChain(this, sessionId);
+        addEffectChain_l(chain);
+        chain->setStrategy(getStrategyForSession_l(sessionId));
+        chainCreated = true;
+    }
+    LOGV("addEffect_l() %p chain %p effect %p", this, chain.get(), effect.get());
+
+    if (chain->getEffectFromId_l(effect->id()) != 0) {
+        LOGW("addEffect_l() %p effect %s already present in chain %p",
+                this, effect->desc().name, chain.get());
+        return BAD_VALUE;
+    }
+
+    status_t status = chain->addEffect_l(effect);
+    if (status != NO_ERROR) {
+        if (chainCreated) {
+            removeEffectChain_l(chain);
+        }
+        return status;
+    }
+
+    effect->setDevice(mDevice);
+    effect->setMode(mAudioFlinger->getMode());
+    return NO_ERROR;
+}
+
+void AudioFlinger::PlaybackThread::removeEffect_l(const sp<EffectModule>& effect) {
+
+    LOGV("removeEffect_l() %p effect %p", this, effect.get());
     effect_descriptor_t desc = effect->desc();
+    if ((desc.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY) {
+        detachAuxEffect_l(effect->id());
+    }
+
+    sp<EffectChain> chain = effect->chain().promote();
+    if (chain != 0) {
+        // remove effect chain if removing last effect
+        if (chain->removeEffect_l(effect) == 0) {
+            removeEffectChain_l(chain);
+        }
+    } else {
+        LOGW("removeEffect_l() %p cannot promote chain for effect %p", this, effect.get());
+    }
+}
+
+void AudioFlinger::PlaybackThread::disconnectEffect(const sp<EffectModule>& effect,
+                                                    const wp<EffectHandle>& handle) {
     Mutex::Autolock _l(mLock);
+    LOGV("disconnectEffect() %p effect %p", this, effect.get());
     // delete the effect module if removing last handle on it
     if (effect->removeHandle(handle) == 0) {
-        if ((desc.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY) {
-            detachAuxEffect_l(effect->id());
-        }
-        sp<EffectChain> chain = effect->chain().promote();
-        if (chain != 0) {
-            // remove effect chain if remove last effect
-            if (chain->removeEffect_l(effect) == 0) {
-                removeEffectChain_l(chain);
-            }
-        }
-        mLock.unlock();
-        mAudioFlinger->mLock.lock();
-        mAudioFlinger->unregisterEffectResource_l(&desc);
-        mAudioFlinger->mLock.unlock();
+        removeEffect_l(effect);
+        AudioSystem::unregisterEffect(effect->id());
     }
 }
 
@@ -4863,13 +5026,16 @@
 
     chain->setInBuffer(buffer, ownsBuffer);
     chain->setOutBuffer(mMixBuffer);
-    // Effect chain for session -1 is inserted at end of effect chains list
-    // in order to be processed last as it contains output stage effects
-    // Effect chain for session 0 is inserted before session -1 to be processed
+    // Effect chain for session AudioSystem::SESSION_OUTPUT_STAGE is inserted at end of effect
+    // chains list in order to be processed last as it contains output stage effects
+    // Effect chain for session AudioSystem::SESSION_OUTPUT_MIX is inserted before
+    // session AudioSystem::SESSION_OUTPUT_STAGE to be processed
     // after track specific effects and before output stage
-    // Effect chain for session other than 0 is inserted at beginning of effect
-    // chains list to be processed before output mix effects. Relative order between
-    // sessions other than 0 is not important
+    // It is therefore mandatory that AudioSystem::SESSION_OUTPUT_MIX == 0 and
+    // that AudioSystem::SESSION_OUTPUT_STAGE < AudioSystem::SESSION_OUTPUT_MIX
+    // Effect chain for other sessions are inserted at beginning of effect
+    // chains list to be processed before output mix effects. Relative order between other
+    // sessions is not important
     size_t size = mEffectChains.size();
     size_t i = 0;
     for (i = 0; i < size; i++) {
@@ -4896,26 +5062,30 @@
                     track->setMainBuffer(mMixBuffer);
                 }
             }
+            break;
         }
     }
     return mEffectChains.size();
 }
 
-void AudioFlinger::PlaybackThread::lockEffectChains_l()
+void AudioFlinger::PlaybackThread::lockEffectChains_l(
+        Vector<sp <AudioFlinger::EffectChain> >& effectChains)
 {
+    effectChains = mEffectChains;
     for (size_t i = 0; i < mEffectChains.size(); i++) {
         mEffectChains[i]->lock();
     }
 }
 
-void AudioFlinger::PlaybackThread::unlockEffectChains()
+void AudioFlinger::PlaybackThread::unlockEffectChains(
+        Vector<sp <AudioFlinger::EffectChain> >& effectChains)
 {
-    Mutex::Autolock _l(mLock);
-    for (size_t i = 0; i < mEffectChains.size(); i++) {
-        mEffectChains[i]->unlock();
+    for (size_t i = 0; i < effectChains.size(); i++) {
+        effectChains[i]->unlock();
     }
 }
 
+
 sp<AudioFlinger::EffectModule> AudioFlinger::PlaybackThread::getEffect_l(int sessionId, int effectId)
 {
     sp<EffectModule> effect;
@@ -4927,21 +5097,23 @@
     return effect;
 }
 
-status_t AudioFlinger::PlaybackThread::attachAuxEffect(const sp<AudioFlinger::PlaybackThread::Track> track, int EffectId)
+status_t AudioFlinger::PlaybackThread::attachAuxEffect(
+        const sp<AudioFlinger::PlaybackThread::Track> track, int EffectId)
 {
     Mutex::Autolock _l(mLock);
     return attachAuxEffect_l(track, EffectId);
 }
 
-status_t AudioFlinger::PlaybackThread::attachAuxEffect_l(const sp<AudioFlinger::PlaybackThread::Track> track, int EffectId)
+status_t AudioFlinger::PlaybackThread::attachAuxEffect_l(
+        const sp<AudioFlinger::PlaybackThread::Track> track, int EffectId)
 {
     status_t status = NO_ERROR;
 
     if (EffectId == 0) {
         track->setAuxBuffer(0, NULL);
     } else {
-        // Auxiliary effects are always in audio session 0
-        sp<EffectModule> effect = getEffect_l(0, EffectId);
+        // Auxiliary effects are always in audio session AudioSystem::SESSION_OUTPUT_MIX
+        sp<EffectModule> effect = getEffect_l(AudioSystem::SESSION_OUTPUT_MIX, EffectId);
         if (effect != 0) {
             if ((effect->desc().flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY) {
                 track->setAuxBuffer(EffectId, (int32_t *)effect->inBuffer());
@@ -5137,7 +5309,7 @@
         if ((mDescriptor.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY) {
             AudioMixer::ditherAndClamp(mConfig.inputCfg.buffer.s32,
                                         mConfig.inputCfg.buffer.s32,
-                                        mConfig.inputCfg.buffer.frameCount);
+                                        mConfig.inputCfg.buffer.frameCount/2);
         }
 
         // do the actual processing in the effect engine
@@ -5214,7 +5386,8 @@
     mConfig.outputCfg.bufferProvider.releaseBuffer = NULL;
     mConfig.inputCfg.accessMode = EFFECT_BUFFER_ACCESS_READ;
     // Insert effect:
-    // - in session 0 or -1, always overwrites output buffer: input buffer == output buffer
+    // - in session AudioSystem::SESSION_OUTPUT_MIX or AudioSystem::SESSION_OUTPUT_STAGE,
+    // always overwrites output buffer: input buffer == output buffer
     // - in other sessions:
     //      last effect in the chain accumulates in output buffer: input buffer != output buffer
     //      other effect: overwrites output buffer: input buffer == output buffer
@@ -5231,6 +5404,9 @@
     mConfig.inputCfg.buffer.frameCount = thread->frameCount();
     mConfig.outputCfg.buffer.frameCount = mConfig.inputCfg.buffer.frameCount;
 
+    LOGV("configure() %p thread %p buffer %p framecount %d",
+            this, thread.get(), mConfig.inputCfg.buffer.raw, mConfig.inputCfg.buffer.frameCount);
+
     status_t cmdStatus;
     int size = sizeof(int);
     status_t status = (*mEffectInterface)->command(mEffectInterface, EFFECT_CMD_CONFIGURE, sizeof(effect_config_t), &mConfig, &size, &cmdStatus);
@@ -5753,7 +5929,7 @@
             mVolumeCtrlIdx(-1), mLeftVolume(0), mRightVolume(0),
             mNewLeftVolume(0), mNewRightVolume(0)
 {
-
+    mStrategy = AudioSystem::getStrategyForStream(AudioSystem::MUSIC);
 }
 
 AudioFlinger::EffectChain::~EffectChain()
@@ -5786,7 +5962,8 @@
     size_t size = mEffects.size();
 
     for (size_t i = 0; i < size; i++) {
-        if (mEffects[i]->id() == id) {
+        // by convention, return first effect if id provided is 0 (0 is never a valid id)
+        if (id == 0 || mEffects[i]->id() == id) {
             effect = mEffects[i];
             break;
         }
@@ -5816,21 +5993,24 @@
 }
 
 // addEffect_l() must be called with PlaybackThread::mLock held
-status_t AudioFlinger::EffectChain::addEffect_l(sp<EffectModule>& effect)
+status_t AudioFlinger::EffectChain::addEffect_l(const sp<EffectModule>& effect)
 {
     effect_descriptor_t desc = effect->desc();
     uint32_t insertPref = desc.flags & EFFECT_FLAG_INSERT_MASK;
 
     Mutex::Autolock _l(mLock);
+    effect->setChain(this);
+    sp<ThreadBase> thread = mThread.promote();
+    if (thread == 0) {
+        return NO_INIT;
+    }
+    effect->setThread(thread);
 
     if ((desc.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY) {
         // Auxiliary effects are inserted at the beginning of mEffects vector as
         // they are processed first and accumulated in chain input buffer
         mEffects.insertAt(effect, 0);
-        sp<ThreadBase> thread = mThread.promote();
-        if (thread == 0) {
-            return NO_INIT;
-        }
+
         // the input buffer for auxiliary effect contains mono samples in
         // 32 bit format. This is to avoid saturation in AudoMixer
         // accumulation stage. Saturation is done in EffectModule::process() before