Allow creation of an audio effect on a session with no audio tracks.

This is necessary to allow creating and enabling an effect attached to a particular player
session before the playback is started. As a matter of fact, the implementation of the mediaplayer
does not create the AudioTrack before playback starts.

Change-Id: I1266e8885f9d756acc949303321aaac0fbf83e34
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 3572d10..4a7243f 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -307,6 +307,7 @@
     {
         Mutex::Autolock _l(mLock);
         PlaybackThread *thread = checkPlaybackThread_l(output);
+        PlaybackThread *effectThread = NULL;
         if (thread == NULL) {
             LOGE("unknown output thread");
             lStatus = BAD_VALUE;
@@ -324,12 +325,19 @@
 
         LOGV("createTrack() sessionId: %d", (sessionId == NULL) ? -2 : *sessionId);
         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;
+                sp<PlaybackThread> t = mPlaybackThreads.valueAt(i);
+                if (mPlaybackThreads.keyAt(i) != output) {
+                    // prevent same audio session on different output threads
+                    uint32_t sessions = t->hasAudioSession(*sessionId);
+                    if (sessions & PlaybackThread::TRACK_SESSION) {
+                        lStatus = BAD_VALUE;
+                        goto Exit;
+                    }
+                    // check if an effect with same session ID is waiting for a track to be created
+                    if (sessions & PlaybackThread::EFFECT_SESSION) {
+                        effectThread = t.get();
+                    }
                 }
             }
             lSessionId = *sessionId;
@@ -344,6 +352,14 @@
 
         track = thread->createTrack_l(client, streamType, sampleRate, format,
                 channelCount, frameCount, sharedBuffer, lSessionId, &lStatus);
+
+        // move effect chain to this output thread if an effect on same session was waiting
+        // for a track to be created
+        if (lStatus == NO_ERROR && effectThread != NULL) {
+            Mutex::Autolock _dl(thread->mLock);
+            Mutex::Autolock _sl(effectThread->mLock);
+            moveEffectChain_l(lSessionId, effectThread, thread, true);
+        }
     }
     if (lStatus == NO_ERROR) {
         trackHandle = new TrackHandle(track);
@@ -1377,7 +1393,7 @@
     // 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);
+        mAudioFlinger->moveEffectChain_l(effectChains[i]->sessionId(), this, this, false);
     }
 }
 
@@ -1394,22 +1410,24 @@
     return mOutput->getRenderPosition(dspFrames);
 }
 
-bool AudioFlinger::PlaybackThread::hasAudioSession(int sessionId)
+uint32_t AudioFlinger::PlaybackThread::hasAudioSession(int sessionId)
 {
     Mutex::Autolock _l(mLock);
+    uint32_t result = 0;
     if (getEffectChain_l(sessionId) != 0) {
-        return true;
+        result = EFFECT_SESSION;
     }
 
     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 true;
+            result |= TRACK_SESSION;
+            break;
         }
     }
 
-    return false;
+    return result;
 }
 
 uint32_t AudioFlinger::PlaybackThread::getStrategyForSession_l(int sessionId)
@@ -4704,11 +4722,17 @@
             } else {
                  // look for the thread where the specified audio session is present
                 for (size_t i = 0; i < mPlaybackThreads.size(); i++) {
-                    if (mPlaybackThreads.valueAt(i)->hasAudioSession(sessionId)) {
+                    if (mPlaybackThreads.valueAt(i)->hasAudioSession(sessionId) != 0) {
                         output = mPlaybackThreads.keyAt(i);
                         break;
                     }
                 }
+                // If no output thread contains the requested session ID, default to
+                // first output. The effect chain will be moved to the correct output
+                // thread when a track with the same session ID is created
+                if (output == 0 && mPlaybackThreads.size()) {
+                    output = mPlaybackThreads.keyAt(0);
+                }
             }
         }
         PlaybackThread *thread = checkPlaybackThread_l(output);
@@ -4764,7 +4788,7 @@
 
     Mutex::Autolock _dl(dstThread->mLock);
     Mutex::Autolock _sl(srcThread->mLock);
-    moveEffectChain_l(session, srcThread, dstThread);
+    moveEffectChain_l(session, srcThread, dstThread, false);
 
     return NO_ERROR;
 }
@@ -4772,7 +4796,8 @@
 // moveEffectChain_l mustbe called with both srcThread and dstThread mLocks held
 status_t AudioFlinger::moveEffectChain_l(int session,
                                    AudioFlinger::PlaybackThread *srcThread,
-                                   AudioFlinger::PlaybackThread *dstThread)
+                                   AudioFlinger::PlaybackThread *dstThread,
+                                   bool reRegister)
 {
     LOGV("moveEffectChain_l() session %d from thread %p to thread %p",
             session, srcThread, dstThread);
@@ -4784,7 +4809,7 @@
         return INVALID_OPERATION;
     }
 
-    // remove chain first. This is usefull only if reconfiguring effect chain on same output thread,
+    // remove chain first. This is useful 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.
@@ -4792,10 +4817,32 @@
 
     // 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
+    int dstOutput = dstThread->id();
+    sp<EffectChain> dstChain;
+    uint32_t strategy;
     sp<EffectModule> effect = chain->getEffectFromId_l(0);
     while (effect != 0) {
         srcThread->removeEffect_l(effect);
         dstThread->addEffect_l(effect);
+        // if the move request is not received from audio policy manager, the effect must be
+        // re-registered with the new strategy and output
+        if (dstChain == 0) {
+            dstChain = effect->chain().promote();
+            if (dstChain == 0) {
+                LOGW("moveEffectChain_l() cannot get chain from effect %p", effect.get());
+                srcThread->addEffect_l(effect);
+                return NO_INIT;
+            }
+            strategy = dstChain->strategy();
+        }
+        if (reRegister) {
+            AudioSystem::unregisterEffect(effect->id());
+            AudioSystem::registerEffect(&effect->desc(),
+                                        dstOutput,
+                                        strategy,
+                                        session,
+                                        effect->id());
+        }
         effect = chain->getEffectFromId_l(0);
     }
 
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index 8f667a3..5520551 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -613,7 +613,15 @@
                     void disconnectEffect(const sp< EffectModule>& effect,
                                           const wp<EffectHandle>& handle);
 
-                    bool hasAudioSession(int sessionId);
+                    // return values for hasAudioSession (bit field)
+                    enum effect_state {
+                        EFFECT_SESSION = 0x1,   // the audio session corresponds to at least one
+                                                // effect
+                        TRACK_SESSION = 0x2     // the audio session corresponds to at least one
+                                                // track
+                    };
+
+                    uint32_t hasAudioSession(int sessionId);
                     sp<EffectChain> getEffectChain(int sessionId);
                     sp<EffectChain> getEffectChain_l(int sessionId);
                     status_t addEffectChain_l(const sp<EffectChain>& chain);
@@ -776,7 +784,8 @@
               int  nextUniqueId();
               status_t moveEffectChain_l(int session,
                                      AudioFlinger::PlaybackThread *srcThread,
-                                     AudioFlinger::PlaybackThread *dstThread);
+                                     AudioFlinger::PlaybackThread *dstThread,
+                                     bool reRegister);
 
     friend class AudioBuffer;