Merge "Switch audio tracks at runtime" into jb-dev
diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp
index 7ac0f23..3d7ea92 100644
--- a/media/libstagefright/ACodec.cpp
+++ b/media/libstagefright/ACodec.cpp
@@ -499,13 +499,6 @@
         return err;
     }
 
-    err = native_window_set_scaling_mode(mNativeWindow.get(),
-            NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW);
-
-    if (err != OK) {
-        return err;
-    }
-
     err = native_window_set_buffers_geometry(
             mNativeWindow.get(),
             def.format.video.nFrameWidth,
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 77b2bc8..51a8367 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -2760,35 +2760,98 @@
 
             // Determine whether the track is currently in underrun condition,
             // and whether it had a recent underrun.
-            uint32_t underruns = mFastMixerDumpState.mTracks[j].mUnderruns;
-            uint32_t recentUnderruns = (underruns - (track->mObservedUnderruns & ~1)) >> 1;
+            FastTrackUnderruns underruns = mFastMixerDumpState.mTracks[j].mUnderruns;
+            uint32_t recentFull = (underruns.mBitFields.mFull -
+                    track->mObservedUnderruns.mBitFields.mFull) & UNDERRUN_MASK;
+            uint32_t recentPartial = (underruns.mBitFields.mPartial -
+                    track->mObservedUnderruns.mBitFields.mPartial) & UNDERRUN_MASK;
+            uint32_t recentEmpty = (underruns.mBitFields.mEmpty -
+                    track->mObservedUnderruns.mBitFields.mEmpty) & UNDERRUN_MASK;
+            uint32_t recentUnderruns = recentPartial + recentEmpty;
+            track->mObservedUnderruns = underruns;
             // don't count underruns that occur while stopping or pausing
-            if (!(track->isStopped() || track->isPausing())) {
+            // or stopped which can occur when flush() is called while active
+            if (!(track->isStopping() || track->isPausing() || track->isStopped())) {
                 track->mUnderrunCount += recentUnderruns;
             }
-            track->mObservedUnderruns = underruns;
 
-            // This is similar to the formula for normal tracks,
+            // This is similar to the state machine for normal tracks,
             // with a few modifications for fast tracks.
-            bool isActive;
-            if (track->isStopped()) {
-                // track stays active after stop() until first underrun
-                isActive = recentUnderruns == 0;
-            } else if (track->isPaused() || track->isTerminated()) {
-                isActive = false;
-            } else if (track->isPausing()) {
+            bool isActive = true;
+            switch (track->mState) {
+            case TrackBase::STOPPING_1:
+                // track stays active in STOPPING_1 state until first underrun
+                if (recentUnderruns > 0) {
+                    track->mState = TrackBase::STOPPING_2;
+                }
+                break;
+            case TrackBase::PAUSING:
                 // ramp down is not yet implemented
-                isActive = true;
                 track->setPaused();
-            } else if (track->isResuming()) {
+                break;
+            case TrackBase::RESUMING:
                 // ramp up is not yet implemented
-                isActive = true;
                 track->mState = TrackBase::ACTIVE;
-            } else {
-                // no minimum frame count for fast tracks; continual underrun is allowed,
-                // but later could implement automatic pause after several consecutive underruns,
-                // or auto-mute yet still consider the track active and continue to service it
-                isActive = true;
+                break;
+            case TrackBase::ACTIVE:
+                if (recentFull > 0 || recentPartial > 0) {
+                    // track has provided at least some frames recently: reset retry count
+                    track->mRetryCount = kMaxTrackRetries;
+                }
+                if (recentUnderruns == 0) {
+                    // no recent underruns: stay active
+                    break;
+                }
+                // there has recently been an underrun of some kind
+                if (track->sharedBuffer() == 0) {
+                    // were any of the recent underruns "empty" (no frames available)?
+                    if (recentEmpty == 0) {
+                        // no, then ignore the partial underruns as they are allowed indefinitely
+                        break;
+                    }
+                    // there has recently been an "empty" underrun: decrement the retry counter
+                    if (--(track->mRetryCount) > 0) {
+                        break;
+                    }
+                    // indicate to client process that the track was disabled because of underrun;
+                    // it will then automatically call start() when data is available
+                    android_atomic_or(CBLK_DISABLED_ON, &track->mCblk->flags);
+                    // remove from active list, but state remains ACTIVE [confusing but true]
+                    isActive = false;
+                    break;
+                }
+                // fall through
+            case TrackBase::STOPPING_2:
+            case TrackBase::PAUSED:
+            case TrackBase::TERMINATED:
+            case TrackBase::STOPPED:    // 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
+                {
+                    size_t audioHALFrames =
+                            (mOutput->stream->get_latency(mOutput->stream)*mSampleRate) / 1000;
+                    size_t framesWritten =
+                            mBytesWritten / audio_stream_frame_size(&mOutput->stream->common);
+                    if (!track->presentationComplete(framesWritten, audioHALFrames)) {
+                        // track stays in active list until presentation is complete
+                        break;
+                    }
+                }
+                if (track->isStopping_2()) {
+                    track->mState = TrackBase::STOPPED;
+                }
+                if (track->isStopped()) {
+                    // Can't reset directly, as fast mixer is still polling this track
+                    //   track->reset();
+                    // So instead mark this track as needing to be reset after push with ack
+                    resetMask |= 1 << i;
+                }
+                isActive = false;
+                break;
+            case TrackBase::IDLE:
+            default:
+                LOG_FATAL("unexpected track state %d", track->mState);
             }
 
             if (isActive) {
@@ -2820,24 +2883,12 @@
                     // If any fast tracks were removed, we must wait for acknowledgement
                     // because we're about to decrement the last sp<> on those tracks.
                     block = FastMixerStateQueue::BLOCK_UNTIL_ACKED;
+                } else {
+                    LOG_FATAL("fast track %d should have been active", j);
                 }
-                // Remainder of this block is copied from similar code for normal tracks
-                if (track->isStopped()) {
-                    // Can't reset directly, as fast mixer is still polling this track
-                    //   track->reset();
-                    // So instead mark this track as needing to be reset after push with ack
-                    resetMask |= 1 << i;
-                }
-                // This would be incomplete if we auto-paused on underrun
-                size_t audioHALFrames =
-                        (mOutput->stream->get_latency(mOutput->stream)*mSampleRate) / 1000;
-                size_t framesWritten =
-                        mBytesWritten / audio_stream_frame_size(&mOutput->stream->common);
-                if (track->presentationComplete(framesWritten, audioHALFrames)) {
-                    tracksToRemove->add(track);
-                }
+                tracksToRemove->add(track);
                 // Avoids a misleading display in dumpsys
-                track->mObservedUnderruns &= ~1;
+                track->mObservedUnderruns.mBitFields.mMostRecent = UNDERRUN_FULL;
             }
             continue;
         }
@@ -4082,7 +4133,6 @@
     mPresentationCompleteFrames(0),
     mFlags(flags),
     mFastIndex(-1),
-    mObservedUnderruns(0),
     mUnderrunCount(0),
     mCachedVolume(1.0)
 {
@@ -4101,7 +4151,7 @@
             //       being created.  It would be better to allocate the index dynamically.
             mFastIndex = i;
             // Read the initial underruns because this field is never cleared by the fast mixer
-            mObservedUnderruns = thread->getFastTrackUnderruns(i) & ~1;
+            mObservedUnderruns = thread->getFastTrackUnderruns(i);
             thread->mFastTrackAvailMask &= ~(1 << i);
         }
         // to avoid leaking a track name, do not allocate one unless there is an mCblk
@@ -4181,6 +4231,12 @@
     case TERMINATED:
         stateChar = 'T';
         break;
+    case STOPPING_1:
+        stateChar = 's';
+        break;
+    case STOPPING_2:
+        stateChar = '5';
+        break;
     case STOPPED:
         stateChar = 'S';
         break;
@@ -4200,7 +4256,21 @@
         stateChar = '?';
         break;
     }
