fifo: add new APIs, and other cleanup

New APIs:
 - buffer()
 - capacity()
 - flush()
 - frameSize()
 - totalFlushed()
 - totalLost()
 - totalReleased()

Pull up mFifo from writer and reader into the base provider class.

Update documentation.

Test: see unit tests in tests/ directory
Change-Id: I1957638c69e1c1dc742b6f024156def757736867
diff --git a/audio_utils/fifo.cpp b/audio_utils/fifo.cpp
index fb489e2..5fc8c44 100644
--- a/audio_utils/fifo.cpp
+++ b/audio_utils/fifo.cpp
@@ -185,8 +185,8 @@
 
 ////////////////////////////////////////////////////////////////////////////////
 
-audio_utils_fifo_provider::audio_utils_fifo_provider() :
-    mObtained(0)
+audio_utils_fifo_provider::audio_utils_fifo_provider(audio_utils_fifo& fifo) :
+    mFifo(fifo), mObtained(0), mTotalReleased(0)
 {
 }
 
@@ -197,7 +197,7 @@
 ////////////////////////////////////////////////////////////////////////////////
 
 audio_utils_fifo_writer::audio_utils_fifo_writer(audio_utils_fifo& fifo) :
-    audio_utils_fifo_provider(), mFifo(fifo), mLocalRear(0),
+    audio_utils_fifo_provider(fifo), mLocalRear(0),
     mLowLevelArm(fifo.mFrameCount), mHighLevelTrigger(0),
     mArmed(true),   // because initial fill level of zero is < mLowLevelArm
     mEffectiveFrames(fifo.mFrameCount)
@@ -373,6 +373,7 @@
                     std::memory_order_release);
         }
         mObtained -= count;
+        mTotalReleased += count;
     }
 }
 
@@ -431,10 +432,11 @@
 ////////////////////////////////////////////////////////////////////////////////
 
 audio_utils_fifo_reader::audio_utils_fifo_reader(audio_utils_fifo& fifo, bool throttlesWriter) :
-    audio_utils_fifo_provider(), mFifo(fifo), mLocalFront(0),
+    audio_utils_fifo_provider(fifo), mLocalFront(0),
     mThrottleFront(throttlesWriter ? mFifo.mThrottleFront : NULL),
     mHighLevelArm(-1), mLowLevelTrigger(mFifo.mFrameCount),
-    mArmed(true)    // because initial fill level of zero is > mHighLevelArm
+    mArmed(true),   // because initial fill level of zero is > mHighLevelArm
+    mTotalLost(0), mTotalFlushed(0)
 {
 }
 
@@ -514,6 +516,7 @@
             mLocalFront = mFifo.sum(mLocalFront, count);
         }
         mObtained -= count;
+        mTotalReleased += count;
     }
 }
 
@@ -580,7 +583,13 @@
         }
         timeout = NULL;
     }
+    size_t ourLost;
+    if (lost == NULL) {
+        lost = &ourLost;
+    }
     int32_t filled = mFifo.diff(rear, mLocalFront, lost);
