Merge "AudioTrack: Add getUnderrunCount()"
diff --git a/include/media/AudioTrack.h b/include/media/AudioTrack.h
index bc010b4..38adb03 100644
--- a/include/media/AudioTrack.h
+++ b/include/media/AudioTrack.h
@@ -305,6 +305,11 @@
      */
             uint32_t    latency() const     { return mLatency; }
 
+    /* Returns the number of application-level buffer underruns
+     * since the AudioTrack was created.
+     */
+            uint32_t    getUnderrunCount() const;
+
     /* getters, see constructors and set() */
 
             audio_stream_type_t streamType() const;
@@ -803,6 +808,8 @@
             // FIXME enum is faster than strcmp() for parameter 'from'
             status_t restoreTrack_l(const char *from);
 
+            uint32_t    getUnderrunCount_l() const;
+
             bool     isOffloaded() const;
             bool     isDirect() const;
             bool     isOffloadedOrDirect() const;
@@ -934,6 +941,8 @@
     bool                    mRetrogradeMotionReported; // reduce log spam
     AudioTimestamp          mPreviousTimestamp;     // used to detect retrograde motion
 
+    uint32_t                mUnderrunCountOffset;   // updated when restoring tracks
+
     audio_output_flags_t    mFlags;
         // const after set(), except for bits AUDIO_OUTPUT_FLAG_FAST and AUDIO_OUTPUT_FLAG_OFFLOAD.
         // mLock must be held to read or write those bits reliably.
diff --git a/include/private/media/AudioTrackShared.h b/include/private/media/AudioTrackShared.h
index 68d27bb..770f007 100644
--- a/include/private/media/AudioTrackShared.h
+++ b/include/private/media/AudioTrackShared.h
@@ -59,7 +59,8 @@
     volatile int32_t mRear;     // written by producer (output: client, input: server)
     volatile int32_t mFlush;    // incremented by client to indicate a request to flush;
                                 // server notices and discards all data between mFront and mRear
-    volatile uint32_t mUnderrunFrames;  // server increments for each unavailable but desired frame
+    volatile uint32_t mUnderrunFrames; // server increments for each unavailable but desired frame
+    volatile uint32_t mUnderrunCount;  // server increments for each underrun occurrence
 };
 
 // Represents a single state of an AudioTrack that was created in static mode (shared memory buffer
@@ -174,8 +175,6 @@
 
     volatile    int32_t     mFlags;         // combinations of CBLK_*
 
-                // Cache line boundary (32 bytes)
-
 public:
                 union {
                     AudioTrackSharedStreaming   mStreaming;
@@ -358,6 +357,9 @@
     virtual uint32_t    getUnderrunFrames() const {
         return mCblk->u.mStreaming.mUnderrunFrames;
     }
+    virtual uint32_t    getUnderrunCount() const {
+        return mCblk->u.mStreaming.mUnderrunCount;
+    }
 
     bool        clearStreamEndDone();   // and return previous value
 
@@ -482,7 +484,8 @@
     AudioTrackServerProxy(audio_track_cblk_t* cblk, void *buffers, size_t frameCount,
             size_t frameSize, bool clientInServer = false, uint32_t sampleRate = 0)
         : ServerProxy(cblk, buffers, frameCount, frameSize, true /*isOut*/, clientInServer),
-          mPlaybackRateObserver(&cblk->mPlaybackRateQueue) {
+          mPlaybackRateObserver(&cblk->mPlaybackRateQueue),
+          mUnderrunCount(0), mUnderrunning(false) {
         mCblk->mSampleRate = sampleRate;
         mPlaybackRate = AUDIO_PLAYBACK_RATE_DEFAULT;
     }
@@ -525,6 +528,10 @@
 private:
     AudioPlaybackRate             mPlaybackRate;  // last observed playback rate
     PlaybackRateQueue::Observer   mPlaybackRateObserver;
+
+    // The server keeps a copy here where it is safe from the client.
+    uint32_t                      mUnderrunCount; // echoed to mCblk
+    bool                          mUnderrunning;  // used to detect edge of underrun
 };
 
 class StaticAudioTrackServerProxy : public AudioTrackServerProxy {
diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp
index 63cda36..a13d53a 100644
--- a/media/libmedia/AudioTrack.cpp
+++ b/media/libmedia/AudioTrack.cpp
@@ -498,6 +498,7 @@
     mPreviousTimestampValid = false;
     mTimestampStartupGlitchReported = false;
     mRetrogradeMotionReported = false;
+    mUnderrunCountOffset = 0;
 
     return NO_ERROR;
 }
@@ -2150,6 +2151,9 @@
         return DEAD_OBJECT;
     }
 
+    // Save so we can return count since creation.
+    mUnderrunCountOffset = getUnderrunCount_l();
+
     // save the old static buffer position
     size_t bufferPosition = 0;
     int loopCount = 0;
