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