Fix issues with synchronous record start.

- Added a timeout in case the trigger event is never fired.
- Extend AudioRecord obtainBuffer() timeout in case of
synchronous start to avoid spurious warning.
- Make sure that the event is triggered if the track is
destroyed.
- Reject event if the triggering track is in an incompatible state.

Also fix a problem when restoring a static AudioTrack after
a mediaserver crash.

Bug 6449468.

Change-Id: Ib36e11111fb88f73caa31dcb0622792737d57a4b
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index e1fae82..5acf29a 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -515,7 +515,11 @@
         for (int i = 0; i < (int)mPendingSyncEvents.size(); i++) {
             if (mPendingSyncEvents[i]->triggerSession() == lSessionId) {
                 if (thread->isValidSyncEvent(mPendingSyncEvents[i])) {
-                    track->setSyncEvent(mPendingSyncEvents[i]);
+                    if (lStatus == NO_ERROR) {
+                        track->setSyncEvent(mPendingSyncEvents[i]);
+                    } else {
+                        mPendingSyncEvents[i]->cancel();
+                    }
                     mPendingSyncEvents.removeAt(i);
                     i--;
                 }
@@ -1847,6 +1851,7 @@
         // effectively get the latency it requested.
         track->mFillingUpStatus = Track::FS_FILLING;
         track->mResetDone = false;
+        track->mPresentationCompleteFrames = 0;
         mActiveTracks.add(track);
         if (track->mainBuffer() != mMixBuffer) {
             sp<EffectChain> chain = getEffectChain_l(track->sessionId());
@@ -1877,13 +1882,14 @@
 
 void AudioFlinger::PlaybackThread::removeTrack_l(const sp<Track>& track)
 {
+    track->triggerEvents(AudioSystem::SYNC_EVENT_PRESENTATION_COMPLETE);
     mTracks.remove(track);
     deleteTrackName_l(track->name());
     // redundant as track is about to be destroyed, for dumpsys only
     track->mName = -1;
     if (track->isFastTrack()) {
         int index = track->mFastIndex;
-        ALOG_ASSERT(0 < index && index < FastMixerState::kMaxFastTracks);
+        ALOG_ASSERT(0 < index && index < (int)FastMixerState::kMaxFastTracks);
         ALOG_ASSERT(!(mFastTrackAvailMask & (1 << index)));
         mFastTrackAvailMask |= 1 << index;
         // redundant as track is about to be destroyed, for dumpsys only
@@ -2850,7 +2856,8 @@
             case TrackBase::STOPPING_2:
             case TrackBase::PAUSED:
             case TrackBase::TERMINATED:
-            case TrackBase::STOPPED:    // flush() while active
+            case TrackBase::STOPPED:
+            case TrackBase::FLUSHED:   // flush() while active
                 // Check for presentation complete if track is inactive
                 // We have consumed all the buffers of this track.
                 // This would be incomplete if we auto-paused on underrun
@@ -3088,9 +3095,6 @@
             }
         } else {
             //ALOGV("track %d u=%08x, s=%08x [NOT READY] on thread %p", name, cblk->user, cblk->server, this);
-            if (track->isStopped()) {
-                track->reset();
-            }
             if ((track->sharedBuffer() != 0) || track->isTerminated() ||
                     track->isStopped() || track->isPaused()) {
                 // We have consumed all the buffers of this track.
@@ -3102,6 +3106,9 @@
                 size_t framesWritten =
                         mBytesWritten / audio_stream_frame_size(&mOutput->stream->common);
                 if (track->presentationComplete(framesWritten, audioHALFrames)) {
+                    if (track->isStopped()) {
+                        track->reset();
+                    }
                     tracksToRemove->add(track);
                 }
             } else {
@@ -3546,9 +3553,6 @@
             mixerStatus = MIXER_TRACKS_READY;
         } else {
             //ALOGV("track %d u=%08x, s=%08x [NOT READY]", track->name(), cblk->user, cblk->server);
-            if (track->isStopped()) {
-                track->reset();
-            }
             if (track->isTerminated() || track->isStopped() || track->isPaused()) {
                 // We have consumed all the buffers of this track.
                 // Remove it from the list of active tracks.
@@ -3558,6 +3562,9 @@
                 size_t framesWritten =
                         mBytesWritten / audio_stream_frame_size(&mOutput->stream->common);
                 if (track->presentationComplete(framesWritten, audioHALFrames)) {
+                    if (track->isStopped()) {
+                        track->reset();
+                    }
                     trackToRemove = track;
                 }
             } else {
@@ -4170,7 +4177,7 @@
             mCblk->flags |= CBLK_FAST;  // atomic op not needed yet
             ALOG_ASSERT(thread->mFastTrackAvailMask != 0);
             int i = __builtin_ctz(thread->mFastTrackAvailMask);
-            ALOG_ASSERT(0 < i && i < FastMixerState::kMaxFastTracks);
+            ALOG_ASSERT(0 < i && i < (int)FastMixerState::kMaxFastTracks);
             // FIXME This is too eager.  We allocate a fast track index before the
             //       fast track becomes active.  Since fast tracks are a scarce resource,
             //       this means we are potentially denying other more important fast tracks from
@@ -4278,6 +4285,9 @@
     case PAUSED:
         stateChar = 'P';
         break;
+    case FLUSHED:
+        stateChar = 'F';
+        break;
     default:
         stateChar = '?';
         break;
@@ -4435,6 +4445,7 @@
             playbackThread->addTrack_l(this);
         } else {
             mState = state;
+            triggerEvents(AudioSystem::SYNC_EVENT_PRESENTATION_COMPLETE);
         }
     } else {
         status = BAD_VALUE;
@@ -4511,11 +4522,11 @@
             return;
         }
         // No point remaining in PAUSED state after a flush => go to
-        // STOPPED state
-        mState = STOPPED;
+        // FLUSHED state
+        mState = FLUSHED;
         // do not reset the track if it is still in the process of being stopped or paused.
         // this will be done by prepareTracks_l() when the track is stopped.
-        // prepareTracks_l() will see mState == STOPPED, then
+        // prepareTracks_l() will see mState == FLUSHED, then
         // remove from active track list, reset(), and trigger presentation complete
         PlaybackThread *playbackThread = (PlaybackThread *)thread.get();
         if (playbackThread->mActiveTracks.indexOf(this) < 0) {
@@ -4536,7 +4547,9 @@
         android_atomic_or(CBLK_UNDERRUN_ON, &mCblk->flags);
         mFillingUpStatus = FS_FILLING;
         mResetDone = true;
-        mPresentationCompleteFrames = 0;
+        if (mState == FLUSHED) {
+            mState = IDLE;
+        }
     }
 }
 
@@ -4577,7 +4590,6 @@
         ALOGV("presentationComplete() session %d complete: framesWritten %d",
                   mSessionId, framesWritten);
         triggerEvents(AudioSystem::SYNC_EVENT_PRESENTATION_COMPLETE);
-        mPresentationCompleteFrames = 0;
         return true;
     }
     return false;
@@ -4621,6 +4633,20 @@
     return vlr;
 }
 
+status_t AudioFlinger::PlaybackThread::Track::setSyncEvent(const sp<SyncEvent>& event)
+{
+    if (mState == TERMINATED || mState == PAUSED ||
+            ((framesReady() == 0) && ((mSharedBuffer != 0) ||
+                                      (mState == STOPPED)))) {
+        ALOGW("Track::setSyncEvent() in invalid state %d on session %d %s mode, framesReady %d ",
+              mState, mSessionId, (mSharedBuffer != 0) ? "static" : "stream", framesReady());
+        event->cancel();
+        return INVALID_OPERATION;
+    }
+    TrackBase::setSyncEvent(event);
+    return NO_ERROR;
+}
+
 // timed audio tracks
 
 sp<AudioFlinger::PlaybackThread::TimedTrack>
@@ -5945,8 +5971,18 @@
                 } else {
                     if (mFramestoDrop > 0) {
                         mFramestoDrop -= buffer.frameCount;
-                        if (mFramestoDrop < 0) {
-                            mFramestoDrop = 0;
+                        if (mFramestoDrop <= 0) {
+                            clearSyncStartEvent();
+                        }
+                    } else {
+                        mFramestoDrop += buffer.frameCount;
+                        if (mFramestoDrop >= 0 || mSyncStartEvent == 0 ||
+                                mSyncStartEvent->isCancelled()) {
+                            ALOGW("Synced record %s, session %d, trigger session %d",
+                                  (mFramestoDrop >= 0) ? "timed out" : "cancelled",
+                                  mActiveTrack->sessionId(),
+                                  (mSyncStartEvent != 0) ? mSyncStartEvent->triggerSession() : 0);
+                            clearSyncStartEvent();
                         }
                     }
                 }
@@ -6040,15 +6076,21 @@
     status_t status = NO_ERROR;
 
     if (event == AudioSystem::SYNC_EVENT_NONE) {
-        mSyncStartEvent.clear();
-        mFramestoDrop = 0;
+        clearSyncStartEvent();
     } else if (event != AudioSystem::SYNC_EVENT_SAME) {
         mSyncStartEvent = mAudioFlinger->createSyncEvent(event,
                                        triggerSession,
                                        recordTrack->sessionId(),
                                        syncStartEventCallback,
                                        this);
-        mFramestoDrop = -1;
+        // Sync event can be cancelled by the trigger session if the track is not in a
+        // compatible state in which case we start record immediately
+        if (mSyncStartEvent->isCancelled()) {
+            clearSyncStartEvent();
+        } else {
+            // do not wait for the event for more than AudioSystem::kSyncRecordStartTimeOutMs
+            mFramestoDrop = - ((AudioSystem::kSyncRecordStartTimeOutMs * mReqSampleRate) / 1000);
+        }
     }
 
     {
@@ -6108,6 +6150,7 @@
         mSyncStartEvent->cancel();
     }
     mSyncStartEvent.clear();
+    mFramestoDrop = 0;
 }
 
 void AudioFlinger::RecordThread::syncStartEventCallback(const wp<SyncEvent>& event)
@@ -6122,17 +6165,10 @@
 
 void AudioFlinger::RecordThread::handleSyncStartEvent(const sp<SyncEvent>& event)
 {
-    ALOGV("handleSyncStartEvent() mActiveTrack %p session %d event->listenerSession() %d",
-              mActiveTrack.get(),
-              mActiveTrack.get() ? mActiveTrack->sessionId() : 0,
-              event->listenerSession());
-
-    if (mActiveTrack != 0 &&
-            event == mSyncStartEvent) {
+    if (event == mSyncStartEvent) {
         // TODO: use actual buffer filling status instead of 2 buffers when info is available
         // from audio HAL
         mFramestoDrop = mFrameCount * 2;
-        mSyncStartEvent.clear();
     }
 }