@@ -2465,6 +2469,17 @@
     return NO_ERROR;
 }
 
+uint32_t AudioTrack::getUnderrunCount() const
+{
+    AutoMutex lock(mLock);
+    return getUnderrunCount_l();
+}
+
+uint32_t AudioTrack::getUnderrunCount_l() const
+{
+    return mProxy->getUnderrunCount() + mUnderrunCountOffset;
+}
+
 uint32_t AudioTrack::getUnderrunFrames() const
 {
     AutoMutex lock(mLock);
diff --git a/media/libmedia/AudioTrackShared.cpp b/media/libmedia/AudioTrackShared.cpp
index 0d43da8..2742db0 100644
--- a/media/libmedia/AudioTrackShared.cpp
+++ b/media/libmedia/AudioTrackShared.cpp
@@ -804,10 +804,25 @@
 void AudioTrackServerProxy::tallyUnderrunFrames(uint32_t frameCount)
 {
     audio_track_cblk_t* cblk = mCblk;
-    cblk->u.mStreaming.mUnderrunFrames += frameCount;
+    if (frameCount > 0) {
+        cblk->u.mStreaming.mUnderrunFrames += frameCount;
 
-    // FIXME also wake futex so that underrun is noticed more quickly
-    (void) android_atomic_or(CBLK_UNDERRUN, &cblk->mFlags);
+        if (!mUnderrunning) { // start of underrun?
+            mUnderrunCount++;
+            cblk->u.mStreaming.mUnderrunCount = mUnderrunCount;
+            mUnderrunning = true;
+            ALOGV("tallyUnderrunFrames(%3u) at uf = %u, bump mUnderrunCount = %u",
+                frameCount, cblk->u.mStreaming.mUnderrunFrames, mUnderrunCount);
+        }
+
+        // FIXME also wake futex so that underrun is noticed more quickly
+        (void) android_atomic_or(CBLK_UNDERRUN, &cblk->mFlags);
+    } else {
+        ALOGV_IF(mUnderrunning,
+            "tallyUnderrunFrames(%3u) at uf = %u, underrun finished",
+            frameCount, cblk->u.mStreaming.mUnderrunFrames);
+        mUnderrunning = false; // so we can detect the next edge
+    }
 }
 
 AudioPlaybackRate AudioTrackServerProxy::getPlaybackRate()
@@ -1033,7 +1048,7 @@
     buffer->mNonContig = 0;
 }
 
-void StaticAudioTrackServerProxy::tallyUnderrunFrames(uint32_t frameCount __unused)
+void StaticAudioTrackServerProxy::tallyUnderrunFrames(uint32_t frameCount)
 {
     // Unlike AudioTrackServerProxy::tallyUnderrunFrames() used for streaming tracks,
     // we don't have a location to count underrun frames.  The underrun frame counter
@@ -1041,7 +1056,9 @@
     // possible for static buffer tracks other than at end of buffer, so this is not a loss.
 
     // FIXME also wake futex so that underrun is noticed more quickly
-    (void) android_atomic_or(CBLK_UNDERRUN, &mCblk->mFlags);
+    if (frameCount > 0) {
+        (void) android_atomic_or(CBLK_UNDERRUN, &mCblk->mFlags);
+    }
 }
 
 // ---------------------------------------------------------------------------
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index 65166b0..0458554 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -3788,6 +3788,8 @@
                     recentUnderruns > 0) {
                 // FIXME fast mixer will pull & mix partial buffers, but we count as a full underrun
                 track->mAudioTrackServerProxy->tallyUnderrunFrames(recentUnderruns * mFrameCount);
+            } else {
+                track->mAudioTrackServerProxy->tallyUnderrunFrames(0);
             }
 
             // This is similar to the state machine for normal tracks,
@@ -4157,7 +4159,10 @@
                 ALOGV("track(%p) underrun,  framesReady(%zu) < framesDesired(%zd)",
                         track, framesReady, desiredFrames);
                 track->mAudioTrackServerProxy->tallyUnderrunFrames(desiredFrames);
+            } else {
+                track->mAudioTrackServerProxy->tallyUnderrunFrames(0);
             }
+
             // clear effect chain input buffer if an active track underruns to avoid sending
             // previous audio buffer again to effects
             chain = getEffectChain_l(track->sessionId());
diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp
index b1638ea..c753afd 100644
--- a/services/audioflinger/Tracks.cpp
+++ b/services/audioflinger/Tracks.cpp
@@ -600,7 +600,10 @@
     buffer->raw = buf.mRaw;
     if (buf.mFrameCount == 0) {
         mAudioTrackServerProxy->tallyUnderrunFrames(desiredFrames);
+    } else {
+        mAudioTrackServerProxy->tallyUnderrunFrames(0);
     }
+
     return status;
 }