+    mTotalLost += *lost;
+    mTotalReleased += *lost;
     if (filled < 0) {
         if (filled == -EOVERFLOW) {
             mLocalFront = rear;
@@ -621,6 +630,19 @@
     return obtain(NULL /*iovec*/, SIZE_MAX /*count*/, NULL /*timeout*/, lost);
 }
 
+ssize_t audio_utils_fifo_reader::flush(size_t *lost)
+{
+    audio_utils_iovec iovec[2];
+    ssize_t ret = obtain(iovec, SIZE_MAX /*count*/, NULL /*timeout*/, lost);
+    if (ret > 0) {
+        size_t flushed = (size_t) ret;
+        release(flushed);
+        mTotalFlushed += flushed;
+        ret = flushed;
+    }
+    return ret;
+}
+
 void audio_utils_fifo_reader::setHysteresis(int32_t highLevelArm, uint32_t lowLevelTrigger)
 {
     // cap to range [0, mFifo.mFrameCount]
diff --git a/audio_utils/include/audio_utils/fifo.h b/audio_utils/include/audio_utils/fifo.h
index 3eeff7b..bf28fe2 100644
--- a/audio_utils/include/audio_utils/fifo.h
+++ b/audio_utils/include/audio_utils/fifo.h
@@ -66,6 +66,16 @@
  */
 class audio_utils_fifo_base {
 
+public:
+
+    /**
+     * Return the capacity, or statically configured maximum frame count.
+     *
+     * \return The capacity in frames.
+     */
+    uint32_t capacity() const
+            { return mFrameCount; }
+
 protected:
 
     /**
@@ -100,14 +110,17 @@
      *              re-synchronization when -EOVERFLOW occurs, or set to zero when no frames lost.
      *
      * \return The zero or positive difference <= mFrameCount, or a negative error code.
+     * \retval -EIO        corrupted indices, no recovery is possible
+     * \retval -EOVERFLOW  reader doesn't throttle writer, and frames were lost because reader
+     *                     isn't keeping up with writer; see \p lost
      */
     int32_t diff(uint32_t rear, uint32_t front, size_t *lost = NULL);
 
     // These fields are const after initialization
 
-    /** Maximum usable frames to be stored in the FIFO > 0 && <= INT32_MAX, aka "capacity" */
+    /** Maximum usable frames to be stored in the FIFO > 0 && <= INT32_MAX, aka "capacity". */
     const uint32_t mFrameCount;
-    /** Equal to roundup(mFrameCount) */
+    /** Equal to roundup(mFrameCount). */
     const uint32_t mFrameCountP2;
 
     /**
@@ -136,7 +149,7 @@
  * Same as audio_utils_fifo_base, but understands frame sizes and knows about the buffer but does
  * not own it.
  */
-class audio_utils_fifo : audio_utils_fifo_base {
+class audio_utils_fifo : public audio_utils_fifo_base {
 
     friend class audio_utils_fifo_reader;
     friend class audio_utils_fifo_writer;
@@ -176,8 +189,23 @@
 
     /*virtual*/ ~audio_utils_fifo();
 
-private:
+    /**
+     * Return the frame size in bytes.
+     *
+     * \return frame size in bytes.
+     */
+    uint32_t frameSize() const
+            { return mFrameSize; }
 
+    /**
+     * Return a pointer to the caller-allocated buffer.
+     *
+     * \return pointer to buffer.
+     */
+    void *buffer() const
+            { return mBuffer; }
+
+private:
     // These fields are const after initialization
     const uint32_t mFrameSize;  // size of each frame in bytes
     void * const   mBuffer;     // pointer to caller-allocated buffer of size mFrameCount frames
@@ -207,7 +235,7 @@
  */
 class audio_utils_fifo_provider {
 public:
-    audio_utils_fifo_provider();
+    audio_utils_fifo_provider(audio_utils_fifo& fifo);
     virtual ~audio_utils_fifo_provider();
 
     /**
@@ -256,7 +284,7 @@
      * Release access to a portion of the most recently obtained slice.
      * It is permitted to call release() multiple times without an intervening obtain().
      *
-     * \param count Number of frames to release.  The total number of frames released must not
+     * \param count Number of frames to release.  The cumulative number of frames released must not
      *              exceed the number of frames most recently obtained.
      */
     virtual void release(size_t count) = 0;
@@ -273,12 +301,37 @@
      */
     virtual ssize_t available() = 0;
 
+    /**
+     * Return the capacity, or statically configured maximum frame count.
+     *
+     * \return The capacity in frames.
+     */
+    uint32_t capacity() const
+            { return mFifo.capacity(); }
+
+    /**
+     * Return the total number of frames released since construction.
+     * For a reader, this includes lost and flushed frames.
+     *
+     * \return Total frames released.
+     */
+    uint64_t totalReleased() const
+            { return mTotalReleased; }
+
 protected:
+    audio_utils_fifo&   mFifo;
+
     /** Number of frames obtained at most recent obtain(), less total number of frames released. */
     uint32_t    mObtained;
 
     /** Number of times to retry a futex wait that fails with EWOULDBLOCK. */
     static const int kRetries = 2;
+
+    /**
+     * Total number of frames released since construction.
+     * For a reader, this includes lost and flushed frames.
+     */
+    uint64_t    mTotalReleased;
 };
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -346,6 +399,7 @@
 
     /**
      * Get the current effective buffer size.
+     * This value is not exposed to reader(s), and so must be conveyed via an out-of-band channel.
      *
      * \return effective buffer size in frames
      */
@@ -378,12 +432,9 @@
     void getHysteresis(uint32_t *lowLevelArm, uint32_t *highLevelTrigger) const;
 
 private:
-    audio_utils_fifo&   mFifo;
-
     // Accessed by writer only using ordinary operations
     uint32_t    mLocalRear; // frame index of next frame slot available to write, or write index
 
-    // TODO needs a state transition diagram for threshold and arming process
     // TODO make a separate class and associate with the synchronization object
     uint32_t    mLowLevelArm;       // arm if filled < arm level before release()
     uint32_t    mHighLevelTrigger;  // trigger if armed and filled > trigger level after release()
@@ -481,6 +532,22 @@
     ssize_t available(size_t *lost);
 
     /**
+     * Flush (discard) all frames that could be obtained or read without blocking.
+     * Note that fluah is a method on a reader, so if the writer wants to flush
+     * then it must communicate the request to the reader(s) via an out-of-band channel.
+     *
+     * \param lost    If non-NULL, set to the approximate number of frames lost before
+     *                re-synchronization when -EOVERFLOW occurs, or set to zero when no frames lost.
+     *
+     * \return Number of flushed frames, if greater than or equal to zero.
+     *         This number does not include any lost frames.
+     *  \retval -EIO        corrupted indices, no recovery is possible
+     *  \retval -EOVERFLOW  reader doesn't throttle writer, and frames were lost because reader
+     *                      isn't keeping up with writer; see \p lost
+     */
+    ssize_t flush(size_t *lost = NULL);
+
+    /**
      * Set the hysteresis levels for a throttling reader to wake a blocked writer.
      * A non-empty read() or release() by a throttling reader will wake the writer
      * only if the fill level was > \p highLevelArm before the read() or release(),
@@ -508,9 +575,25 @@
      */
     void getHysteresis(int32_t *highLevelArm, uint32_t *lowLevelTrigger) const;
 
-private:
-    audio_utils_fifo&   mFifo;
+    /**
+     * Return the total number of lost frames since construction, due to reader not keeping up with
+     * writer.  Does not include flushed frames.
+     *
+     * \return Total lost frames.
+     */
+    uint64_t totalLost() const
+            { return mTotalLost; }
 
+    /**
+     * Return the total number of flushed frames since construction.
+     * Does not include lost frames.
+     *
+     * \return Total flushed frames.
+     */
+    uint64_t totalFlushed() const
+            { return mTotalFlushed; }
+
+private:
     // Accessed by reader only using ordinary operations
     uint32_t     mLocalFront;   // frame index of first frame slot available to read, or read index
 
@@ -518,10 +601,12 @@
     // FIXME consider making it a boolean
     audio_utils_fifo_index*     mThrottleFront;
 
-    // TODO not used yet, needs state transition diagram
     int32_t     mHighLevelArm;      // arm if filled > arm level before release()
     uint32_t    mLowLevelTrigger;   // trigger if armed and filled < trigger level after release()
     bool        mArmed;             // whether currently armed
+
+    uint64_t    mTotalLost;         // total lost frames, does not include flushed frames
+    uint64_t    mTotalFlushed;      // total flushed frames, does not include lost frames
 };
 
 #endif  // !ANDROID_AUDIO_FIFO_H