-    bool nowInUnderrun = mObservedUnderruns & 1;
+    char nowInUnderrun;
+    switch (mObservedUnderruns.mBitFields.mMostRecent) {
+    case UNDERRUN_FULL:
+        nowInUnderrun = ' ';
+        break;
+    case UNDERRUN_PARTIAL:
+        nowInUnderrun = '<';
+        break;
+    case UNDERRUN_EMPTY:
+        nowInUnderrun = '*';
+        break;
+    default:
+        nowInUnderrun = '?';
+        break;
+    }
     snprintf(&buffer[7], size-7, " %6d %4u %3u 0x%08x %7u %6u %6u %1c %1d %1d %5u %5.2g %5.2g  "
             "0x%08x 0x%08x 0x%08x 0x%08x %#5x %9u%c\n",
             (mClient == 0) ? getpid_cached : mClient->pid(),
@@ -4222,7 +4292,7 @@
             (int)mAuxBuffer,
             mCblk->flags,
             mUnderrunCount,
-            nowInUnderrun ? '*' : ' ');
+            nowInUnderrun);
 }
 
 // AudioBufferProvider interface
@@ -4353,14 +4423,20 @@
     if (thread != 0) {
         Mutex::Autolock _l(thread->mLock);
         track_state state = mState;
-        if (mState > STOPPED) {
-            mState = STOPPED;
+        if (state == RESUMING || state == ACTIVE || state == PAUSING || state == PAUSED) {
             // If the track is not active (PAUSED and buffers full), flush buffers
             PlaybackThread *playbackThread = (PlaybackThread *)thread.get();
             if (playbackThread->mActiveTracks.indexOf(this) < 0) {
                 reset();
+                mState = STOPPED;
+            } else if (!isFastTrack()) {
+                mState = STOPPED;
+            } else {
+                // prepareTracks_l() will set state to STOPPING_2 after next underrun,
+                // and then to STOPPED and reset() when presentation is complete
+                mState = STOPPING_1;
             }
-            ALOGV("(> STOPPED) => STOPPED (%d) on thread %p", mName, playbackThread);
+            ALOGV("not stopping/stopped => stopping/stopped (%d) on thread %p", mName, playbackThread);
         }
         if (!isOutputTrack() && (state == ACTIVE || state == RESUMING)) {
             thread->mLock.unlock();
@@ -4404,15 +4480,17 @@
     sp<ThreadBase> thread = mThread.promote();
     if (thread != 0) {
         Mutex::Autolock _l(thread->mLock);
-        if (mState != STOPPED && mState != PAUSED && mState != PAUSING) {
+        if (mState != STOPPING_1 && mState != STOPPING_2 && mState != STOPPED && mState != PAUSED &&
+                mState != PAUSING) {
             return;
         }
         // No point remaining in PAUSED state after a flush => go to
         // STOPPED state
         mState = STOPPED;
-
         // 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
+        // remove from active track list, reset(), and trigger presentation complete
         PlaybackThread *playbackThread = (PlaybackThread *)thread.get();
         if (playbackThread->mActiveTracks.indexOf(this) < 0) {
             reset();
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index f10295f..88056b8 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -365,6 +365,9 @@
                 // These are order-sensitive; do not change order without reviewing the impact.
                 // In particular there are assumptions about > STOPPED.
                 STOPPED,
+                // next 2 states are currently used for fast tracks only
+                STOPPING_1,     // waiting for first underrun
+                STOPPING_2,     // waiting for presentation complete
                 RESUMING,
                 ACTIVE,
                 PAUSING,
@@ -417,6 +420,17 @@
                 return mState == STOPPED;
             }
 
+            // for fast tracks only
+            bool isStopping() const {
+                return mState == STOPPING_1 || mState == STOPPING_2;
+            }
+            bool isStopping_1() const {
+                return mState == STOPPING_1;
+            }
+            bool isStopping_2() const {
+                return mState == STOPPING_2;
+            }
+
             bool isTerminated() const {
                 return mState == TERMINATED;
             }
@@ -779,7 +793,7 @@
                                             // index 0 is reserved for normal mixer's submix;
                                             // index is allocated statically at track creation time
                                             // but the slot is only used if track is active
-            uint32_t            mObservedUnderruns; // Most recently observed value of
+            FastTrackUnderruns  mObservedUnderruns; // Most recently observed value of
                                             // mFastMixerDumpState.mTracks[mFastIndex].mUnderruns
             uint32_t            mUnderrunCount; // Counter of total number of underruns, never reset
             volatile float      mCachedVolume;  // combined master volume and stream type volume;
@@ -1098,7 +1112,8 @@
         sp<NBAIO_Sink>          mNormalSink;
     public:
         virtual     bool        hasFastMixer() const = 0;
-        virtual     uint32_t    getFastTrackUnderruns(size_t fastIndex) const { return 0; }
+        virtual     FastTrackUnderruns getFastTrackUnderruns(size_t fastIndex) const
+                                    { FastTrackUnderruns dummy; return dummy; }
 
     protected:
                     // accessed by both binder threads and within threadLoop(), lock on mutex needed
@@ -1153,10 +1168,10 @@
 
     public:
         virtual     bool        hasFastMixer() const { return mFastMixer != NULL; }
-        virtual     uint32_t    getFastTrackUnderruns(size_t fastIndex) const {
-                                    ALOG_ASSERT(0 < fastIndex &&
-                                            fastIndex < FastMixerState::kMaxFastTracks);
-                                    return mFastMixerDumpState.mTracks[fastIndex].mUnderruns;
+        virtual     FastTrackUnderruns getFastTrackUnderruns(size_t fastIndex) const {
+                                  ALOG_ASSERT(0 < fastIndex &&
+                                              fastIndex < FastMixerState::kMaxFastTracks);
+                                  return mFastMixerDumpState.mTracks[fastIndex].mUnderruns;
                                 }
     };
 
diff --git a/services/audioflinger/FastMixer.cpp b/services/audioflinger/FastMixer.cpp
index 04d0f65..cd55396 100644
--- a/services/audioflinger/FastMixer.cpp
+++ b/services/audioflinger/FastMixer.cpp
@@ -360,20 +360,25 @@
                 // in the overall fast mix cycle being delayed.  Should use a non-blocking FIFO.
                 size_t framesReady = fastTrack->mBufferProvider->framesReady();
                 FastTrackDump *ftDump = &dumpState->mTracks[i];
-                uint32_t underruns = ftDump->mUnderruns;
+                FastTrackUnderruns underruns = ftDump->mUnderruns;
                 if (framesReady < frameCount) {
                     ATRACE_INT("underrun", i);
-                    ftDump->mUnderruns = (underruns + 2) | 1;
                     if (framesReady == 0) {
+                        underruns.mBitFields.mEmpty++;
+                        underruns.mBitFields.mMostRecent = UNDERRUN_EMPTY;
                         mixer->disable(name);
                     } else {
                         // allow mixing partial buffer
+                        underruns.mBitFields.mPartial++;
+                        underruns.mBitFields.mMostRecent = UNDERRUN_PARTIAL;
                         mixer->enable(name);
                     }
-                } else if (underruns & 1) {
-                    ftDump->mUnderruns = underruns & ~1;
+                } else {
+                    underruns.mBitFields.mFull++;
+                    underruns.mBitFields.mMostRecent = UNDERRUN_FULL;
                     mixer->enable(name);
                 }
+                ftDump->mUnderruns = underruns;
             }
             // process() is CPU-bound
             mixer->process(AudioBufferProvider::kInvalidPTS);
diff --git a/services/audioflinger/FastMixer.h b/services/audioflinger/FastMixer.h
index e2ed553..e95abf6 100644
--- a/services/audioflinger/FastMixer.h
+++ b/services/audioflinger/FastMixer.h
@@ -17,6 +17,7 @@
 #ifndef ANDROID_AUDIO_FAST_MIXER_H
 #define ANDROID_AUDIO_FAST_MIXER_H
 
+#include <utils/Debug.h>
 #include <utils/Thread.h>
 extern "C" {
 #include "../private/bionic_futex.h"
@@ -42,16 +43,38 @@
 
 };  // class FastMixer
 
+// Describes the underrun status for a single "pull" attempt
+enum FastTrackUnderrunStatus {
+    UNDERRUN_FULL,      // framesReady() is full frame count, no underrun
+    UNDERRUN_PARTIAL,   // framesReady() is non-zero but < full frame count, partial underrun
+    UNDERRUN_EMPTY,     // framesReady() is zero, total underrun
+};
+
+// Underrun counters are not reset to zero for new tracks or if track generation changes.
+// This packed representation is used to keep the information atomic.
+union FastTrackUnderruns {
+    FastTrackUnderruns() { mAtomic = 0;
+            COMPILE_TIME_ASSERT_FUNCTION_SCOPE(sizeof(FastTrackUnderruns) == sizeof(uint32_t)); }
+    FastTrackUnderruns(const FastTrackUnderruns& copyFrom) : mAtomic(copyFrom.mAtomic) { }
+    FastTrackUnderruns& operator=(const FastTrackUnderruns& rhs)
+            { if (this != &rhs) mAtomic = rhs.mAtomic; return *this; }
+    struct {
+#define UNDERRUN_BITS 10
+#define UNDERRUN_MASK ((1 << UNDERRUN_BITS) - 1)
+        uint32_t mFull    : UNDERRUN_BITS; // framesReady() is full frame count
+        uint32_t mPartial : UNDERRUN_BITS; // framesReady() is non-zero but < full frame count
+        uint32_t mEmpty   : UNDERRUN_BITS; // framesReady() is zero
+        FastTrackUnderrunStatus mMostRecent : 2;    // status of most recent framesReady()
+    }        mBitFields;
+private:
+    uint32_t mAtomic;
+};
+
 // Represents the dump state of a fast track
 struct FastTrackDump {
-    FastTrackDump() : mUnderruns(0) { }
+    FastTrackDump() { }
     /*virtual*/ ~FastTrackDump() { }
-    uint32_t mUnderruns;        // Underrun status, represented as follows:
-                                //   bit 0 == 0 means not currently in underrun
-                                //   bit 0 == 1 means currently in underrun
-                                //   bits 1 to 31 == total number of underruns
-                                // Not reset to zero for new tracks or if track generation changes.
-                                // This representation is used to keep the information atomic.
+    FastTrackUnderruns mUnderruns;
 };
 
 // The FastMixerDumpState keeps a cache of FastMixer statistics that can be logged by dumpsys.