Implement client playback timestamps with 64 bit accuracy

Provide server timestamps if the HAL doesn't provide it.
Provide monotonic - boottime translation.

Bug: 17472992
Bug: 26682703
Bug: 27749434
Change-Id: I6c9b213d9f9284092e34d57f52870e02c72df62a
diff --git a/include/media/AudioTimestamp.h b/include/media/AudioTimestamp.h
index 531b548..969003c 100644
--- a/include/media/AudioTimestamp.h
+++ b/include/media/AudioTimestamp.h
@@ -69,12 +69,23 @@
     // or NTP adjustment.
     int64_t mTimebaseOffset[TIMEBASE_MAX];
 
+    // Playback only:
+    // mFlushed is number of flushed frames before entering the server mix;
+    // hence not included in mPosition. This is used for adjusting server positions
+    // information for frames "dropped".
+    // FIXME: This variable should be eliminated, with the offset added on the server side
+    // before sending to client, but differences in legacy position offset handling
+    // and new extended timestamps require this to be maintained as a separate quantity.
+    int64_t mFlushed;
+
+    // Call to reset the timestamp to the original (invalid) state
     void clear() {
         memset(mPosition, 0, sizeof(mPosition)); // actually not necessary if time is -1
         for (int i = 0; i < LOCATION_MAX; ++i) {
             mTimeNs[i] = -1;
         }
         memset(mTimebaseOffset, 0, sizeof(mTimebaseOffset));
+        mFlushed = 0;
     }
 
     // Returns the best timestamp as judged from the closest-to-hw stage in the
diff --git a/include/media/AudioTrack.h b/include/media/AudioTrack.h
index bdd6372..b96a450 100644
--- a/include/media/AudioTrack.h
+++ b/include/media/AudioTrack.h
@@ -727,12 +727,51 @@
      *                     because the audio device changed or AudioFlinger died.
      *                     This typically occurs for direct or offload tracks
      *                     or if mDoNotReconnect is true.
-     *         INVALID_OPERATION  if called on a FastTrack, wrong state, or some other error.
+     *         INVALID_OPERATION  wrong state, or some other error.
      *
      * The timestamp parameter is undefined on return, if status is not NO_ERROR.
      */
             status_t    getTimestamp(AudioTimestamp& timestamp);
 
