SoundPool: Implement generic garbage collection

Move audio tracks to a gc mechanism for release outside of lock.

Test: soundpool_stress
Test: atest SoundPoolAacTest
Test: atest SoundPoolHapticTest
Test: atest SoundPoolMidiTest
Test: atest SoundPoolOggTest
Bug: 203193643
Change-Id: I5babd4f3fa73cca25579fde2194123bf62cf53b4
diff --git a/media/jni/soundpool/Stream.cpp b/media/jni/soundpool/Stream.cpp
index bbbef38..773cdc9 100644
--- a/media/jni/soundpool/Stream.cpp
+++ b/media/jni/soundpool/Stream.cpp
@@ -228,10 +228,9 @@
    return mStreamManager->getPairStream(this);
 }
 
-Stream* Stream::playPairStream() {
+Stream* Stream::playPairStream(std::vector<std::any>& garbage) {
     Stream* pairStream = getPairStream();
     LOG_ALWAYS_FATAL_IF(pairStream == nullptr, "No pair stream!");
-    sp<AudioTrack> releaseTracks[2];
     {
         ALOGV("%s: track streamID: %d", __func__, (int)getStreamID());
         // TODO: Do we really want to force a simultaneous synchronization between
@@ -260,7 +259,7 @@
         const int pairState = pairStream->mState;
         pairStream->play_l(pairStream->mSound, pairStream->mStreamID,
                 pairStream->mLeftVolume, pairStream->mRightVolume, pairStream->mPriority,
-                pairStream->mLoop, pairStream->mRate, releaseTracks);
+                pairStream->mLoop, pairStream->mRate, garbage);
         if (pairStream->mState == IDLE) {
             return nullptr; // AudioTrack error
         }
@@ -269,17 +268,16 @@
             pairStream->mAudioTrack->pause();
         }
     }
-    // release tracks outside of Stream lock
     return pairStream;
 }
 
 void Stream::play_l(const std::shared_ptr<Sound>& sound, int32_t nextStreamID,
         float leftVolume, float rightVolume, int32_t priority, int32_t loop, float rate,
-        sp<AudioTrack> releaseTracks[2])
+        std::vector<std::any>& garbage)
 {
-    // These tracks are released without the lock.
-    sp<AudioTrack> &oldTrack = releaseTracks[0];
-    sp<AudioTrack> &newTrack = releaseTracks[1];
+    // oldTrack and newTrack are placeholders to be released by garbage without the lock.
+    sp<AudioTrack> oldTrack;
+    sp<AudioTrack> newTrack;
     status_t status = NO_ERROR;
 
     {
@@ -383,8 +381,12 @@
         mState = IDLE;
         mSoundID = 0;
         mSound.reset();
-        mAudioTrack.clear();  // actual release from releaseTracks[]
+        mAudioTrack.clear();  // actual release from garbage
     }
+
+    // move tracks to garbage to be released later outside of lock.
+    if (newTrack) garbage.emplace_back(std::move(newTrack));
+    if (oldTrack) garbage.emplace_back(std::move(oldTrack));
 }
 
 /* static */
diff --git a/media/jni/soundpool/Stream.h b/media/jni/soundpool/Stream.h
index d4e5c9f..aa0eef5 100644
--- a/media/jni/soundpool/Stream.h
+++ b/media/jni/soundpool/Stream.h
@@ -18,6 +18,7 @@
 
 #include "Sound.h"
 
+#include <any>
 #include <android-base/thread_annotations.h>
 #include <audio_utils/clock.h>
 #include <media/AudioTrack.h>
@@ -90,8 +91,9 @@
     void mute(bool muting);
     void dump() const NO_THREAD_SAFETY_ANALYSIS; // disable for ALOGV (see func for details).
 
-    // returns the pair stream if successful, nullptr otherwise
-    Stream* playPairStream();
+    // returns the pair stream if successful, nullptr otherwise.
+    // garbage is used to release tracks and data outside of any lock.
+    Stream* playPairStream(std::vector<std::any>& garbage);
 
     // These parameters are explicitly checked in the SoundPool class
     // so never deviate from the Java API specified values.
@@ -123,9 +125,10 @@
     Stream* getPairStream() const;
 
 private:
+    // garbage is used to release tracks and data outside of any lock.
     void play_l(const std::shared_ptr<Sound>& sound, int streamID,
             float leftVolume, float rightVolume, int priority, int loop, float rate,
-            sp<AudioTrack> releaseTracks[2]) REQUIRES(mLock);
+            std::vector<std::any>& garbage) REQUIRES(mLock);
     void stop_l() REQUIRES(mLock);
     void setVolume_l(float leftVolume, float rightVolume) REQUIRES(mLock);
 
diff --git a/media/jni/soundpool/StreamManager.cpp b/media/jni/soundpool/StreamManager.cpp
index 309d71c..7f987e3 100644
--- a/media/jni/soundpool/StreamManager.cpp
+++ b/media/jni/soundpool/StreamManager.cpp
@@ -157,6 +157,7 @@
             __func__, sound.get(), soundID, leftVolume, rightVolume, priority, loop, rate);
     bool launchThread = false;
     int32_t streamID = 0;
+    std::vector<std::any> garbage;
 
     { // for lock
         std::unique_lock lock(mStreamManagerLock);
@@ -243,7 +244,7 @@
             removeFromQueues_l(newStream);
             mProcessingStreams.emplace(newStream);
             lock.unlock();
-            if (Stream* nextStream = newStream->playPairStream()) {
+            if (Stream* nextStream = newStream->playPairStream(garbage)) {
                 lock.lock();
                 ALOGV("%s: starting streamID:%d", __func__, nextStream->getStreamID());
                 addToActiveQueue_l(nextStream);
@@ -266,6 +267,7 @@
         ALOGV_IF(id != 0, "%s: launched thread %d", __func__, id);
     }
     ALOGV("%s: returning %d", __func__, streamID);
+    // garbage is cleared here outside mStreamManagerLock.
     return streamID;
 }
 
@@ -359,6 +361,7 @@
 {
     ALOGV("%s(%d) entering", __func__, id);
     int64_t waitTimeNs = 0;  // on thread start, mRestartStreams can be non-empty.
+    std::vector<std::any> garbage; // used for garbage collection
     std::unique_lock lock(mStreamManagerLock);
     while (!mQuit) {
         if (waitTimeNs > 0) {
@@ -388,7 +391,7 @@
             if (!mLockStreamManagerStop) lock.unlock();
             stream->stop();
             ALOGV("%s(%d) stopping streamID:%d", __func__, id, stream->getStreamID());
-            if (Stream* nextStream = stream->playPairStream()) {
+            if (Stream* nextStream = stream->playPairStream(garbage)) {
                 ALOGV("%s(%d) starting streamID:%d", __func__, id, nextStream->getStreamID());
                 if (!mLockStreamManagerStop) lock.lock();
                 if (nextStream->getStopTimeNs() > 0) {
@@ -405,6 +408,12 @@
             }
             mProcessingStreams.erase(stream);
             sanityCheckQueue_l();
+            if (!garbage.empty()) {
+                lock.unlock();
+                // garbage audio tracks (etc) are cleared here outside mStreamManagerLock.
+                garbage.clear();
+                lock.lock();
+            }
         }
     }
     ALOGV("%s(%d) exiting", __func__, id);