AudioFlinger: synchronize OutputTracks start on duplicating threads

When playback starts on a duplicating thread and target output
mixer threads exit standby, it is possible that one output stream takes
a long time to actually start playback (e.g. for Bluetooth software
encoder path). In this case, the OutputTrack on the other mixer thread
will underrun and audio will get choppy on this output stream.

In order to avoid this, an actual stream start detection mechanism is
added on the mixer thread and the OutputTrack write method will wait
for the target thread to actually start when exiting standby before
sending actual audio.

Bug: 218803475
Test: repro steps in bug
Change-Id: I0eec8c5817c97f7b7643a7151037aff5dc73f3ae
Merged-In: I0eec8c5817c97f7b7643a7151037aff5dc73f3ae
diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h
index 7b4c150..e88134b 100644
--- a/services/audioflinger/Threads.h
+++ b/services/audioflinger/Threads.h
@@ -1116,6 +1116,32 @@
                 void startMelComputation_l(const sp<audio_utils::MelProcessor>& processor) override;
                 void stopMelComputation_l() override;
 
+                void setStandby() {
+                    Mutex::Autolock _l(mLock);
+                    setStandby_l();
+                }
+
+                void setStandby_l() {
+                    mStandby = true;
+                    mHalStarted = false;
+                    mKernelPositionOnStandby =
+                        mTimestamp.mPosition[ExtendedTimestamp::LOCATION_KERNEL];
+                }
+
+                bool waitForHalStart() {
+                    Mutex::Autolock _l(mLock);
+                    static const nsecs_t kWaitHalTimeoutNs = seconds(2);
+                    nsecs_t endWaitTimetNs = systemTime() + kWaitHalTimeoutNs;
+                    while (!mHalStarted) {
+                        nsecs_t timeNs = systemTime();
+                        if (timeNs >= endWaitTimetNs) {
+                            break;
+                        }
+                        nsecs_t waitTimeLeftNs = endWaitTimetNs - timeNs;
+                        mWaitHalStartCV.waitRelative(mLock, waitTimeLeftNs);
+                    }
+                    return mHalStarted;
+                }
 protected:
     // updated by readOutputParameters_l()
     size_t                          mNormalFrameCount;  // normal mixer and effects
@@ -1415,6 +1441,14 @@
     // Downstream patch latency, available if mDownstreamLatencyStatMs.getN() > 0.
     audio_utils::Statistics<double> mDownstreamLatencyStatMs{0.999};
 
+    // output stream start detection based on render position returned by the kernel
+    // condition signalled when the output stream has started
+    Condition                mWaitHalStartCV;
+    // true when the output stream render position has moved, reset to false in standby
+    bool                     mHalStarted = false;
+    // last kernel render position saved when entering standby
+    int64_t                  mKernelPositionOnStandby = 0;
+
 public:
     virtual     bool        hasFastMixer() const = 0;
     virtual     FastTrackUnderruns getFastTrackUnderruns(size_t fastIndex __unused) const