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