Merge "mediaplayer: handle surface dis/connection errors" into lmp-dev
diff --git a/media/libmediaplayerservice/nuplayer/GenericSource.cpp b/media/libmediaplayerservice/nuplayer/GenericSource.cpp
index d8ed836..511871d 100644
--- a/media/libmediaplayerservice/nuplayer/GenericSource.cpp
+++ b/media/libmediaplayerservice/nuplayer/GenericSource.cpp
@@ -1176,12 +1176,14 @@
 void NuPlayer::GenericSource::readBuffer(
         media_track_type trackType, int64_t seekTimeUs, int64_t *actualTimeUs, bool formatChange) {
     Track *track;
+    size_t maxBuffers = 1;
     switch (trackType) {
         case MEDIA_TRACK_TYPE_VIDEO:
             track = &mVideoTrack;
             break;
         case MEDIA_TRACK_TYPE_AUDIO:
             track = &mAudioTrack;
+            maxBuffers = 64;
             break;
         case MEDIA_TRACK_TYPE_SUBTITLE:
             track = &mSubtitleTrack;
@@ -1214,7 +1216,7 @@
         options.setNonBlocking();
     }
 
-    for (;;) {
+    for (size_t numBuffers = 0; numBuffers < maxBuffers; ) {
         MediaBuffer *mbuf;
         status_t err = track->mSource->read(&mbuf, &options);
 
@@ -1245,7 +1247,7 @@
 
             sp<ABuffer> buffer = mediaBufferToABuffer(mbuf, trackType, actualTimeUs);
             track->mPackets->queueAccessUnit(buffer);
-            break;
+            ++numBuffers;
         } else if (err == WOULD_BLOCK) {
             break;
         } else if (err == INFO_FORMAT_CHANGED) {
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
index df3e992..9020a8d 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
@@ -50,6 +50,10 @@
 
 namespace android {
 
+// TODO optimize buffer size for power consumption
+// The offload read buffer size is 32 KB but 24 KB uses less power.
+const size_t NuPlayer::kAggregateBufferSizeBytes = 24 * 1024;
+
 struct NuPlayer::Action : public RefBase {
     Action() {}
 
@@ -730,7 +734,7 @@
 
                 if (err == -EWOULDBLOCK) {
                     if (mSource->feedMoreTSData() == OK) {
-                        msg->post(10000ll);
+                        msg->post(10 * 1000ll);
                     }
                 }
             } else if (what == Decoder::kWhatEOS) {
@@ -995,6 +999,7 @@
     ALOGV("both audio and video are flushed now.");
 
     mPendingAudioAccessUnit.clear();
+    mAggregateBuffer.clear();
 
     if (mTimeDiscontinuityPending) {
         mRenderer->signalTimeDiscontinuity();
@@ -1256,14 +1261,8 @@
     // Aggregate smaller buffers into a larger buffer.
     // The goal is to reduce power consumption.
     // Unfortunately this does not work with the software AAC decoder.
-    // TODO optimize buffer size for power consumption
-    // The offload read buffer size is 32 KB but 24 KB uses less power.
-    const int kAudioBigBufferSizeBytes = 24 * 1024;
-    bool doBufferAggregation = (audio && mOffloadAudio);
-    sp<ABuffer> biggerBuffer;
+    bool doBufferAggregation = (audio && mOffloadAudio);;
     bool needMoreData = false;
-    int numSmallBuffers = 0;
-    bool gotTime = false;
 
     bool dropAccessUnit;
     do {
@@ -1279,14 +1278,10 @@
         }
 
         if (err == -EWOULDBLOCK) {
-            if (biggerBuffer == NULL) {
-                return err;
-            } else {
-                break; // Reply with data that we already have.
-            }
+            return err;
         } else if (err != OK) {
             if (err == INFO_DISCONTINUITY) {
-                if (biggerBuffer != NULL) {
+                if (mAggregateBuffer != NULL) {
                     // We already have some data so save this for later.
                     mPendingAudioErr = err;
                     mPendingAudioAccessUnit = accessUnit;
@@ -1401,46 +1396,45 @@
 
         size_t smallSize = accessUnit->size();
         needMoreData = false;
-        if (doBufferAggregation && (biggerBuffer == NULL)
+        if (doBufferAggregation && (mAggregateBuffer == NULL)
                 // Don't bother if only room for a few small buffers.
-                && (smallSize < (kAudioBigBufferSizeBytes / 3))) {
+                && (smallSize < (kAggregateBufferSizeBytes / 3))) {
             // Create a larger buffer for combining smaller buffers from the extractor.
-            biggerBuffer = new ABuffer(kAudioBigBufferSizeBytes);
-            biggerBuffer->setRange(0, 0); // start empty
+            mAggregateBuffer = new ABuffer(kAggregateBufferSizeBytes);
+            mAggregateBuffer->setRange(0, 0); // start empty
         }
 
-        if (biggerBuffer != NULL) {
+        if (mAggregateBuffer != NULL) {
             int64_t timeUs;
+            int64_t dummy;
             bool smallTimestampValid = accessUnit->meta()->findInt64("timeUs", &timeUs);
+            bool bigTimestampValid = mAggregateBuffer->meta()->findInt64("timeUs", &dummy);
             // Will the smaller buffer fit?
-            size_t bigSize = biggerBuffer->size();
-            size_t roomLeft = biggerBuffer->capacity() - bigSize;
+            size_t bigSize = mAggregateBuffer->size();
+            size_t roomLeft = mAggregateBuffer->capacity() - bigSize;
             // Should we save this small buffer for the next big buffer?
             // If the first small buffer did not have a timestamp then save
             // any buffer that does have a timestamp until the next big buffer.
             if ((smallSize > roomLeft)
-                || (!gotTime && (numSmallBuffers > 0) && smallTimestampValid)) {
+                || (!bigTimestampValid && (bigSize > 0) && smallTimestampValid)) {
                 mPendingAudioErr = err;
                 mPendingAudioAccessUnit = accessUnit;
                 accessUnit.clear();
             } else {
+                // Grab time from first small buffer if available.
+                if ((bigSize == 0) && smallTimestampValid) {
+                    mAggregateBuffer->meta()->setInt64("timeUs", timeUs);
+                }
                 // Append small buffer to the bigger buffer.
-                memcpy(biggerBuffer->base() + bigSize, accessUnit->data(), smallSize);
+                memcpy(mAggregateBuffer->base() + bigSize, accessUnit->data(), smallSize);
                 bigSize += smallSize;
-                biggerBuffer->setRange(0, bigSize);
+                mAggregateBuffer->setRange(0, bigSize);
 
-                // Keep looping until we run out of room in the biggerBuffer.
+                // Keep looping until we run out of room in the mAggregateBuffer.
                 needMoreData = true;
 
-                // Grab time from first small buffer if available.
-                if ((numSmallBuffers == 0) && smallTimestampValid) {
-                    biggerBuffer->meta()->setInt64("timeUs", timeUs);
-                    gotTime = true;
-                }
-
-                ALOGV("feedDecoderInputData() #%d, smallSize = %zu, bigSize = %zu, capacity = %zu",
-                        numSmallBuffers, smallSize, bigSize, biggerBuffer->capacity());
-                numSmallBuffers++;
+                ALOGV("feedDecoderInputData() smallSize = %zu, bigSize = %zu, capacity = %zu",
+                        smallSize, bigSize, mAggregateBuffer->capacity());
             }
         }
     } while (dropAccessUnit || needMoreData);
@@ -1459,9 +1453,11 @@
         mCCDecoder->decode(accessUnit);
     }
 
-    if (biggerBuffer != NULL) {
-        ALOGV("feedDecoderInputData() reply with aggregated buffer, %d", numSmallBuffers);
-        reply->setBuffer("buffer", biggerBuffer);
+    if (mAggregateBuffer != NULL) {
+        ALOGV("feedDecoderInputData() reply with aggregated buffer, %zu",
+                mAggregateBuffer->size());
+        reply->setBuffer("buffer", mAggregateBuffer);
+        mAggregateBuffer.clear();
     } else {
         reply->setBuffer("buffer", accessUnit);
     }
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.h b/media/libmediaplayerservice/nuplayer/NuPlayer.h
index 89ae11c..2e951bd 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayer.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.h
@@ -67,6 +67,8 @@
     status_t getSelectedTrack(int32_t type, Parcel* reply) const;
     status_t selectTrack(size_t trackIndex, bool select);
 
+    static const size_t kAggregateBufferSizeBytes;
+
 protected:
     virtual ~NuPlayer();
 
@@ -158,8 +160,11 @@
     // notion of time has changed.
     bool mTimeDiscontinuityPending;
 
+    // Used by feedDecoderInputData to aggregate small buffers into
+    // one large buffer.
     sp<ABuffer> mPendingAudioAccessUnit;
     status_t    mPendingAudioErr;
+    sp<ABuffer> mAggregateBuffer;
 
     FlushStatus mFlushingAudio;
     FlushStatus mFlushingVideo;
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoderPassThrough.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDecoderPassThrough.cpp
index ab7906a..f7aacdd 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoderPassThrough.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoderPassThrough.cpp
@@ -30,8 +30,10 @@
 
 namespace android {
 
-static const int kMaxPendingBuffers = 10;
-static const int kMaxCachedBytes = 200000;
+static const size_t kMaxCachedBytes = 200000;
+// The buffers will contain a bit less than kAggregateBufferSizeBytes.
+// So we can start off with just enough buffers to keep the cache full.
+static const size_t kMaxPendingBuffers = 1 + (kMaxCachedBytes / NuPlayer::kAggregateBufferSizeBytes);
 
 NuPlayer::DecoderPassThrough::DecoderPassThrough(
         const sp<AMessage> &notify)
@@ -39,7 +41,8 @@
       mNotify(notify),
       mBufferGeneration(0),
       mReachedEOS(true),
-      mPendingBuffers(0),
+      mPendingBuffersToFill(0),
+      mPendingBuffersToDrain(0),
       mCachedBytes(0),
       mComponentName("pass through decoder") {
     mDecoderLooper = new ALooper;
@@ -79,12 +82,13 @@
 
 void NuPlayer::DecoderPassThrough::onConfigure(const sp<AMessage> &format) {
     ALOGV("[%s] onConfigure", mComponentName.c_str());
-    mPendingBuffers = 0;
     mCachedBytes = 0;
+    mPendingBuffersToFill = 0;
+    mPendingBuffersToDrain = 0;
     mReachedEOS = false;
     ++mBufferGeneration;
 
-    requestABuffer();
+    requestMaxBuffers();
 
     sp<AMessage> notify = mNotify->dup();
     notify->setInt32("what", kWhatOutputFormatChanged);
@@ -98,12 +102,15 @@
     return generation != mBufferGeneration;
 }
 
-void NuPlayer::DecoderPassThrough::requestABuffer() {
-    if (mCachedBytes >= kMaxCachedBytes || mReachedEOS) {
-        ALOGV("[%s] mReachedEOS=%d, max pending buffers(%d:%d)",
-                mComponentName.c_str(), (mReachedEOS ? 1 : 0),
-                mPendingBuffers, kMaxPendingBuffers);
-        return;
+bool NuPlayer::DecoderPassThrough::requestABuffer() {
+    if (mCachedBytes >= kMaxCachedBytes) {
+        ALOGV("[%s] mCachedBytes = %zu",
+                mComponentName.c_str(), mCachedBytes);
+        return false;
+    }
+    if (mReachedEOS) {
+        ALOGV("[%s] reached EOS", mComponentName.c_str());
+        return false;
     }
 
     sp<AMessage> reply = new AMessage(kWhatInputBufferFilled, id());
@@ -113,19 +120,16 @@
     notify->setInt32("what", kWhatFillThisBuffer);
     notify->setMessage("reply", reply);
     notify->post();
-    mPendingBuffers++;
+    mPendingBuffersToFill++;
+    ALOGV("requestABuffer: #ToFill = %zu, #ToDrain = %zu", mPendingBuffersToFill,
+            mPendingBuffersToDrain);
 
-    // pending buffers will already result in requestABuffer
-    if (mPendingBuffers < kMaxPendingBuffers) {
-        sp<AMessage> message = new AMessage(kWhatRequestABuffer, id());
-        message->setInt32("generation", mBufferGeneration);
-        message->post();
-    }
-    return;
+    return true;
 }
 
 void android::NuPlayer::DecoderPassThrough::onInputBufferFilled(
         const sp<AMessage> &msg) {
+    --mPendingBuffersToFill;
     if (mReachedEOS) {
         return;
     }
@@ -153,11 +157,16 @@
     notify->setBuffer("buffer", buffer);
     notify->setMessage("reply", reply);
     notify->post();
+    ++mPendingBuffersToDrain;
+    ALOGV("onInputBufferFilled: #ToFill = %zu, #ToDrain = %zu, cachedBytes = %zu",
+            mPendingBuffersToFill, mPendingBuffersToDrain, mCachedBytes);
 }
 
 void NuPlayer::DecoderPassThrough::onBufferConsumed(int32_t size) {
-    mPendingBuffers--;
+    --mPendingBuffersToDrain;
     mCachedBytes -= size;
+    ALOGV("onBufferConsumed: #ToFill = %zu, #ToDrain = %zu, cachedBytes = %zu",
+           mPendingBuffersToFill, mPendingBuffersToDrain, mCachedBytes);
     requestABuffer();
 }
 
@@ -167,11 +176,20 @@
     sp<AMessage> notify = mNotify->dup();
     notify->setInt32("what", kWhatFlushCompleted);
     notify->post();
-    mPendingBuffers = 0;
+    mPendingBuffersToFill = 0;
+    mPendingBuffersToDrain = 0;
     mCachedBytes = 0;
     mReachedEOS = false;
 }
 
+void NuPlayer::DecoderPassThrough::requestMaxBuffers() {
+    for (size_t i = 0; i < kMaxPendingBuffers; i++) {
+        if (!requestABuffer()) {
+            break;
+        }
+    }
+}
+
 void NuPlayer::DecoderPassThrough::onShutdown() {
     ++mBufferGeneration;
 
@@ -229,7 +247,7 @@
 
         case kWhatResume:
         {
-            requestABuffer();
+            requestMaxBuffers();
             break;
         }
 
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoderPassThrough.h b/media/libmediaplayerservice/nuplayer/NuPlayerDecoderPassThrough.h
index 8590856..fb20257 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoderPassThrough.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoderPassThrough.h
@@ -55,19 +55,26 @@
     sp<AMessage> mNotify;
     sp<ALooper> mDecoderLooper;
 
-    void requestABuffer();
+    /** Returns true if a buffer was requested.
+     * Returns false if at EOS or cache already full.
+     */
+    bool requestABuffer();
     bool isStaleReply(const sp<AMessage> &msg);
 
     void onConfigure(const sp<AMessage> &format);
     void onFlush();
     void onInputBufferFilled(const sp<AMessage> &msg);
     void onBufferConsumed(int32_t size);
+    void requestMaxBuffers();
     void onShutdown();
 
     int32_t mBufferGeneration;
-    bool mReachedEOS;
-    int32_t mPendingBuffers;
-    int32_t mCachedBytes;
+    bool    mReachedEOS;
+    // TODO mPendingBuffersToFill and mPendingBuffersToDrain are only for
+    // debugging. They can be removed when the power investigation is done.
+    size_t  mPendingBuffersToFill;
+    size_t  mPendingBuffersToDrain;
+    size_t  mCachedBytes;
     AString mComponentName;
 
     DISALLOW_EVIL_CONSTRUCTORS(DecoderPassThrough);
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index cee7feb..e200857 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -418,6 +418,13 @@
             mRecordThreads.valueAt(i)->dump(fd, args);
         }
 
+        // dump orphan effect chains
+        if (mOrphanEffectChains.size() != 0) {
+            write(fd, "  Orphan Effect Chains\n", strlen("  Orphan Effect Chains\n"));
+            for (size_t i = 0; i < mOrphanEffectChains.size(); i++) {
+                mOrphanEffectChains.valueAt(i)->dump(fd, args);
+            }
+        }
         // dump all hardware devs
         for (size_t i = 0; i < mAudioHwDevs.size(); i++) {
             audio_hw_device_t *dev = mAudioHwDevs.valueAt(i)->hwDevice();
@@ -1421,7 +1428,7 @@
                 *sessionId = lSessionId;
             }
         }
-        ALOGV("openRecord() lSessionId: %d", lSessionId);
+        ALOGV("openRecord() lSessionId: %d input %d", lSessionId, input);
 
         // TODO: the uid should be passed in as a parameter to openRecord
         recordTrack = thread->createRecordTrack_l(client, sampleRate, format, channelMask,
@@ -2027,6 +2034,16 @@
         }
 
         ALOGV("closeInput() %d", input);
+        {
+            // If we still have effect chains, it means that a client still holds a handle
+            // on at least one effect. We must keep the chain alive in case a new record
+            // thread is opened for a new capture on the same session
+            Mutex::Autolock _sl(thread->mLock);
+            Vector< sp<EffectChain> > effectChains = thread->getEffectChains_l();
+            for (size_t i = 0; i < effectChains.size(); i++) {
+                putOrphanEffectChain_l(effectChains[i]);
+            }
+        }
         audioConfigChanged(AudioSystem::INPUT_CLOSED, input, NULL);
         mRecordThreads.removeItem(input);
     }
@@ -2456,6 +2473,13 @@
                 lStatus = BAD_VALUE;
                 goto Exit;
             }
+        } else {
+            // Check if one effect chain was awaiting for an effect to be created on this
+            // session and used it instead of creating a new one.
+            sp<EffectChain> chain = getOrphanEffectChain_l((audio_session_t)sessionId);
+            if (chain != 0) {
+                thread->addEffectChain_l(chain);
+            }
         }
 
         sp<Client> client = registerPid(pid);
@@ -2628,6 +2652,49 @@
 
 }
 
+status_t AudioFlinger::putOrphanEffectChain_l(const sp<AudioFlinger::EffectChain>& chain)
+{
+    audio_session_t session = (audio_session_t)chain->sessionId();
+    ssize_t index = mOrphanEffectChains.indexOfKey(session);
+    ALOGV("putOrphanEffectChain_l session %d index %d", session, index);
+    if (index >= 0) {
+        ALOGW("putOrphanEffectChain_l chain for session %d already present", session);
+        return ALREADY_EXISTS;
+    }
+    mOrphanEffectChains.add(session, chain);
+    return NO_ERROR;
+}
+
+sp<AudioFlinger::EffectChain> AudioFlinger::getOrphanEffectChain_l(audio_session_t session)
+{
+    sp<EffectChain> chain;
+    ssize_t index = mOrphanEffectChains.indexOfKey(session);
+    ALOGV("getOrphanEffectChain_l session %d index %d", session, index);
+    if (index >= 0) {
+        chain = mOrphanEffectChains.valueAt(index);
+        mOrphanEffectChains.removeItemsAt(index);
+    }
+    return chain;
+}
+
+bool AudioFlinger::updateOrphanEffectChains(const sp<AudioFlinger::EffectModule>& effect)
+{
+    Mutex::Autolock _l(mLock);
+    audio_session_t session = (audio_session_t)effect->sessionId();
+    ssize_t index = mOrphanEffectChains.indexOfKey(session);
+    ALOGV("updateOrphanEffectChains session %d index %d", session, index);
+    if (index >= 0) {
+        sp<EffectChain> chain = mOrphanEffectChains.valueAt(index);
+        if (chain->removeEffect_l(effect) == 0) {
+            ALOGV("updateOrphanEffectChains removing effect chain at index %d", index);
+            mOrphanEffectChains.removeItemsAt(index);
+        }
+        return true;
+    }
+    return false;
+}
+
+
 struct Entry {
 #define MAX_NAME 32     // %Y%m%d%H%M%S_%d.wav
     char mName[MAX_NAME];
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index 753314f..1003017 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -569,6 +569,23 @@
                 bool isNonOffloadableGlobalEffectEnabled_l();
                 void onNonOffloadableGlobalEffectEnable();
 
+                // Store an effect chain to mOrphanEffectChains keyed vector.
+                // Called when a thread exits and effects are still attached to it.
+                // If effects are later created on the same session, they will reuse the same
+                // effect chain and same instances in the effect library.
+                // return ALREADY_EXISTS if a chain with the same session already exists in
+                // mOrphanEffectChains. Note that this should never happen as there is only one
+                // chain for a given session and it is attached to only one thread at a time.
+                status_t        putOrphanEffectChain_l(const sp<EffectChain>& chain);
+                // Get an effect chain for the specified session in mOrphanEffectChains and remove
+                // it if found. Returns 0 if not found (this is the most common case).
+                sp<EffectChain> getOrphanEffectChain_l(audio_session_t session);
+                // Called when the last effect handle on an effect instance is removed. If this
+                // effect belongs to an effect chain in mOrphanEffectChains, the chain is updated
+                // and removed from mOrphanEffectChains if it does not contain any effect.
+                // Return true if the effect was found in mOrphanEffectChains, false otherwise.
+                bool            updateOrphanEffectChains(const sp<EffectModule>& effect);
+
     class AudioHwDevice {
     public:
         enum Flags {
@@ -713,6 +730,9 @@
                 Vector < sp<SyncEvent> > mPendingSyncEvents; // sync events awaiting for a session
                                                              // to be created
 
+                // Effect chains without a valid thread
+                DefaultKeyedVector< audio_session_t , sp<EffectChain> > mOrphanEffectChains;
+
 private:
     sp<Client>  registerPid(pid_t pid);    // always returns non-0
 
diff --git a/services/audioflinger/Effects.cpp b/services/audioflinger/Effects.cpp
index 365f271..15f1f23 100644
--- a/services/audioflinger/Effects.cpp
+++ b/services/audioflinger/Effects.cpp
@@ -68,7 +68,8 @@
       mStatus(NO_INIT), mState(IDLE),
       // mMaxDisableWaitCnt is set by configure() and not used before then
       // mDisableWaitCnt is set by process() and updateState() and not used before then
-      mSuspended(false)
+      mSuspended(false),
+      mAudioFlinger(thread->mAudioFlinger)
 {
     ALOGV("Constructor %p", this);
     int lStatus;
@@ -197,9 +198,19 @@
     // destructor before we exit
     sp<EffectModule> keep(this);
     {
-        sp<ThreadBase> thread = mThread.promote();
-        if (thread != 0) {
-            thread->disconnectEffect(keep, handle, unpinIfLast);
+        if (removeHandle(handle) == 0) {
+            if (!isPinned() || unpinIfLast) {
+                sp<ThreadBase> thread = mThread.promote();
+                if (thread != 0) {
+                    Mutex::Autolock _l(thread->mLock);
+                    thread->removeEffect_l(this);
+                }
+                sp<AudioFlinger> af = mAudioFlinger.promote();
+                if (af != 0) {
+                    af->updateOrphanEffectChains(this);
+                }
+                AudioSystem::unregisterEffect(mId);
+            }
         }
     }
     return mHandles.size();
@@ -1911,4 +1922,13 @@
     return false;
 }
 
+void AudioFlinger::EffectChain::setThread(const sp<ThreadBase>& thread)
+{
+    Mutex::Autolock _l(mLock);
+    mThread = thread;
+    for (size_t i = 0; i < mEffects.size(); i++) {
+        mEffects[i]->setThread(thread);
+    }
+}
+
 }; // namespace android
diff --git a/services/audioflinger/Effects.h b/services/audioflinger/Effects.h
index 4170fd4..eaf90e7 100644
--- a/services/audioflinger/Effects.h
+++ b/services/audioflinger/Effects.h
@@ -153,6 +153,7 @@
     uint32_t mDisableWaitCnt;       // current process() calls count during disable period.
     bool     mSuspended;            // effect is suspended: temporarily disabled by framework
     bool     mOffloaded;            // effect is currently offloaded to the audio DSP
+    wp<AudioFlinger>    mAudioFlinger;
 };
 
 // The EffectHandle class implements the IEffect interface. It provides resources
@@ -347,6 +348,8 @@
 
     void clearInputBuffer_l(sp<ThreadBase> thread);
 
+    void setThread(const sp<ThreadBase>& thread);
+
     wp<ThreadBase> mThread;     // parent mixer thread
     Mutex mLock;                // mutex protecting effect list
     Vector< sp<EffectModule> > mEffects; // list of effect modules
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index 97b1753..3d17c89 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -1147,21 +1147,6 @@
     }
 }
 
-void AudioFlinger::ThreadBase::disconnectEffect(const sp<EffectModule>& effect,
-                                                    EffectHandle *handle,
-                                                    bool unpinIfLast) {
-
-    Mutex::Autolock _l(mLock);
-    ALOGV("disconnectEffect() %p effect %p", this, effect.get());
-    // delete the effect module if removing last handle on it
-    if (effect->removeHandle(handle) == 0) {
-        if (!effect->isPinned() || unpinIfLast) {
-            removeEffect_l(effect);
-            AudioSystem::unregisterEffect(effect->id());
-        }
-    }
-}
-
 void AudioFlinger::ThreadBase::getAudioPortConfig(struct audio_port_config *config)
 {
     config->type = AUDIO_PORT_TYPE_MIX;
@@ -2278,7 +2263,7 @@
             }
         }
     }
-
+    chain->setThread(this);
     chain->setInBuffer(buffer, ownsBuffer);
     chain->setOutBuffer(reinterpret_cast<int16_t*>(mEffectBufferEnabled
             ? mEffectBuffer : mSinkBuffer));
@@ -6188,10 +6173,11 @@
 {
     // only one chain per input thread
     if (mEffectChains.size() != 0) {
+        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);
     chain->setOutBuffer(NULL);
 
diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h
index 648502b..fd025b5 100644
--- a/services/audioflinger/Threads.h
+++ b/services/audioflinger/Threads.h
@@ -283,9 +283,6 @@
                                     effect_descriptor_t *desc,
                                     int *enabled,
                                     status_t *status /*non-NULL*/);
-                void disconnectEffect(const sp< EffectModule>& effect,
-                                      EffectHandle *handle,
-                                      bool unpinIfLast);
 
                 // return values for hasAudioSession (bit field)
                 enum effect_state {
diff --git a/services/audioflinger/TrackBase.h b/services/audioflinger/TrackBase.h
index 864daa5..98bf96e 100644
--- a/services/audioflinger/TrackBase.h
+++ b/services/audioflinger/TrackBase.h
@@ -166,6 +166,7 @@
     sp<NBAIO_Source>    mTeeSource;
     bool                mTerminated;
     track_type          mType;      // must be one of TYPE_DEFAULT, TYPE_OUTPUT, TYPE_PATCH ...
+    audio_io_handle_t   mThreadIoHandle; // I/O handle of the thread the track is attached to
 };
 
 // PatchProxyBufferProvider interface is implemented by PatchTrack and PatchRecord.
diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp
index 6cbab04..c0a75b9 100644
--- a/services/audioflinger/Tracks.cpp
+++ b/services/audioflinger/Tracks.cpp
@@ -96,7 +96,8 @@
         mServerProxy(NULL),
         mId(android_atomic_inc(&nextTrackId)),
         mTerminated(false),
-        mType(type)
+        mType(type),
+        mThreadIoHandle(thread->id())
 {
     // if the caller is us, trust the specified uid
     if (IPCThreadState::self()->getCallingPid() != getpid_cached || clientUid == -1) {
@@ -482,14 +483,15 @@
     // this Track with its member mTrack.
     sp<Track> keep(this);
     { // scope for mLock
+        bool wasActive = false;
         sp<ThreadBase> thread = mThread.promote();
         if (thread != 0) {
             Mutex::Autolock _l(thread->mLock);
             PlaybackThread *playbackThread = (PlaybackThread *)thread.get();
-            bool wasActive = playbackThread->destroyTrack_l(this);
-            if (isExternalTrack() && !wasActive) {
-                AudioSystem::releaseOutput(thread->id());
-            }
+            wasActive = playbackThread->destroyTrack_l(this);
+        }
+        if (isExternalTrack() && !wasActive) {
+            AudioSystem::releaseOutput(mThreadIoHandle);
         }
     }
 }
@@ -2050,7 +2052,7 @@
     if (thread != 0) {
         RecordThread *recordThread = (RecordThread *)thread.get();
         if (recordThread->stop(this) && isExternalTrack()) {
-            AudioSystem::stopInput(recordThread->id(), (audio_session_t)mSessionId);
+            AudioSystem::stopInput(mThreadIoHandle, (audio_session_t)mSessionId);
         }
     }
 }
@@ -2060,14 +2062,14 @@
     // see comments at AudioFlinger::PlaybackThread::Track::destroy()
     sp<RecordTrack> keep(this);
     {
+        if (isExternalTrack()) {
+            if (mState == ACTIVE || mState == RESUMING) {
+                AudioSystem::stopInput(mThreadIoHandle, (audio_session_t)mSessionId);
+            }
+            AudioSystem::releaseInput(mThreadIoHandle, (audio_session_t)mSessionId);
+        }
         sp<ThreadBase> thread = mThread.promote();
         if (thread != 0) {
-            if (isExternalTrack()) {
-                if (mState == ACTIVE || mState == RESUMING) {
-                    AudioSystem::stopInput(thread->id(), (audio_session_t)mSessionId);
-                }
-                AudioSystem::releaseInput(thread->id(), (audio_session_t)mSessionId);
-            }
             Mutex::Autolock _l(thread->mLock);
             RecordThread *recordThread = (RecordThread *) thread.get();
             recordThread->destroyTrack_l(this);
diff --git a/services/camera/libcameraservice/api1/Camera2Client.cpp b/services/camera/libcameraservice/api1/Camera2Client.cpp
index 10038c5..fe2f299 100644
--- a/services/camera/libcameraservice/api1/Camera2Client.cpp
+++ b/services/camera/libcameraservice/api1/Camera2Client.cpp
@@ -1091,7 +1091,7 @@
     }
 
     if (recordingStreamNeedsUpdate) {
-        // Need to stop stream here in case updateRecordingStream fails
+        // Need to stop stream here so updateProcessorStream won't trigger configureStream
         // Right now camera device cannot handle configureStream failure gracefully
         // when device is streaming
         res = mStreamingProcessor->stopStream();
@@ -1110,18 +1110,6 @@
                 &StreamingProcessor::updateRecordingStream>(mStreamingProcessor,
                                                             params);
 
-        // updateRecordingStream might trigger a configureStream call and device might fail
-        // configureStream due to jpeg size > video size. Try again with jpeg size overridden
-        // to video size.
-        // TODO: This may not be needed after we add stop streaming above. Remove that if
-        // it's the case.
-        if (res == BAD_VALUE) {
-            overrideVideoSnapshotSize(params);
-            res = updateProcessorStream<
-                    StreamingProcessor,
-                    &StreamingProcessor::updateRecordingStream>(mStreamingProcessor,
-                                                                params);
-        }
         if (res != OK) {
             ALOGE("%s: Camera %d: Unable to update recording stream: %s (%d)",
                     __FUNCTION__, mCameraId, strerror(-res), res);
@@ -1380,6 +1368,12 @@
 
         int lastJpegStreamId = mJpegProcessor->getStreamId();
         res = updateProcessorStream(mJpegProcessor, l.mParameters);
+        // If video snapshot fail to configureStream, try override video snapshot size to
+        // video size
+        if (res == BAD_VALUE && l.mParameters.state == Parameters::VIDEO_SNAPSHOT) {
+            overrideVideoSnapshotSize(l.mParameters);
+            res = updateProcessorStream(mJpegProcessor, l.mParameters);
+        }
         if (res != OK) {
             ALOGE("%s: Camera %d: Can't set up still image stream: %s (%d)",
                     __FUNCTION__, mCameraId, strerror(-res), res);
diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp
index 690f675..6a7f9e7 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Device.cpp
@@ -1092,6 +1092,10 @@
 
     ALOGV("%s: Camera %d: Waiting until idle", __FUNCTION__, mId);
     status_t res = waitUntilStateThenRelock(/*active*/ false, kShutdownTimeout);
+    if (res != OK) {
+        SET_ERR_L("Error waiting for HAL to drain: %s (%d)", strerror(-res),
+                res);
+    }
     return res;
 }