xref: /aosp_15_r20/frameworks/base/media/jni/soundpool/StreamManager.cpp (revision d57664e9bc4670b3ecf6748a746a57c557b6bc9e)
1*d57664e9SAndroid Build Coastguard Worker /*
2*d57664e9SAndroid Build Coastguard Worker  * Copyright (C) 2019 The Android Open Source Project
3*d57664e9SAndroid Build Coastguard Worker  *
4*d57664e9SAndroid Build Coastguard Worker  * Licensed under the Apache License, Version 2.0 (the "License");
5*d57664e9SAndroid Build Coastguard Worker  * you may not use this file except in compliance with the License.
6*d57664e9SAndroid Build Coastguard Worker  * You may obtain a copy of the License at
7*d57664e9SAndroid Build Coastguard Worker  *
8*d57664e9SAndroid Build Coastguard Worker  *      http://www.apache.org/licenses/LICENSE-2.0
9*d57664e9SAndroid Build Coastguard Worker  *
10*d57664e9SAndroid Build Coastguard Worker  * Unless required by applicable law or agreed to in writing, software
11*d57664e9SAndroid Build Coastguard Worker  * distributed under the License is distributed on an "AS IS" BASIS,
12*d57664e9SAndroid Build Coastguard Worker  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*d57664e9SAndroid Build Coastguard Worker  * See the License for the specific language governing permissions and
14*d57664e9SAndroid Build Coastguard Worker  * limitations under the License.
15*d57664e9SAndroid Build Coastguard Worker  */
16*d57664e9SAndroid Build Coastguard Worker 
17*d57664e9SAndroid Build Coastguard Worker //#define LOG_NDEBUG 0
18*d57664e9SAndroid Build Coastguard Worker #define LOG_TAG "SoundPool::StreamManager"
19*d57664e9SAndroid Build Coastguard Worker #include <utils/Log.h>
20*d57664e9SAndroid Build Coastguard Worker 
21*d57664e9SAndroid Build Coastguard Worker #include "StreamManager.h"
22*d57664e9SAndroid Build Coastguard Worker 
23*d57664e9SAndroid Build Coastguard Worker #include <audio_utils/clock.h>
24*d57664e9SAndroid Build Coastguard Worker #include <audio_utils/roundup.h>
25*d57664e9SAndroid Build Coastguard Worker 
26*d57664e9SAndroid Build Coastguard Worker namespace android::soundpool {
27*d57664e9SAndroid Build Coastguard Worker 
28*d57664e9SAndroid Build Coastguard Worker // kMaxStreams is number that should be less than the current AudioTrack max per UID of 40.
29*d57664e9SAndroid Build Coastguard Worker // It is the maximum number of AudioTrack resources allowed in the SoundPool.
30*d57664e9SAndroid Build Coastguard Worker // We suggest a value at least 4 or greater to allow CTS tests to pass.
31*d57664e9SAndroid Build Coastguard Worker static constexpr int32_t kMaxStreams = 32;
32*d57664e9SAndroid Build Coastguard Worker 
33*d57664e9SAndroid Build Coastguard Worker // kStealActiveStream_OldestFirst = false historically (Q and earlier)
34*d57664e9SAndroid Build Coastguard Worker // Changing to true could break app expectations but could change behavior beneficially.
35*d57664e9SAndroid Build Coastguard Worker // In R, we change this to true, as it is the correct way per SoundPool documentation.
36*d57664e9SAndroid Build Coastguard Worker static constexpr bool kStealActiveStream_OldestFirst = true;
37*d57664e9SAndroid Build Coastguard Worker 
38*d57664e9SAndroid Build Coastguard Worker // Changing to false means calls to play() are almost instantaneous instead of taking around
39*d57664e9SAndroid Build Coastguard Worker // ~10ms to launch the AudioTrack. It is perhaps 100x faster.
40*d57664e9SAndroid Build Coastguard Worker static constexpr bool kPlayOnCallingThread = false;
41*d57664e9SAndroid Build Coastguard Worker 
42*d57664e9SAndroid Build Coastguard Worker // Amount of time for a StreamManager thread to wait before closing.
43*d57664e9SAndroid Build Coastguard Worker static constexpr int64_t kWaitTimeBeforeCloseNs = 9 * NANOS_PER_SECOND;
44*d57664e9SAndroid Build Coastguard Worker 
45*d57664e9SAndroid Build Coastguard Worker // Debug flag:
46*d57664e9SAndroid Build Coastguard Worker // kForceLockStreamManagerStop is set to true to force lock the StreamManager
47*d57664e9SAndroid Build Coastguard Worker // worker thread during stop. This limits concurrency of Stream processing.
48*d57664e9SAndroid Build Coastguard Worker // Normally we lock the StreamManager worker thread during stop ONLY
49*d57664e9SAndroid Build Coastguard Worker // for SoundPools configured with a single Stream.
50*d57664e9SAndroid Build Coastguard Worker //
51*d57664e9SAndroid Build Coastguard Worker static constexpr bool kForceLockStreamManagerStop = false;
52*d57664e9SAndroid Build Coastguard Worker 
53*d57664e9SAndroid Build Coastguard Worker ////////////
54*d57664e9SAndroid Build Coastguard Worker 
StreamMap(int32_t streams)55*d57664e9SAndroid Build Coastguard Worker StreamMap::StreamMap(int32_t streams) {
56*d57664e9SAndroid Build Coastguard Worker     ALOGV("%s(%d)", __func__, streams);
57*d57664e9SAndroid Build Coastguard Worker     if (streams > kMaxStreams) {
58*d57664e9SAndroid Build Coastguard Worker         ALOGW("%s: requested %d streams, clamping to %d", __func__, streams, kMaxStreams);
59*d57664e9SAndroid Build Coastguard Worker         streams = kMaxStreams;
60*d57664e9SAndroid Build Coastguard Worker     } else if (streams < 1) {
61*d57664e9SAndroid Build Coastguard Worker         ALOGW("%s: requested %d streams, clamping to 1", __func__, streams);
62*d57664e9SAndroid Build Coastguard Worker         streams = 1;
63*d57664e9SAndroid Build Coastguard Worker     }
64*d57664e9SAndroid Build Coastguard Worker     mStreamPoolSize = streams * 2;
65*d57664e9SAndroid Build Coastguard Worker     mStreamPool = std::make_unique<Stream[]>(mStreamPoolSize); // create array of streams.
66*d57664e9SAndroid Build Coastguard Worker     // we use a perfect hash table with 2x size to map StreamIDs to Stream pointers.
67*d57664e9SAndroid Build Coastguard Worker     mPerfectHash = std::make_unique<PerfectHash<int32_t, Stream *>>(roundup(mStreamPoolSize * 2));
68*d57664e9SAndroid Build Coastguard Worker }
69*d57664e9SAndroid Build Coastguard Worker 
findStream(int32_t streamID) const70*d57664e9SAndroid Build Coastguard Worker Stream* StreamMap::findStream(int32_t streamID) const
71*d57664e9SAndroid Build Coastguard Worker {
72*d57664e9SAndroid Build Coastguard Worker     Stream *stream = lookupStreamFromId(streamID);
73*d57664e9SAndroid Build Coastguard Worker     return stream != nullptr && stream->getStreamID() == streamID ? stream : nullptr;
74*d57664e9SAndroid Build Coastguard Worker }
75*d57664e9SAndroid Build Coastguard Worker 
streamPosition(const Stream * stream) const76*d57664e9SAndroid Build Coastguard Worker size_t StreamMap::streamPosition(const Stream* stream) const
77*d57664e9SAndroid Build Coastguard Worker {
78*d57664e9SAndroid Build Coastguard Worker     ptrdiff_t index = stream - mStreamPool.get();
79*d57664e9SAndroid Build Coastguard Worker     LOG_ALWAYS_FATAL_IF(index < 0 || (size_t)index >= mStreamPoolSize,
80*d57664e9SAndroid Build Coastguard Worker             "%s: stream position out of range: %td", __func__, index);
81*d57664e9SAndroid Build Coastguard Worker     return (size_t)index;
82*d57664e9SAndroid Build Coastguard Worker }
83*d57664e9SAndroid Build Coastguard Worker 
lookupStreamFromId(int32_t streamID) const84*d57664e9SAndroid Build Coastguard Worker Stream* StreamMap::lookupStreamFromId(int32_t streamID) const
85*d57664e9SAndroid Build Coastguard Worker {
86*d57664e9SAndroid Build Coastguard Worker     return streamID > 0 ? mPerfectHash->getValue(streamID).load() : nullptr;
87*d57664e9SAndroid Build Coastguard Worker }
88*d57664e9SAndroid Build Coastguard Worker 
getNextIdForStream(Stream * stream) const89*d57664e9SAndroid Build Coastguard Worker int32_t StreamMap::getNextIdForStream(Stream* stream) const {
90*d57664e9SAndroid Build Coastguard Worker     // even though it is const, it mutates the internal hash table.
91*d57664e9SAndroid Build Coastguard Worker     const int32_t id = mPerfectHash->generateKey(
92*d57664e9SAndroid Build Coastguard Worker         stream,
93*d57664e9SAndroid Build Coastguard Worker         [] (Stream *stream) {
94*d57664e9SAndroid Build Coastguard Worker             return stream == nullptr ? 0 : stream->getStreamID();
95*d57664e9SAndroid Build Coastguard Worker         }, /* getKforV() */
96*d57664e9SAndroid Build Coastguard Worker         stream->getStreamID() /* oldID */);
97*d57664e9SAndroid Build Coastguard Worker     return id;
98*d57664e9SAndroid Build Coastguard Worker }
99*d57664e9SAndroid Build Coastguard Worker 
100*d57664e9SAndroid Build Coastguard Worker ////////////
101*d57664e9SAndroid Build Coastguard Worker 
102*d57664e9SAndroid Build Coastguard Worker // Thread safety analysis is supposed to be disabled for constructors and destructors
103*d57664e9SAndroid Build Coastguard Worker // but clang in R seems to have a bug.  We use pragma to disable.
104*d57664e9SAndroid Build Coastguard Worker #pragma clang diagnostic push
105*d57664e9SAndroid Build Coastguard Worker #pragma clang diagnostic ignored "-Wthread-safety-analysis"
106*d57664e9SAndroid Build Coastguard Worker 
StreamManager(int32_t streams,size_t threads,const audio_attributes_t & attributes,std::string opPackageName)107*d57664e9SAndroid Build Coastguard Worker StreamManager::StreamManager(
108*d57664e9SAndroid Build Coastguard Worker         int32_t streams, size_t threads, const audio_attributes_t& attributes,
109*d57664e9SAndroid Build Coastguard Worker         std::string opPackageName)
110*d57664e9SAndroid Build Coastguard Worker     : StreamMap(streams)
111*d57664e9SAndroid Build Coastguard Worker     , mAttributes([attributes](){
112*d57664e9SAndroid Build Coastguard Worker         audio_attributes_t attr = attributes;
113*d57664e9SAndroid Build Coastguard Worker         attr.flags = static_cast<audio_flags_mask_t>(attr.flags | AUDIO_FLAG_LOW_LATENCY);
114*d57664e9SAndroid Build Coastguard Worker         return attr; }())
115*d57664e9SAndroid Build Coastguard Worker     , mOpPackageName(std::move(opPackageName))
116*d57664e9SAndroid Build Coastguard Worker     , mLockStreamManagerStop(streams == 1 || kForceLockStreamManagerStop)
117*d57664e9SAndroid Build Coastguard Worker {
118*d57664e9SAndroid Build Coastguard Worker     ALOGV("%s(%d, %zu, ...)", __func__, streams, threads);
__anon9150efb80302(Stream *stream) 119*d57664e9SAndroid Build Coastguard Worker     forEach([this](Stream *stream) {
120*d57664e9SAndroid Build Coastguard Worker         stream->setStreamManager(this);
121*d57664e9SAndroid Build Coastguard Worker         if ((streamPosition(stream) & 1) == 0) { // put the first stream of pair as available.
122*d57664e9SAndroid Build Coastguard Worker             mAvailableStreams.insert(stream);
123*d57664e9SAndroid Build Coastguard Worker         }
124*d57664e9SAndroid Build Coastguard Worker     });
125*d57664e9SAndroid Build Coastguard Worker 
126*d57664e9SAndroid Build Coastguard Worker     mThreadPool = std::make_unique<ThreadPool>(
127*d57664e9SAndroid Build Coastguard Worker             std::min((size_t)streams,  // do not make more threads than streams to play
128*d57664e9SAndroid Build Coastguard Worker                     std::min(threads, (size_t)std::thread::hardware_concurrency())),
129*d57664e9SAndroid Build Coastguard Worker             "SoundPool_",
130*d57664e9SAndroid Build Coastguard Worker             ANDROID_PRIORITY_AUDIO);
131*d57664e9SAndroid Build Coastguard Worker }
132*d57664e9SAndroid Build Coastguard Worker 
133*d57664e9SAndroid Build Coastguard Worker #pragma clang diagnostic pop
134*d57664e9SAndroid Build Coastguard Worker 
~StreamManager()135*d57664e9SAndroid Build Coastguard Worker StreamManager::~StreamManager()
136*d57664e9SAndroid Build Coastguard Worker {
137*d57664e9SAndroid Build Coastguard Worker     ALOGV("%s", __func__);
138*d57664e9SAndroid Build Coastguard Worker     {
139*d57664e9SAndroid Build Coastguard Worker         std::unique_lock lock(mStreamManagerLock);
140*d57664e9SAndroid Build Coastguard Worker         mQuit = true;
141*d57664e9SAndroid Build Coastguard Worker         mStreamManagerCondition.notify_all();
142*d57664e9SAndroid Build Coastguard Worker     }
143*d57664e9SAndroid Build Coastguard Worker     mThreadPool->quit();
144*d57664e9SAndroid Build Coastguard Worker 
145*d57664e9SAndroid Build Coastguard Worker     // call stop on the stream pool
146*d57664e9SAndroid Build Coastguard Worker     forEach([](Stream *stream) { stream->stop(); });
147*d57664e9SAndroid Build Coastguard Worker 
148*d57664e9SAndroid Build Coastguard Worker     // This invokes the destructor on the AudioTracks -
149*d57664e9SAndroid Build Coastguard Worker     // we do it here to ensure that AudioTrack callbacks will not occur
150*d57664e9SAndroid Build Coastguard Worker     // afterwards.
151*d57664e9SAndroid Build Coastguard Worker     forEach([](Stream *stream) { stream->clearAudioTrack(); });
152*d57664e9SAndroid Build Coastguard Worker }
153*d57664e9SAndroid Build Coastguard Worker 
154*d57664e9SAndroid Build Coastguard Worker 
queueForPlay(const std::shared_ptr<Sound> & sound,int32_t soundID,float leftVolume,float rightVolume,int32_t priority,int32_t loop,float rate,int32_t playerIId)155*d57664e9SAndroid Build Coastguard Worker int32_t StreamManager::queueForPlay(const std::shared_ptr<Sound> &sound,
156*d57664e9SAndroid Build Coastguard Worker         int32_t soundID, float leftVolume, float rightVolume,
157*d57664e9SAndroid Build Coastguard Worker         int32_t priority, int32_t loop, float rate, int32_t playerIId)
158*d57664e9SAndroid Build Coastguard Worker {
159*d57664e9SAndroid Build Coastguard Worker     ALOGV(
160*d57664e9SAndroid Build Coastguard Worker         "%s(sound=%p, soundID=%d, leftVolume=%f, rightVolume=%f, priority=%d, loop=%d, rate=%f,"
161*d57664e9SAndroid Build Coastguard Worker         " playerIId=%d)", __func__, sound.get(), soundID, leftVolume, rightVolume, priority,
162*d57664e9SAndroid Build Coastguard Worker         loop, rate, playerIId);
163*d57664e9SAndroid Build Coastguard Worker 
164*d57664e9SAndroid Build Coastguard Worker     bool launchThread = false;
165*d57664e9SAndroid Build Coastguard Worker     int32_t streamID = 0;
166*d57664e9SAndroid Build Coastguard Worker     std::vector<std::any> garbage;
167*d57664e9SAndroid Build Coastguard Worker 
168*d57664e9SAndroid Build Coastguard Worker     { // for lock
169*d57664e9SAndroid Build Coastguard Worker         std::unique_lock lock(mStreamManagerLock);
170*d57664e9SAndroid Build Coastguard Worker         Stream *newStream = nullptr;
171*d57664e9SAndroid Build Coastguard Worker         bool fromAvailableQueue = false;
172*d57664e9SAndroid Build Coastguard Worker         ALOGV("%s: mStreamManagerLock lock acquired", __func__);
173*d57664e9SAndroid Build Coastguard Worker 
174*d57664e9SAndroid Build Coastguard Worker         sanityCheckQueue_l();
175*d57664e9SAndroid Build Coastguard Worker         // find an available stream, prefer one that has matching sound id.
176*d57664e9SAndroid Build Coastguard Worker         if (mAvailableStreams.size() > 0) {
177*d57664e9SAndroid Build Coastguard Worker             for (auto stream : mAvailableStreams) {
178*d57664e9SAndroid Build Coastguard Worker                 if (stream->getSoundID() == soundID) {
179*d57664e9SAndroid Build Coastguard Worker                     newStream = stream;
180*d57664e9SAndroid Build Coastguard Worker                     ALOGV("%s: found soundID %d in available queue", __func__, soundID);
181*d57664e9SAndroid Build Coastguard Worker                     break;
182*d57664e9SAndroid Build Coastguard Worker                 }
183*d57664e9SAndroid Build Coastguard Worker             }
184*d57664e9SAndroid Build Coastguard Worker             if (newStream == nullptr) {
185*d57664e9SAndroid Build Coastguard Worker                 ALOGV("%s: found stream in available queue", __func__);
186*d57664e9SAndroid Build Coastguard Worker                 newStream = *mAvailableStreams.begin();
187*d57664e9SAndroid Build Coastguard Worker             }
188*d57664e9SAndroid Build Coastguard Worker             newStream->setStopTimeNs(systemTime());
189*d57664e9SAndroid Build Coastguard Worker             fromAvailableQueue = true;
190*d57664e9SAndroid Build Coastguard Worker         }
191*d57664e9SAndroid Build Coastguard Worker 
192*d57664e9SAndroid Build Coastguard Worker         // also look in the streams restarting (if the paired stream doesn't have a pending play)
193*d57664e9SAndroid Build Coastguard Worker         if (newStream == nullptr || newStream->getSoundID() != soundID) {
194*d57664e9SAndroid Build Coastguard Worker             for (auto [unused , stream] : mRestartStreams) {
195*d57664e9SAndroid Build Coastguard Worker                 if (!stream->getPairStream()->hasSound()) {
196*d57664e9SAndroid Build Coastguard Worker                     if (stream->getSoundID() == soundID) {
197*d57664e9SAndroid Build Coastguard Worker                         ALOGV("%s: found soundID %d in restart queue", __func__, soundID);
198*d57664e9SAndroid Build Coastguard Worker                         newStream = stream;
199*d57664e9SAndroid Build Coastguard Worker                         fromAvailableQueue = false;
200*d57664e9SAndroid Build Coastguard Worker                         break;
201*d57664e9SAndroid Build Coastguard Worker                     } else if (newStream == nullptr) {
202*d57664e9SAndroid Build Coastguard Worker                         ALOGV("%s: found stream in restart queue", __func__);
203*d57664e9SAndroid Build Coastguard Worker                         newStream = stream;
204*d57664e9SAndroid Build Coastguard Worker                     }
205*d57664e9SAndroid Build Coastguard Worker                 }
206*d57664e9SAndroid Build Coastguard Worker             }
207*d57664e9SAndroid Build Coastguard Worker         }
208*d57664e9SAndroid Build Coastguard Worker 
209*d57664e9SAndroid Build Coastguard Worker         // no available streams, look for one to steal from the active list
210*d57664e9SAndroid Build Coastguard Worker         if (newStream == nullptr) {
211*d57664e9SAndroid Build Coastguard Worker             for (auto stream : mActiveStreams) {
212*d57664e9SAndroid Build Coastguard Worker                 if (stream->getPriority() <= priority) {
213*d57664e9SAndroid Build Coastguard Worker                     if (newStream == nullptr
214*d57664e9SAndroid Build Coastguard Worker                             || newStream->getPriority() > stream->getPriority()) {
215*d57664e9SAndroid Build Coastguard Worker                         newStream = stream;
216*d57664e9SAndroid Build Coastguard Worker                         ALOGV("%s: found stream in active queue", __func__);
217*d57664e9SAndroid Build Coastguard Worker                     }
218*d57664e9SAndroid Build Coastguard Worker                 }
219*d57664e9SAndroid Build Coastguard Worker             }
220*d57664e9SAndroid Build Coastguard Worker             if (newStream != nullptr) { // we need to mute as it is still playing.
221*d57664e9SAndroid Build Coastguard Worker                 (void)newStream->requestStop(newStream->getStreamID());
222*d57664e9SAndroid Build Coastguard Worker             }
223*d57664e9SAndroid Build Coastguard Worker         }
224*d57664e9SAndroid Build Coastguard Worker 
225*d57664e9SAndroid Build Coastguard Worker         // none found, look for a stream that is restarting, evict one.
226*d57664e9SAndroid Build Coastguard Worker         if (newStream == nullptr) {
227*d57664e9SAndroid Build Coastguard Worker             for (auto [unused, stream] : mRestartStreams) {
228*d57664e9SAndroid Build Coastguard Worker                 if (stream->getPairPriority() <= priority) {
229*d57664e9SAndroid Build Coastguard Worker                     ALOGV("%s: evict stream from restart queue", __func__);
230*d57664e9SAndroid Build Coastguard Worker                     newStream = stream;
231*d57664e9SAndroid Build Coastguard Worker                     break;
232*d57664e9SAndroid Build Coastguard Worker                 }
233*d57664e9SAndroid Build Coastguard Worker             }
234*d57664e9SAndroid Build Coastguard Worker         }
235*d57664e9SAndroid Build Coastguard Worker 
236*d57664e9SAndroid Build Coastguard Worker         // DO NOT LOOK into mProcessingStreams as those are held by the StreamManager threads.
237*d57664e9SAndroid Build Coastguard Worker 
238*d57664e9SAndroid Build Coastguard Worker         if (newStream == nullptr) {
239*d57664e9SAndroid Build Coastguard Worker             ALOGD("%s: unable to find stream, returning 0", __func__);
240*d57664e9SAndroid Build Coastguard Worker             return 0; // unable to find available stream
241*d57664e9SAndroid Build Coastguard Worker         }
242*d57664e9SAndroid Build Coastguard Worker 
243*d57664e9SAndroid Build Coastguard Worker         Stream *pairStream = newStream->getPairStream();
244*d57664e9SAndroid Build Coastguard Worker         streamID = getNextIdForStream(pairStream);
245*d57664e9SAndroid Build Coastguard Worker         ALOGV("%s: newStream:%p  pairStream:%p, streamID:%d",
246*d57664e9SAndroid Build Coastguard Worker                 __func__, newStream, pairStream, streamID);
247*d57664e9SAndroid Build Coastguard Worker         pairStream->setPlay(
248*d57664e9SAndroid Build Coastguard Worker                 streamID, sound, soundID, leftVolume, rightVolume, priority, loop, rate);
249*d57664e9SAndroid Build Coastguard Worker         if (fromAvailableQueue && kPlayOnCallingThread) {
250*d57664e9SAndroid Build Coastguard Worker             removeFromQueues_l(newStream);
251*d57664e9SAndroid Build Coastguard Worker             mProcessingStreams.emplace(newStream);
252*d57664e9SAndroid Build Coastguard Worker             lock.unlock();
253*d57664e9SAndroid Build Coastguard Worker             if (Stream* nextStream = newStream->playPairStream(garbage, playerIId)) {
254*d57664e9SAndroid Build Coastguard Worker                 lock.lock();
255*d57664e9SAndroid Build Coastguard Worker                 ALOGV("%s: starting streamID:%d", __func__, nextStream->getStreamID());
256*d57664e9SAndroid Build Coastguard Worker                 addToActiveQueue_l(nextStream);
257*d57664e9SAndroid Build Coastguard Worker             } else {
258*d57664e9SAndroid Build Coastguard Worker                 lock.lock();
259*d57664e9SAndroid Build Coastguard Worker                 mAvailableStreams.insert(newStream);
260*d57664e9SAndroid Build Coastguard Worker                 streamID = 0;
261*d57664e9SAndroid Build Coastguard Worker             }
262*d57664e9SAndroid Build Coastguard Worker             mProcessingStreams.erase(newStream);
263*d57664e9SAndroid Build Coastguard Worker         } else {
264*d57664e9SAndroid Build Coastguard Worker             launchThread = moveToRestartQueue_l(newStream) && needMoreThreads_l();
265*d57664e9SAndroid Build Coastguard Worker         }
266*d57664e9SAndroid Build Coastguard Worker         sanityCheckQueue_l();
267*d57664e9SAndroid Build Coastguard Worker         ALOGV("%s: mStreamManagerLock released", __func__);
268*d57664e9SAndroid Build Coastguard Worker     } // lock
269*d57664e9SAndroid Build Coastguard Worker 
270*d57664e9SAndroid Build Coastguard Worker     if (launchThread) {
271*d57664e9SAndroid Build Coastguard Worker         const int32_t id = mThreadPool->launch([this](int32_t id) { run(id); });
272*d57664e9SAndroid Build Coastguard Worker         (void)id; // avoid clang warning -Wunused-variable -Wused-but-marked-unused
273*d57664e9SAndroid Build Coastguard Worker         ALOGV_IF(id != 0, "%s: launched thread %d", __func__, id);
274*d57664e9SAndroid Build Coastguard Worker     }
275*d57664e9SAndroid Build Coastguard Worker     ALOGV("%s: returning %d", __func__, streamID);
276*d57664e9SAndroid Build Coastguard Worker     // garbage is cleared here outside mStreamManagerLock.
277*d57664e9SAndroid Build Coastguard Worker     return streamID;
278*d57664e9SAndroid Build Coastguard Worker }
279*d57664e9SAndroid Build Coastguard Worker 
moveToRestartQueue(Stream * stream,int32_t activeStreamIDToMatch)280*d57664e9SAndroid Build Coastguard Worker void StreamManager::moveToRestartQueue(
281*d57664e9SAndroid Build Coastguard Worker         Stream* stream, int32_t activeStreamIDToMatch)
282*d57664e9SAndroid Build Coastguard Worker {
283*d57664e9SAndroid Build Coastguard Worker     ALOGV("%s(stream(ID)=%d, activeStreamIDToMatch=%d)",
284*d57664e9SAndroid Build Coastguard Worker             __func__, stream->getStreamID(), activeStreamIDToMatch);
285*d57664e9SAndroid Build Coastguard Worker     bool restart;
286*d57664e9SAndroid Build Coastguard Worker     {
287*d57664e9SAndroid Build Coastguard Worker         std::lock_guard lock(mStreamManagerLock);
288*d57664e9SAndroid Build Coastguard Worker         sanityCheckQueue_l();
289*d57664e9SAndroid Build Coastguard Worker         if (mProcessingStreams.count(stream) > 0 ||
290*d57664e9SAndroid Build Coastguard Worker                 mProcessingStreams.count(stream->getPairStream()) > 0) {
291*d57664e9SAndroid Build Coastguard Worker             ALOGD("%s: attempting to restart processing stream(%d)",
292*d57664e9SAndroid Build Coastguard Worker                     __func__, stream->getStreamID());
293*d57664e9SAndroid Build Coastguard Worker             restart = false;
294*d57664e9SAndroid Build Coastguard Worker         } else {
295*d57664e9SAndroid Build Coastguard Worker             moveToRestartQueue_l(stream, activeStreamIDToMatch);
296*d57664e9SAndroid Build Coastguard Worker             restart = needMoreThreads_l();
297*d57664e9SAndroid Build Coastguard Worker         }
298*d57664e9SAndroid Build Coastguard Worker         sanityCheckQueue_l();
299*d57664e9SAndroid Build Coastguard Worker     }
300*d57664e9SAndroid Build Coastguard Worker     if (restart) {
301*d57664e9SAndroid Build Coastguard Worker         const int32_t id = mThreadPool->launch([this](int32_t id) { run(id); });
302*d57664e9SAndroid Build Coastguard Worker         (void)id; // avoid clang warning -Wunused-variable -Wused-but-marked-unused
303*d57664e9SAndroid Build Coastguard Worker         ALOGV_IF(id != 0, "%s: launched thread %d", __func__, id);
304*d57664e9SAndroid Build Coastguard Worker     }
305*d57664e9SAndroid Build Coastguard Worker }
306*d57664e9SAndroid Build Coastguard Worker 
moveToRestartQueue_l(Stream * stream,int32_t activeStreamIDToMatch)307*d57664e9SAndroid Build Coastguard Worker bool StreamManager::moveToRestartQueue_l(
308*d57664e9SAndroid Build Coastguard Worker         Stream* stream, int32_t activeStreamIDToMatch)
309*d57664e9SAndroid Build Coastguard Worker {
310*d57664e9SAndroid Build Coastguard Worker     ALOGV("%s(stream(ID)=%d, activeStreamIDToMatch=%d)",
311*d57664e9SAndroid Build Coastguard Worker             __func__, stream->getStreamID(), activeStreamIDToMatch);
312*d57664e9SAndroid Build Coastguard Worker     if (activeStreamIDToMatch > 0 && stream->getStreamID() != activeStreamIDToMatch) {
313*d57664e9SAndroid Build Coastguard Worker         return false;
314*d57664e9SAndroid Build Coastguard Worker     }
315*d57664e9SAndroid Build Coastguard Worker     const ssize_t found = removeFromQueues_l(stream, activeStreamIDToMatch);
316*d57664e9SAndroid Build Coastguard Worker     if (found < 0) return false;
317*d57664e9SAndroid Build Coastguard Worker 
318*d57664e9SAndroid Build Coastguard Worker     LOG_ALWAYS_FATAL_IF(found > 1, "stream on %zd > 1 stream lists", found);
319*d57664e9SAndroid Build Coastguard Worker 
320*d57664e9SAndroid Build Coastguard Worker     addToRestartQueue_l(stream);
321*d57664e9SAndroid Build Coastguard Worker     mStreamManagerCondition.notify_one();
322*d57664e9SAndroid Build Coastguard Worker     return true;
323*d57664e9SAndroid Build Coastguard Worker }
324*d57664e9SAndroid Build Coastguard Worker 
removeFromQueues_l(Stream * stream,int32_t activeStreamIDToMatch)325*d57664e9SAndroid Build Coastguard Worker ssize_t StreamManager::removeFromQueues_l(
326*d57664e9SAndroid Build Coastguard Worker         Stream* stream, int32_t activeStreamIDToMatch) {
327*d57664e9SAndroid Build Coastguard Worker     size_t found = 0;
328*d57664e9SAndroid Build Coastguard Worker     for (auto it = mActiveStreams.begin(); it != mActiveStreams.end(); ++it) {
329*d57664e9SAndroid Build Coastguard Worker         if (*it == stream) {
330*d57664e9SAndroid Build Coastguard Worker             mActiveStreams.erase(it); // we erase the iterator and break (otherwise it not safe).
331*d57664e9SAndroid Build Coastguard Worker             ++found;
332*d57664e9SAndroid Build Coastguard Worker             break;
333*d57664e9SAndroid Build Coastguard Worker         }
334*d57664e9SAndroid Build Coastguard Worker     }
335*d57664e9SAndroid Build Coastguard Worker     // activeStreamIDToMatch is nonzero indicates we proceed only if found.
336*d57664e9SAndroid Build Coastguard Worker     if (found == 0 && activeStreamIDToMatch > 0) {
337*d57664e9SAndroid Build Coastguard Worker         return -1;  // special code: not present on active streams, ignore restart request
338*d57664e9SAndroid Build Coastguard Worker     }
339*d57664e9SAndroid Build Coastguard Worker 
340*d57664e9SAndroid Build Coastguard Worker     for (auto it = mRestartStreams.begin(); it != mRestartStreams.end(); ++it) {
341*d57664e9SAndroid Build Coastguard Worker         if (it->second == stream) {
342*d57664e9SAndroid Build Coastguard Worker             mRestartStreams.erase(it);
343*d57664e9SAndroid Build Coastguard Worker             ++found;
344*d57664e9SAndroid Build Coastguard Worker             break;
345*d57664e9SAndroid Build Coastguard Worker         }
346*d57664e9SAndroid Build Coastguard Worker     }
347*d57664e9SAndroid Build Coastguard Worker     found += mAvailableStreams.erase(stream);
348*d57664e9SAndroid Build Coastguard Worker 
349*d57664e9SAndroid Build Coastguard Worker     // streams on mProcessingStreams are undergoing processing by the StreamManager thread
350*d57664e9SAndroid Build Coastguard Worker     // and do not participate in normal stream migration.
351*d57664e9SAndroid Build Coastguard Worker     return (ssize_t)found;
352*d57664e9SAndroid Build Coastguard Worker }
353*d57664e9SAndroid Build Coastguard Worker 
addToRestartQueue_l(Stream * stream)354*d57664e9SAndroid Build Coastguard Worker void StreamManager::addToRestartQueue_l(Stream *stream) {
355*d57664e9SAndroid Build Coastguard Worker     mRestartStreams.emplace(stream->getStopTimeNs(), stream);
356*d57664e9SAndroid Build Coastguard Worker }
357*d57664e9SAndroid Build Coastguard Worker 
addToActiveQueue_l(Stream * stream)358*d57664e9SAndroid Build Coastguard Worker void StreamManager::addToActiveQueue_l(Stream *stream) {
359*d57664e9SAndroid Build Coastguard Worker     if (kStealActiveStream_OldestFirst) {
360*d57664e9SAndroid Build Coastguard Worker         mActiveStreams.push_back(stream);  // oldest to newest
361*d57664e9SAndroid Build Coastguard Worker     } else {
362*d57664e9SAndroid Build Coastguard Worker         mActiveStreams.push_front(stream); // newest to oldest
363*d57664e9SAndroid Build Coastguard Worker     }
364*d57664e9SAndroid Build Coastguard Worker }
365*d57664e9SAndroid Build Coastguard Worker 
run(int32_t id)366*d57664e9SAndroid Build Coastguard Worker void StreamManager::run(int32_t id)
367*d57664e9SAndroid Build Coastguard Worker {
368*d57664e9SAndroid Build Coastguard Worker     ALOGV("%s(%d) entering", __func__, id);
369*d57664e9SAndroid Build Coastguard Worker     int64_t waitTimeNs = 0;  // on thread start, mRestartStreams can be non-empty.
370*d57664e9SAndroid Build Coastguard Worker     std::vector<std::any> garbage; // used for garbage collection
371*d57664e9SAndroid Build Coastguard Worker     std::unique_lock lock(mStreamManagerLock);
372*d57664e9SAndroid Build Coastguard Worker     while (!mQuit) {
373*d57664e9SAndroid Build Coastguard Worker         if (waitTimeNs > 0) {
374*d57664e9SAndroid Build Coastguard Worker             mStreamManagerCondition.wait_for(
375*d57664e9SAndroid Build Coastguard Worker                     lock, std::chrono::duration<int64_t, std::nano>(waitTimeNs));
376*d57664e9SAndroid Build Coastguard Worker         }
377*d57664e9SAndroid Build Coastguard Worker         ALOGV("%s(%d) awake lock waitTimeNs:%lld", __func__, id, (long long)waitTimeNs);
378*d57664e9SAndroid Build Coastguard Worker 
379*d57664e9SAndroid Build Coastguard Worker         sanityCheckQueue_l();
380*d57664e9SAndroid Build Coastguard Worker 
381*d57664e9SAndroid Build Coastguard Worker         if (mQuit || (mRestartStreams.empty() && waitTimeNs == kWaitTimeBeforeCloseNs)) {
382*d57664e9SAndroid Build Coastguard Worker             break;  // end the thread
383*d57664e9SAndroid Build Coastguard Worker         }
384*d57664e9SAndroid Build Coastguard Worker 
385*d57664e9SAndroid Build Coastguard Worker         waitTimeNs = kWaitTimeBeforeCloseNs;
386*d57664e9SAndroid Build Coastguard Worker         while (!mQuit && !mRestartStreams.empty()) {
387*d57664e9SAndroid Build Coastguard Worker             const nsecs_t nowNs = systemTime();
388*d57664e9SAndroid Build Coastguard Worker             auto it = mRestartStreams.begin();
389*d57664e9SAndroid Build Coastguard Worker             Stream* const stream = it->second;
390*d57664e9SAndroid Build Coastguard Worker             const int64_t diffNs = stream->getStopTimeNs() - nowNs;
391*d57664e9SAndroid Build Coastguard Worker             if (diffNs > 0) {
392*d57664e9SAndroid Build Coastguard Worker                 waitTimeNs = std::min(waitTimeNs, diffNs);
393*d57664e9SAndroid Build Coastguard Worker                 break;
394*d57664e9SAndroid Build Coastguard Worker             }
395*d57664e9SAndroid Build Coastguard Worker             mRestartStreams.erase(it);
396*d57664e9SAndroid Build Coastguard Worker             mProcessingStreams.emplace(stream);
397*d57664e9SAndroid Build Coastguard Worker             if (!mLockStreamManagerStop) lock.unlock();
398*d57664e9SAndroid Build Coastguard Worker             stream->stop();
399*d57664e9SAndroid Build Coastguard Worker             ALOGV("%s(%d) stopping streamID:%d", __func__, id, stream->getStreamID());
400*d57664e9SAndroid Build Coastguard Worker             if (Stream* nextStream = stream->playPairStream(garbage)) {
401*d57664e9SAndroid Build Coastguard Worker                 ALOGV("%s(%d) starting streamID:%d", __func__, id, nextStream->getStreamID());
402*d57664e9SAndroid Build Coastguard Worker                 if (!mLockStreamManagerStop) lock.lock();
403*d57664e9SAndroid Build Coastguard Worker                 if (nextStream->getStopTimeNs() > 0) {
404*d57664e9SAndroid Build Coastguard Worker                     // the next stream was stopped before we can move it to the active queue.
405*d57664e9SAndroid Build Coastguard Worker                     ALOGV("%s(%d) stopping started streamID:%d",
406*d57664e9SAndroid Build Coastguard Worker                             __func__, id, nextStream->getStreamID());
407*d57664e9SAndroid Build Coastguard Worker                     moveToRestartQueue_l(nextStream);
408*d57664e9SAndroid Build Coastguard Worker                 } else {
409*d57664e9SAndroid Build Coastguard Worker                     addToActiveQueue_l(nextStream);
410*d57664e9SAndroid Build Coastguard Worker                 }
411*d57664e9SAndroid Build Coastguard Worker             } else {
412*d57664e9SAndroid Build Coastguard Worker                 if (!mLockStreamManagerStop) lock.lock();
413*d57664e9SAndroid Build Coastguard Worker                 mAvailableStreams.insert(stream);
414*d57664e9SAndroid Build Coastguard Worker             }
415*d57664e9SAndroid Build Coastguard Worker             mProcessingStreams.erase(stream);
416*d57664e9SAndroid Build Coastguard Worker             sanityCheckQueue_l();
417*d57664e9SAndroid Build Coastguard Worker             if (!garbage.empty()) {
418*d57664e9SAndroid Build Coastguard Worker                 lock.unlock();
419*d57664e9SAndroid Build Coastguard Worker                 // garbage audio tracks (etc) are cleared here outside mStreamManagerLock.
420*d57664e9SAndroid Build Coastguard Worker                 garbage.clear();
421*d57664e9SAndroid Build Coastguard Worker                 lock.lock();
422*d57664e9SAndroid Build Coastguard Worker             }
423*d57664e9SAndroid Build Coastguard Worker         }
424*d57664e9SAndroid Build Coastguard Worker     }
425*d57664e9SAndroid Build Coastguard Worker     ALOGV("%s(%d) exiting", __func__, id);
426*d57664e9SAndroid Build Coastguard Worker }
427*d57664e9SAndroid Build Coastguard Worker 
dump() const428*d57664e9SAndroid Build Coastguard Worker void StreamManager::dump() const
429*d57664e9SAndroid Build Coastguard Worker {
430*d57664e9SAndroid Build Coastguard Worker     forEach([](const Stream *stream) { stream->dump(); });
431*d57664e9SAndroid Build Coastguard Worker }
432*d57664e9SAndroid Build Coastguard Worker 
sanityCheckQueue_l() const433*d57664e9SAndroid Build Coastguard Worker void StreamManager::sanityCheckQueue_l() const
434*d57664e9SAndroid Build Coastguard Worker {
435*d57664e9SAndroid Build Coastguard Worker     // We want to preserve the invariant that each stream pair is exactly on one of the queues.
436*d57664e9SAndroid Build Coastguard Worker     const size_t availableStreams = mAvailableStreams.size();
437*d57664e9SAndroid Build Coastguard Worker     const size_t restartStreams = mRestartStreams.size();
438*d57664e9SAndroid Build Coastguard Worker     const size_t activeStreams = mActiveStreams.size();
439*d57664e9SAndroid Build Coastguard Worker     const size_t processingStreams = mProcessingStreams.size();
440*d57664e9SAndroid Build Coastguard Worker     const size_t managedStreams = availableStreams + restartStreams + activeStreams
441*d57664e9SAndroid Build Coastguard Worker                 + processingStreams;
442*d57664e9SAndroid Build Coastguard Worker     const size_t totalStreams = getStreamMapSize() >> 1;
443*d57664e9SAndroid Build Coastguard Worker     LOG_ALWAYS_FATAL_IF(managedStreams != totalStreams,
444*d57664e9SAndroid Build Coastguard Worker             "%s: mAvailableStreams:%zu + mRestartStreams:%zu + "
445*d57664e9SAndroid Build Coastguard Worker             "mActiveStreams:%zu + mProcessingStreams:%zu = %zu != total streams %zu",
446*d57664e9SAndroid Build Coastguard Worker             __func__, availableStreams, restartStreams, activeStreams, processingStreams,
447*d57664e9SAndroid Build Coastguard Worker             managedStreams, totalStreams);
448*d57664e9SAndroid Build Coastguard Worker     ALOGV("%s: mAvailableStreams:%zu + mRestartStreams:%zu + "
449*d57664e9SAndroid Build Coastguard Worker             "mActiveStreams:%zu + mProcessingStreams:%zu = %zu (total streams: %zu)",
450*d57664e9SAndroid Build Coastguard Worker             __func__, availableStreams, restartStreams, activeStreams, processingStreams,
451*d57664e9SAndroid Build Coastguard Worker             managedStreams, totalStreams);
452*d57664e9SAndroid Build Coastguard Worker }
453*d57664e9SAndroid Build Coastguard Worker 
454*d57664e9SAndroid Build Coastguard Worker } // namespace android::soundpool
455