+    /* Return the extended timestamp, with additional timebase info and improved drain behavior.
+     *
+     * This is similar to the AudioTrack.java API:
+     * getTimestamp(@NonNull AudioTimestamp timestamp, @AudioTimestamp.Timebase int timebase)
+     *
+     * Some differences between this method and the getTimestamp(AudioTimestamp& timestamp) method
+     *
+     *   1. stop() by itself does not reset the frame position.
+     *      A following start() resets the frame position to 0.
+     *   2. flush() by itself does not reset the frame position.
+     *      The frame position advances by the number of frames flushed,
+     *      when the first frame after flush reaches the audio sink.
+     *   3. BOOTTIME clock offsets are provided to help synchronize with
+     *      non-audio streams, e.g. sensor data.
+     *   4. Position is returned with 64 bits of resolution.
+     *
+     * Parameters:
+     *  timestamp: A pointer to the caller allocated ExtendedTimestamp.
+     *
+     * Returns NO_ERROR    on success; timestamp is filled with valid data.
+     *         BAD_VALUE   if timestamp is NULL.
+     *         WOULD_BLOCK if called immediately after start() when the number
+     *                     of frames consumed is less than the
+     *                     overall hardware latency to physical output. In WOULD_BLOCK cases,
+     *                     one might poll again, or use getPosition(), or use 0 position and
+     *                     current time for the timestamp.
+     *                     If WOULD_BLOCK is returned, the timestamp is still
+     *                     modified with the LOCATION_CLIENT portion filled.
+     *         DEAD_OBJECT if AudioFlinger dies or the output device changes and
+     *                     the track cannot be automatically restored.
+     *                     The application needs to recreate the AudioTrack
+     *                     because the audio device changed or AudioFlinger died.
+     *                     This typically occurs for direct or offloaded tracks
+     *                     or if mDoNotReconnect is true.
+     *         INVALID_OPERATION  if called on a offloaded or direct track.
+     *                     Use getTimestamp(AudioTimestamp& timestamp) instead.
+     */
+            status_t getTimestamp(ExtendedTimestamp *timestamp);
+
     /* Add an AudioDeviceCallback. The caller will be notified when the audio device to which this
      * AudioTrack is routed is updated.
      * Replaces any previously installed callback.
@@ -956,6 +995,13 @@
 
     uint32_t                mUnderrunCountOffset;   // updated when restoring tracks
 
+    int64_t                 mFramesWritten;         // total frames written. reset to zero after
+                                                    // the start() following stop(). It is not
+                                                    // changed after restoring the track or
+                                                    // after flush.
+    int64_t                 mFramesWrittenServerOffset; // An offset to server frames due to
+                                                    // restoring AudioTrack, or stop/start.
+
     audio_output_flags_t    mFlags;                 // same as mOrigFlags, except for bits that may
                                                     // be denied by client or server, such as
                                                     // AUDIO_OUTPUT_FLAG_FAST.  mLock must be
diff --git a/include/private/media/AudioTrackShared.h b/include/private/media/AudioTrackShared.h
index 92cf6bb..ffdb9b5 100644
--- a/include/private/media/AudioTrackShared.h
+++ b/include/private/media/AudioTrackShared.h
@@ -522,6 +522,9 @@
         mTimestampMutator.push(timestamp);
     }
 
+    // Total count of the number of flushed frames since creation (never reset).
+    virtual int64_t     framesFlushed() const { return mFlushed; }
+
     // Get dynamic buffer size from the shared control block.
     uint32_t            getBufferSizeInFrames() const {
         return android_atomic_acquire_load((int32_t *)&mCblk->mBufferSizeInFrames);
@@ -531,7 +534,7 @@
     size_t      mAvailToClient; // estimated frames available to client prior to releaseBuffer()
     int32_t     mFlush;         // our copy of cblk->u.mStreaming.mFlush, for streaming output only
     int64_t     mReleased;      // our copy of cblk->mServer, at 64 bit resolution
-
+    int64_t     mFlushed;       // flushed frames to account for client-server discrepancy
     ExtendedTimestampQueue::Mutator mTimestampMutator;
 };
 
diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp
index e70c611..1de91bf 100644
--- a/media/libmedia/AudioTrack.cpp
+++ b/media/libmedia/AudioTrack.cpp
@@ -500,6 +500,8 @@
     mTimestampStartupGlitchReported = false;
     mRetrogradeMotionReported = false;
     mUnderrunCountOffset = 0;
+    mFramesWritten = 0;
+    mFramesWrittenServerOffset = 0;
 
     return NO_ERROR;
 }
@@ -537,6 +539,14 @@
         // Note: the if is technically unnecessary because previousState == STATE_FLUSHED
         // is only for streaming tracks, and mMarkerReached is already set to false.
         if (previousState == STATE_STOPPED) {
+            // read last server side position change via timestamp
+            ExtendedTimestamp ets;
+            if (mProxy->getTimestamp(&ets) == OK &&
+                    ets.mTimeNs[ExtendedTimestamp::LOCATION_SERVER] > 0) {
+                mFramesWrittenServerOffset = -(ets.mPosition[ExtendedTimestamp::LOCATION_SERVER]
+                                                             + ets.mFlushed);
+            }
+            mFramesWritten = 0;
             mProxy->clearTimestamp(); // need new server push for valid timestamp
             mMarkerReached = false;
         }
@@ -1657,6 +1667,9 @@
         releaseBuffer(&audioBuffer);
     }
 
+    if (written > 0) {
+        mFramesWritten += written / mFrameSize;
+    }
     return written;
 }
 
@@ -1923,6 +1936,7 @@
         requested = &timeout;
     }
 
+    size_t writtenFrames = 0;
     while (mRemainingFrames > 0) {
 
         Buffer audioBuffer;
@@ -2024,6 +2038,7 @@
         }
 
         releaseBuffer(&audioBuffer);
+        writtenFrames += releasedFrames;
 
         // FIXME here is where we would repeat EVENT_MORE_DATA again on same advanced buffer
         // if callback doesn't like to accept the full chunk
@@ -2047,6 +2062,10 @@
 #endif
 
     }
+    if (writtenFrames > 0) {
+        AutoMutex lock(mLock);
+        mFramesWritten += writtenFrames;
+    }
     mRemainingFrames = notificationFrames;
     mRetryOnPartialBuffer = true;
 
@@ -2109,6 +2128,7 @@
         }
         if (mState == STATE_ACTIVE) {
             result = mAudioTrack->start();
+            mFramesWrittenServerOffset = mFramesWritten; // server resets to zero so we offset
         }
     }
     if (result != NO_ERROR) {
@@ -2162,6 +2182,42 @@
     return mAudioTrack->setParameters(keyValuePairs);
 }
 
+status_t AudioTrack::getTimestamp(ExtendedTimestamp *timestamp)
+{
+    if (timestamp == nullptr) {
+        return BAD_VALUE;
+    }
+    AutoMutex lock(mLock);
+    if (mCblk->mFlags & CBLK_INVALID) {
+        const status_t status = restoreTrack_l("getTimestampExtended");
+        if (status != OK) {
+            // per getTimestamp() API doc in header, we return DEAD_OBJECT here,
+            // recommending that the track be recreated.
+            return DEAD_OBJECT;
+        }
+    }
+    // check for offloaded/direct here in case restoring somehow changed those flags.
+    if (isOffloadedOrDirect_l()) {
+        return INVALID_OPERATION; // not supported
+    }
+    status_t status = mProxy->getTimestamp(timestamp);
+    bool found = false;
+    if (status == OK) {
+        timestamp->mPosition[ExtendedTimestamp::LOCATION_CLIENT] = mFramesWritten;
+        timestamp->mTimeNs[ExtendedTimestamp::LOCATION_CLIENT] = 0;
+        // server side frame offset in case AudioTrack has been restored.
+        for (int i = ExtendedTimestamp::LOCATION_SERVER;
+                i < ExtendedTimestamp::LOCATION_MAX; ++i) {
+            if (timestamp->mTimeNs[i] >= 0) {
+                // apply server offset and the "flush frame correction here"
+                timestamp->mPosition[i] += mFramesWrittenServerOffset + timestamp->mFlushed;
+                found = true;
+            }
+        }
+    }
+    return found ? OK : WOULD_BLOCK;
+}
+
 status_t AudioTrack::getTimestamp(AudioTimestamp& timestamp)
 {
     AutoMutex lock(mLock);
diff --git a/media/libmedia/AudioTrackShared.cpp b/media/libmedia/AudioTrackShared.cpp
index 2396d87..7119517 100644
--- a/media/libmedia/AudioTrackShared.cpp
+++ b/media/libmedia/AudioTrackShared.cpp
@@ -615,7 +615,7 @@
 ServerProxy::ServerProxy(audio_track_cblk_t* cblk, void *buffers, size_t frameCount,
         size_t frameSize, bool isOut, bool clientInServer)
     : Proxy(cblk, buffers, frameCount, frameSize, isOut, clientInServer),
-      mAvailToClient(0), mFlush(0), mReleased(0)
+      mAvailToClient(0), mFlush(0), mReleased(0), mFlushed(0)
     , mTimestampMutator(&cblk->mExtendedTimestampQueue)
 {
     cblk->mBufferSizeInFrames = frameCount;
@@ -671,6 +671,7 @@
                             mClientInServer ? FUTEX_WAKE_PRIVATE : FUTEX_WAKE, 1);
                 }
             }
+            mFlushed += (newFront - front) & mask;
             front = newFront;
         }
     } else {
diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp
index 7cbb6b8..00de4f0 100644
--- a/services/audioflinger/Tracks.cpp
+++ b/services/audioflinger/Tracks.cpp
@@ -1113,6 +1113,9 @@
     if (!checked) { // no server info, assume drained.
         mAudioTrackServerProxy->setDrained(true);
     }
+    // Set correction for flushed frames that are not accounted for in released.
+    // This is important for the new 64 bit timestamps which do not reset to 0 on flush.
+    local.mFlushed = mAudioTrackServerProxy->framesFlushed();
     mServerProxy->setTimestamp(local);
 }