AudioFlinger: Add patch latency for direct record/playback

This relies on being able to obtain presentation position and
capture position accurately from the HAL, in the presence
of potential underruns or overruns.

Test: MSD hal dumpsys
Bug: 112428710
Change-Id: I9aad574baaff60b5e0c5d8c39a2147d19ee613f5
diff --git a/services/audioflinger/PatchPanel.cpp b/services/audioflinger/PatchPanel.cpp
index ada8572..7b165a1 100644
--- a/services/audioflinger/PatchPanel.cpp
+++ b/services/audioflinger/PatchPanel.cpp
@@ -547,14 +547,46 @@
     // reverse due to internal biases).
     //
     // TODO: is this stable enough? Consider a PatchTrack synchronized version of this.
-    double recordServerLatencyMs;
-    if (recordTrack->getServerLatencyMs(&recordServerLatencyMs) != OK) return INVALID_OPERATION;
 
-    double playbackTrackLatencyMs;
-    if (playbackTrack->getTrackLatencyMs(&playbackTrackLatencyMs) != OK) return INVALID_OPERATION;
+    // For PCM tracks get server latency.
+    if (audio_is_linear_pcm(recordTrack->format())) {
+        double recordServerLatencyMs, playbackTrackLatencyMs;
+        if (recordTrack->getServerLatencyMs(&recordServerLatencyMs) == OK
+                && playbackTrack->getTrackLatencyMs(&playbackTrackLatencyMs) == OK) {
+            *latencyMs = recordServerLatencyMs + playbackTrackLatencyMs;
+            return OK;
+        }
+    }
 
-    *latencyMs = recordServerLatencyMs + playbackTrackLatencyMs;
-    return OK;
+    // See if kernel latencies are available.
+    // If so, do a frame diff and time difference computation to estimate
+    // the total patch latency. This requires that frame counts are reported by the
+    // HAL are matched properly in the case of record overruns and playback underruns.
+    ThreadBase::TrackBase::FrameTime recordFT{}, playFT{};
+    recordTrack->getKernelFrameTime(&recordFT);
+    playbackTrack->getKernelFrameTime(&playFT);
+    if (recordFT.timeNs > 0 && playFT.timeNs > 0) {
+        const int64_t frameDiff = recordFT.frames - playFT.frames;
+        const int64_t timeDiffNs = recordFT.timeNs - playFT.timeNs;
+
+        // It is possible that the patch track and patch record have a large time disparity because
+        // one thread runs but another is stopped.  We arbitrarily choose the maximum timestamp
+        // time difference based on how often we expect the timestamps to update in normal operation
+        // (typical should be no more than 50 ms).
+        //
+        // If the timestamps aren't sampled close enough, the patch latency is not
+        // considered valid.
+        //
+        // TODO: change this based on more experiments.
+        constexpr int64_t maxValidTimeDiffNs = 200 * NANOS_PER_MILLISECOND;
+        if (std::abs(timeDiffNs) < maxValidTimeDiffNs) {
+            *latencyMs = frameDiff * 1e3 / recordTrack->sampleRate()
+                   - timeDiffNs * 1e-6;
+            return OK;
+        }
+    }
+
+    return INVALID_OPERATION;
 }
 
 String8 AudioFlinger::PatchPanel::Patch::dump(audio_patch_handle_t myHandle) const
diff --git a/services/audioflinger/TrackBase.h b/services/audioflinger/TrackBase.h
index 95da9d7..a43cb75 100644
--- a/services/audioflinger/TrackBase.h
+++ b/services/audioflinger/TrackBase.h
@@ -187,6 +187,19 @@
                             return status;
                         }
 
+           // TODO: Consider making this external.
+           struct FrameTime {
+               int64_t frames;
+               int64_t timeNs;
+           };
+
+           // KernelFrameTime is updated per "mix" period even for non-pcm tracks.
+           void         getKernelFrameTime(FrameTime *ft) const {
+                           *ft = mKernelFrameTime.load();
+                        }
+
+           audio_format_t format() const { return mFormat; }
+
 protected:
     DISALLOW_COPY_AND_ASSIGN(TrackBase);
 
@@ -198,8 +211,6 @@
     // but putting it in TrackBase avoids the complexity of virtual inheritance
     virtual size_t  framesReady() const { return SIZE_MAX; }
 
-    audio_format_t format() const { return mFormat; }
-
     uint32_t channelCount() const { return mChannelCount; }
 
     audio_channel_mask_t channelMask() const { return mChannelMask; }
@@ -307,6 +318,7 @@
     bool                mServerLatencySupported = false;
     std::atomic<bool>   mServerLatencyFromTrack{}; // latency from track or server timestamp.
     std::atomic<double> mServerLatencyMs{};        // last latency pushed from server thread.
+    std::atomic<FrameTime> mKernelFrameTime{};     // last frame time on kernel side.
 };
 
 // PatchProxyBufferProvider interface is implemented by PatchTrack and PatchRecord.
diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp
index 8ad06f0..78e6c6c 100644
--- a/services/audioflinger/Tracks.cpp
+++ b/services/audioflinger/Tracks.cpp
@@ -1258,6 +1258,16 @@
 void AudioFlinger::PlaybackThread::Track::updateTrackFrameInfo(
         int64_t trackFramesReleased, int64_t sinkFramesWritten,
         uint32_t halSampleRate, const ExtendedTimestamp &timeStamp) {
+   // Make the kernel frametime available.
+    const FrameTime ft{
+            timeStamp.mPosition[ExtendedTimestamp::LOCATION_KERNEL],
+            timeStamp.mTimeNs[ExtendedTimestamp::LOCATION_KERNEL]};
+    // ALOGD("FrameTime: %lld %lld", (long long)ft.frames, (long long)ft.timeNs);
+    mKernelFrameTime.store(ft);
+    if (!audio_is_linear_pcm(mFormat)) {
+        return;
+    }
+
     //update frame map
     mFrameMap.push(trackFramesReleased, sinkFramesWritten);
 
@@ -1886,6 +1896,16 @@
         int64_t trackFramesReleased, int64_t sourceFramesRead,
         uint32_t halSampleRate, const ExtendedTimestamp &timestamp)
 {
+   // Make the kernel frametime available.
+    const FrameTime ft{
+            timestamp.mPosition[ExtendedTimestamp::LOCATION_KERNEL],
+            timestamp.mTimeNs[ExtendedTimestamp::LOCATION_KERNEL]};
+    // ALOGD("FrameTime: %lld %lld", (long long)ft.frames, (long long)ft.timeNs);
+    mKernelFrameTime.store(ft);
+    if (!audio_is_linear_pcm(mFormat)) {
+        return;
+    }
+
     ExtendedTimestamp local = timestamp;
 
     // Convert HAL frames to server-side track frames at track sample rate.