Fix memory access error due to race during fast track removal

Bug: 16205169
Change-Id: Ie15ec2a737b6ddd16b2d26030d01564ff70177f5
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index 193f8e4..f721d5c 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -5017,9 +5017,12 @@
         // activeTracks accumulates a copy of a subset of mActiveTracks
         Vector< sp<RecordTrack> > activeTracks;
 
-        // reference to the (first and only) fast track
+        // reference to the (first and only) active fast track
         sp<RecordTrack> fastTrack;
 
+        // reference to a fast track which is about to be removed
+        sp<RecordTrack> fastTrackToRemove;
+
         { // scope for mLock
             Mutex::Autolock _l(mLock);
 
@@ -5058,6 +5061,10 @@
 
                 activeTrack = mActiveTracks[i];
                 if (activeTrack->isTerminated()) {
+                    if (activeTrack->isFastTrack()) {
+                        ALOG_ASSERT(fastTrackToRemove == 0);
+                        fastTrackToRemove = activeTrack;
+                    }
                     removeTrack_l(activeTrack);
                     mActiveTracks.remove(activeTrack);
                     mActiveTracksGen++;
@@ -5130,10 +5137,12 @@
             effectChains[i]->process_l();
         }
 
-        // Start the fast capture if it's not already running
+        // Push a new fast capture state if fast capture is not already running, or cblk change
         if (mFastCapture != 0) {
             FastCaptureStateQueue *sq = mFastCapture->sq();
             FastCaptureState *state = sq->begin();
+            bool didModify = false;
+            FastCaptureStateQueue::block_t block = FastCaptureStateQueue::BLOCK_UNTIL_PUSHED;
             if (state->mCommand != FastCaptureState::READ_WRITE /* FIXME &&
                     (kUseFastMixer != FastMixer_Dynamic || state->mTrackMask > 1)*/) {
                 if (state->mCommand == FastCaptureState::COLD_IDLE) {
@@ -5147,19 +5156,32 @@
                 mFastCaptureDumpState.increaseSamplingN(mAudioFlinger->isLowRamDevice() ?
                         FastCaptureDumpState::kSamplingNforLowRamDevice : FastMixerDumpState::kSamplingN);
 #endif
-                state->mCblk = fastTrack != 0 ? fastTrack->cblk() : NULL;
-                sq->end();
-                sq->push(FastCaptureStateQueue::BLOCK_UNTIL_PUSHED);
+                didModify = true;
+            }
+            audio_track_cblk_t *cblkOld = state->mCblk;
+            audio_track_cblk_t *cblkNew = fastTrack != 0 ? fastTrack->cblk() : NULL;
+            if (cblkNew != cblkOld) {
+                state->mCblk = cblkNew;
+                // block until acked if removing a fast track
+                if (cblkOld != NULL) {
+                    block = FastCaptureStateQueue::BLOCK_UNTIL_ACKED;
+                }
+                didModify = true;
+            }
+            sq->end(didModify);
+            if (didModify) {
+                sq->push(block);
 #if 0
                 if (kUseFastCapture == FastCapture_Dynamic) {
                     mNormalSource = mPipeSource;
                 }
 #endif
-            } else {
-                sq->end(false /*didModify*/);
             }
         }
 
+        // now run the fast track destructor with thread mutex unlocked
+        fastTrackToRemove.clear();
+
         // Read from HAL to keep up with fastest client if multiple active tracks, not slowest one.
         // Only the client(s) that are too slow will overrun. But if even the fastest client is too
         // slow, then this RecordThread will overrun by not calling HAL read often enough.