• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 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 "TranscodingSessionController"
19 
20 #define VALIDATE_STATE 1
21 
22 #include <android/permission_manager.h>
23 #include <inttypes.h>
24 #include <media/TranscodingSessionController.h>
25 #include <media/TranscodingUidPolicy.h>
26 #include <utils/AndroidThreads.h>
27 #include <utils/Log.h>
28 
29 #include <thread>
30 #include <utility>
31 
32 namespace android {
33 
34 static_assert((SessionIdType)-1 < 0, "SessionIdType should be signed");
35 
36 constexpr static uid_t OFFLINE_UID = -1;
37 constexpr static size_t kSessionHistoryMax = 100;
38 
39 //static
sessionToString(const SessionKeyType & sessionKey)40 String8 TranscodingSessionController::sessionToString(const SessionKeyType& sessionKey) {
41     return String8::format("{client:%lld, session:%d}", (long long)sessionKey.first,
42                            sessionKey.second);
43 }
44 
45 //static
sessionStateToString(const Session::State sessionState)46 const char* TranscodingSessionController::sessionStateToString(const Session::State sessionState) {
47     switch (sessionState) {
48     case Session::State::NOT_STARTED:
49         return "NOT_STARTED";
50     case Session::State::RUNNING:
51         return "RUNNING";
52     case Session::State::PAUSED:
53         return "PAUSED";
54     case Session::State::FINISHED:
55         return "FINISHED";
56     case Session::State::CANCELED:
57         return "CANCELED";
58     case Session::State::ERROR:
59         return "ERROR";
60     default:
61         break;
62     }
63     return "(unknown)";
64 }
65 
66 ///////////////////////////////////////////////////////////////////////////////
67 struct TranscodingSessionController::Watchdog {
68     Watchdog(TranscodingSessionController* owner, int64_t timeoutUs);
69     ~Watchdog();
70 
71     // Starts monitoring the session.
72     void start(const SessionKeyType& key);
73     // Stops monitoring the session.
74     void stop();
75     // Signals that the session is still alive. Must be sent at least every mTimeoutUs.
76     // (Timeout will happen if no ping in mTimeoutUs since the last ping.)
77     void keepAlive();
78 
79 private:
80     void threadLoop();
81     void updateTimer_l();
82 
83     TranscodingSessionController* mOwner;
84     const int64_t mTimeoutUs;
85     mutable std::mutex mLock;
86     std::condition_variable mCondition GUARDED_BY(mLock);
87     // Whether watchdog is monitoring a session for timeout.
88     bool mActive GUARDED_BY(mLock);
89     // Whether watchdog is aborted and the monitoring thread should exit.
90     bool mAbort GUARDED_BY(mLock);
91     // When watchdog is active, the next timeout time point.
92     std::chrono::steady_clock::time_point mNextTimeoutTime GUARDED_BY(mLock);
93     // When watchdog is active, the session being watched.
94     SessionKeyType mSessionToWatch GUARDED_BY(mLock);
95     std::thread mThread;
96 };
97 
Watchdog(TranscodingSessionController * owner,int64_t timeoutUs)98 TranscodingSessionController::Watchdog::Watchdog(TranscodingSessionController* owner,
99                                                  int64_t timeoutUs)
100       : mOwner(owner),
101         mTimeoutUs(timeoutUs),
102         mActive(false),
103         mAbort(false),
104         mThread(&Watchdog::threadLoop, this) {
105     ALOGV("Watchdog CTOR: %p", this);
106 }
107 
~Watchdog()108 TranscodingSessionController::Watchdog::~Watchdog() {
109     ALOGV("Watchdog DTOR: %p", this);
110 
111     {
112         // Exit the looper thread.
113         std::scoped_lock lock{mLock};
114 
115         mAbort = true;
116         mCondition.notify_one();
117     }
118 
119     mThread.join();
120     ALOGV("Watchdog DTOR: %p, done.", this);
121 }
122 
start(const SessionKeyType & key)123 void TranscodingSessionController::Watchdog::start(const SessionKeyType& key) {
124     std::scoped_lock lock{mLock};
125 
126     if (!mActive) {
127         ALOGI("Watchdog start: %s", sessionToString(key).c_str());
128 
129         mActive = true;
130         mSessionToWatch = key;
131         updateTimer_l();
132         mCondition.notify_one();
133     }
134 }
135 
stop()136 void TranscodingSessionController::Watchdog::stop() {
137     std::scoped_lock lock{mLock};
138 
139     if (mActive) {
140         ALOGI("Watchdog stop: %s", sessionToString(mSessionToWatch).c_str());
141 
142         mActive = false;
143         mCondition.notify_one();
144     }
145 }
146 
keepAlive()147 void TranscodingSessionController::Watchdog::keepAlive() {
148     std::scoped_lock lock{mLock};
149 
150     if (mActive) {
151         ALOGI("Watchdog keepAlive: %s", sessionToString(mSessionToWatch).c_str());
152 
153         updateTimer_l();
154         mCondition.notify_one();
155     }
156 }
157 
158 // updateTimer_l() is only called with lock held.
updateTimer_l()159 void TranscodingSessionController::Watchdog::updateTimer_l() NO_THREAD_SAFETY_ANALYSIS {
160     std::chrono::microseconds timeout(mTimeoutUs);
161     mNextTimeoutTime = std::chrono::steady_clock::now() + timeout;
162 }
163 
164 // Unfortunately std::unique_lock is incompatible with -Wthread-safety.
threadLoop()165 void TranscodingSessionController::Watchdog::threadLoop() NO_THREAD_SAFETY_ANALYSIS {
166     androidSetThreadPriority(0 /*tid (0 = current) */, ANDROID_PRIORITY_BACKGROUND);
167     std::unique_lock<std::mutex> lock{mLock};
168 
169     while (!mAbort) {
170         if (!mActive) {
171             mCondition.wait(lock);
172             continue;
173         }
174         // Watchdog active, wait till next timeout time.
175         if (mCondition.wait_until(lock, mNextTimeoutTime) == std::cv_status::timeout) {
176             // If timeout happens, report timeout and deactivate watchdog.
177             mActive = false;
178             // Make a copy of session key, as once we unlock, it could be unprotected.
179             SessionKeyType sessionKey = mSessionToWatch;
180 
181             ALOGE("Watchdog timeout: %s", sessionToString(sessionKey).c_str());
182 
183             lock.unlock();
184             mOwner->onError(sessionKey.first, sessionKey.second,
185                             TranscodingErrorCode::kWatchdogTimeout);
186             lock.lock();
187         }
188     }
189 }
190 ///////////////////////////////////////////////////////////////////////////////
191 struct TranscodingSessionController::Pacer {
Pacerandroid::TranscodingSessionController::Pacer192     Pacer(const ControllerConfig& config)
193           : mBurstThresholdMs(config.pacerBurstThresholdMs),
194             mBurstCountQuota(config.pacerBurstCountQuota),
195             mBurstTimeQuotaSec(config.pacerBurstTimeQuotaSeconds) {}
196 
197     ~Pacer() = default;
198 
199     bool onSessionStarted(uid_t uid, uid_t callingUid);
200     void onSessionCompleted(uid_t uid, std::chrono::microseconds runningTime);
201     void onSessionCancelled(uid_t uid);
202 
203 private:
204     // Threshold of time between finish/start below which a back-to-back start is counted.
205     int32_t mBurstThresholdMs;
206     // Maximum allowed back-to-back start count.
207     int32_t mBurstCountQuota;
208     // Maximum allowed back-to-back running time.
209     int32_t mBurstTimeQuotaSec;
210 
211     struct UidHistoryEntry {
212         bool sessionActive = false;
213         int32_t burstCount = 0;
214         std::chrono::steady_clock::duration burstDuration{0};
215         std::chrono::steady_clock::time_point lastCompletedTime;
216     };
217     std::map<uid_t, UidHistoryEntry> mUidHistoryMap;
218     std::unordered_set<uid_t> mMtpUids;
219     std::unordered_set<uid_t> mNonMtpUids;
220 
221     bool isSubjectToQuota(uid_t uid, uid_t callingUid);
222 };
223 
isSubjectToQuota(uid_t uid,uid_t callingUid)224 bool TranscodingSessionController::Pacer::isSubjectToQuota(uid_t uid, uid_t callingUid) {
225     // Submitting with self uid is not limited (which can only happen if it's used as an
226     // app-facing API). MediaProvider usage always submit on behalf of other uids.
227     if (uid == callingUid) {
228         return false;
229     }
230 
231     if (mMtpUids.find(uid) != mMtpUids.end()) {
232         return false;
233     }
234 
235     if (mNonMtpUids.find(uid) != mNonMtpUids.end()) {
236         return true;
237     }
238 
239     // We don't have MTP permission info about this uid yet, check permission and save the result.
240     int32_t result;
241     if (__builtin_available(android __TRANSCODING_MIN_API__, *)) {
242         if (APermissionManager_checkPermission("android.permission.ACCESS_MTP", -1 /*pid*/, uid,
243                                                &result) == PERMISSION_MANAGER_STATUS_OK &&
244             result == PERMISSION_MANAGER_PERMISSION_GRANTED) {
245             mMtpUids.insert(uid);
246             return false;
247         }
248     }
249 
250     mNonMtpUids.insert(uid);
251     return true;
252 }
253 
onSessionStarted(uid_t uid,uid_t callingUid)254 bool TranscodingSessionController::Pacer::onSessionStarted(uid_t uid, uid_t callingUid) {
255     if (!isSubjectToQuota(uid, callingUid)) {
256         ALOGI("Pacer::onSessionStarted: uid %d (caling uid: %d): not subject to quota", uid,
257               callingUid);
258         return true;
259     }
260 
261     // If uid doesn't exist, only insert the entry and mark session active. Skip quota checking.
262     if (mUidHistoryMap.find(uid) == mUidHistoryMap.end()) {
263         mUidHistoryMap.emplace(uid, UidHistoryEntry{});
264         mUidHistoryMap[uid].sessionActive = true;
265         ALOGV("Pacer::onSessionStarted: uid %d: new", uid);
266         return true;
267     }
268 
269     // TODO: if Thermal throttling or resoure lost happened to occurr between this start
270     // and the previous completion, we should deduct the paused time from the elapsed time.
271     // (Individual session's pause time, on the other hand, doesn't need to be deducted
272     // because it doesn't affect the gap between last completion and the start.
273     auto timeSinceLastComplete =
274             std::chrono::steady_clock::now() - mUidHistoryMap[uid].lastCompletedTime;
275     if (mUidHistoryMap[uid].burstCount >= mBurstCountQuota &&
276         mUidHistoryMap[uid].burstDuration >= std::chrono::seconds(mBurstTimeQuotaSec)) {
277         ALOGW("Pacer::onSessionStarted: uid %d: over quota, burst count %d, time %lldms", uid,
278               mUidHistoryMap[uid].burstCount,
279               (long long)mUidHistoryMap[uid].burstDuration.count() / 1000000);
280         return false;
281     }
282 
283     // If not over quota, allow the session, and reset as long as this is not too close
284     // to previous completion.
285     if (timeSinceLastComplete > std::chrono::milliseconds(mBurstThresholdMs)) {
286         ALOGV("Pacer::onSessionStarted: uid %d: reset quota", uid);
287         mUidHistoryMap[uid].burstCount = 0;
288         mUidHistoryMap[uid].burstDuration = std::chrono::milliseconds(0);
289     } else {
290         ALOGV("Pacer::onSessionStarted: uid %d: burst count %d, time %lldms", uid,
291               mUidHistoryMap[uid].burstCount,
292               (long long)mUidHistoryMap[uid].burstDuration.count() / 1000000);
293     }
294 
295     mUidHistoryMap[uid].sessionActive = true;
296     return true;
297 }
298 
onSessionCompleted(uid_t uid,std::chrono::microseconds runningTime)299 void TranscodingSessionController::Pacer::onSessionCompleted(
300         uid_t uid, std::chrono::microseconds runningTime) {
301     // Skip quota update if this uid missed the start. (Could happen if the uid is added via
302     // addClientUid() after the session start.)
303     if (mUidHistoryMap.find(uid) == mUidHistoryMap.end() || !mUidHistoryMap[uid].sessionActive) {
304         ALOGV("Pacer::onSessionCompleted: uid %d: not started", uid);
305         return;
306     }
307     ALOGV("Pacer::onSessionCompleted: uid %d: runningTime %lld", uid, runningTime.count() / 1000);
308     mUidHistoryMap[uid].sessionActive = false;
309     mUidHistoryMap[uid].burstCount++;
310     mUidHistoryMap[uid].burstDuration += runningTime;
311     mUidHistoryMap[uid].lastCompletedTime = std::chrono::steady_clock::now();
312 }
313 
onSessionCancelled(uid_t uid)314 void TranscodingSessionController::Pacer::onSessionCancelled(uid_t uid) {
315     if (mUidHistoryMap.find(uid) == mUidHistoryMap.end()) {
316         ALOGV("Pacer::onSessionCancelled: uid %d: not present", uid);
317         return;
318     }
319     // This is only called if a uid is removed from a session (due to it being killed
320     // or the original submitting client was gone but session was kept for offline use).
321     // Since the uid is going to miss the onSessionCompleted(), we can't track this
322     // session, and have to check back at next onSessionStarted().
323     mUidHistoryMap[uid].sessionActive = false;
324 }
325 
326 ///////////////////////////////////////////////////////////////////////////////
327 
TranscodingSessionController(const TranscoderFactoryType & transcoderFactory,const std::shared_ptr<UidPolicyInterface> & uidPolicy,const std::shared_ptr<ResourcePolicyInterface> & resourcePolicy,const std::shared_ptr<ThermalPolicyInterface> & thermalPolicy,const ControllerConfig * config)328 TranscodingSessionController::TranscodingSessionController(
329         const TranscoderFactoryType& transcoderFactory,
330         const std::shared_ptr<UidPolicyInterface>& uidPolicy,
331         const std::shared_ptr<ResourcePolicyInterface>& resourcePolicy,
332         const std::shared_ptr<ThermalPolicyInterface>& thermalPolicy,
333         const ControllerConfig* config)
334       : mTranscoderFactory(transcoderFactory),
335         mUidPolicy(uidPolicy),
336         mResourcePolicy(resourcePolicy),
337         mThermalPolicy(thermalPolicy),
338         mCurrentSession(nullptr),
339         mResourceLost(false) {
340     // Only push empty offline queue initially. Realtime queues are added when requests come in.
341     mUidSortedList.push_back(OFFLINE_UID);
342     mOfflineUidIterator = mUidSortedList.begin();
343     mSessionQueues.emplace(OFFLINE_UID, SessionQueueType());
344     mUidPackageNames[OFFLINE_UID] = "(offline)";
345     mThermalThrottling = thermalPolicy->getThrottlingStatus();
346     if (config != nullptr) {
347         mConfig = *config;
348     }
349     mPacer.reset(new Pacer(mConfig));
350     ALOGD("@@@ watchdog %lld, burst count %d, burst time %d, burst threshold %d",
351           (long long)mConfig.watchdogTimeoutUs, mConfig.pacerBurstCountQuota,
352           mConfig.pacerBurstTimeQuotaSeconds, mConfig.pacerBurstThresholdMs);
353 }
354 
~TranscodingSessionController()355 TranscodingSessionController::~TranscodingSessionController() {}
356 
dumpSession_l(const Session & session,String8 & result,bool closedSession)357 void TranscodingSessionController::dumpSession_l(const Session& session, String8& result,
358                                                  bool closedSession) {
359     const size_t SIZE = 256;
360     char buffer[SIZE];
361     const TranscodingRequestParcel& request = session.request;
362     snprintf(buffer, SIZE, "      Session: %s, %s, %d%%\n", sessionToString(session.key).c_str(),
363              sessionStateToString(session.getState()), session.lastProgress);
364     result.append(buffer);
365     snprintf(buffer, SIZE, "        pkg: %s\n", request.clientPackageName.c_str());
366     result.append(buffer);
367     snprintf(buffer, SIZE, "        src: %s\n", request.sourceFilePath.c_str());
368     result.append(buffer);
369     snprintf(buffer, SIZE, "        dst: %s\n", request.destinationFilePath.c_str());
370     result.append(buffer);
371 
372     if (closedSession) {
373         snprintf(buffer, SIZE,
374                  "        waiting: %.1fs, running: %.1fs, paused: %.1fs, paused count: %d\n",
375                  session.waitingTime.count() / 1000000.0f, session.runningTime.count() / 1000000.0f,
376                  session.pausedTime.count() / 1000000.0f, session.pauseCount);
377         result.append(buffer);
378     }
379 }
380 
dumpAllSessions(int fd,const Vector<String16> & args __unused)381 void TranscodingSessionController::dumpAllSessions(int fd, const Vector<String16>& args __unused) {
382     String8 result;
383 
384     const size_t SIZE = 256;
385     char buffer[SIZE];
386     std::scoped_lock lock{mLock};
387 
388     snprintf(buffer, SIZE, "\n========== Dumping live sessions queues =========\n");
389     result.append(buffer);
390     snprintf(buffer, SIZE, "  Total num of Sessions: %zu\n", mSessionMap.size());
391     result.append(buffer);
392 
393     std::vector<int32_t> uids(mUidSortedList.begin(), mUidSortedList.end());
394 
395     for (int32_t i = 0; i < uids.size(); i++) {
396         const uid_t uid = uids[i];
397 
398         if (mSessionQueues[uid].empty()) {
399             continue;
400         }
401         snprintf(buffer, SIZE, "    uid: %d, pkg: %s\n", uid,
402                  mUidPackageNames.count(uid) > 0 ? mUidPackageNames[uid].c_str() : "(unknown)");
403         result.append(buffer);
404         snprintf(buffer, SIZE, "      Num of sessions: %zu\n", mSessionQueues[uid].size());
405         result.append(buffer);
406         for (auto& sessionKey : mSessionQueues[uid]) {
407             auto sessionIt = mSessionMap.find(sessionKey);
408             if (sessionIt == mSessionMap.end()) {
409                 snprintf(buffer, SIZE, "Failed to look up Session %s  \n",
410                          sessionToString(sessionKey).c_str());
411                 result.append(buffer);
412                 continue;
413             }
414             dumpSession_l(sessionIt->second, result);
415         }
416     }
417 
418     snprintf(buffer, SIZE, "\n========== Dumping past sessions =========\n");
419     result.append(buffer);
420     for (auto& session : mSessionHistory) {
421         dumpSession_l(session, result, true /*closedSession*/);
422     }
423 
424     write(fd, result.string(), result.size());
425 }
426 
427 /*
428  * Returns nullptr if there is no session, or we're paused globally (due to resource lost,
429  * thermal throttling, etc.). Otherwise, return the session that should be run next.
430  */
getTopSession_l()431 TranscodingSessionController::Session* TranscodingSessionController::getTopSession_l() {
432     if (mSessionMap.empty()) {
433         return nullptr;
434     }
435 
436     // Return nullptr if we're paused globally due to resource lost or thermal throttling.
437     if (((mResourcePolicy != nullptr && mResourceLost) ||
438          (mThermalPolicy != nullptr && mThermalThrottling))) {
439         return nullptr;
440     }
441 
442     uid_t topUid = *mUidSortedList.begin();
443     // If the current session is running, and it's in the topUid's queue, let it continue
444     // to run even if it's not the earliest in that uid's queue.
445     // For example, uid(B) is added to a session while it's pending in uid(A)'s queue, then
446     // B is brought to front which caused the session to run, then user switches back to A.
447     if (mCurrentSession != nullptr && mCurrentSession->getState() == Session::RUNNING &&
448         mCurrentSession->allClientUids.count(topUid) > 0) {
449         return mCurrentSession;
450     }
451     SessionKeyType topSessionKey = *mSessionQueues[topUid].begin();
452     return &mSessionMap[topSessionKey];
453 }
454 
setSessionState_l(Session * session,Session::State state)455 void TranscodingSessionController::setSessionState_l(Session* session, Session::State state) {
456     bool wasRunning = (session->getState() == Session::RUNNING);
457     session->setState(state);
458     bool isRunning = (session->getState() == Session::RUNNING);
459 
460     if (wasRunning == isRunning) {
461         return;
462     }
463 
464     // Currently we only have 1 running session, and we always put the previous
465     // session in non-running state before we run the new session, so it's okay
466     // to start/stop the watchdog here. If this assumption changes, we need to
467     // track the number of running sessions and start/stop watchdog based on that.
468     if (isRunning) {
469         mWatchdog->start(session->key);
470     } else {
471         mWatchdog->stop();
472     }
473 }
474 
setState(Session::State newState)475 void TranscodingSessionController::Session::setState(Session::State newState) {
476     if (state == newState) {
477         return;
478     }
479     auto nowTime = std::chrono::steady_clock::now();
480     if (state != INVALID) {
481         std::chrono::microseconds elapsedTime =
482                 std::chrono::duration_cast<std::chrono::microseconds>(nowTime - stateEnterTime);
483         switch (state) {
484         case PAUSED:
485             pausedTime = pausedTime + elapsedTime;
486             break;
487         case RUNNING:
488             runningTime = runningTime + elapsedTime;
489             break;
490         case NOT_STARTED:
491             waitingTime = waitingTime + elapsedTime;
492             break;
493         default:
494             break;
495         }
496     }
497     if (newState == PAUSED) {
498         pauseCount++;
499     }
500     stateEnterTime = nowTime;
501     state = newState;
502 }
503 
updateCurrentSession_l()504 void TranscodingSessionController::updateCurrentSession_l() {
505     Session* curSession = mCurrentSession;
506     Session* topSession = nullptr;
507 
508     // Delayed init of transcoder and watchdog.
509     if (mTranscoder == nullptr) {
510         mTranscoder = mTranscoderFactory(shared_from_this());
511         mWatchdog = std::make_shared<Watchdog>(this, mConfig.watchdogTimeoutUs);
512     }
513 
514     // If we found a different top session, or the top session's running state is not
515     // correct. Take some actions to ensure it's correct.
516     while ((topSession = getTopSession_l()) != curSession ||
517            (topSession != nullptr && !topSession->isRunning())) {
518         ALOGV("updateCurrentSession_l: topSession is %s, curSession is %s",
519               topSession == nullptr ? "null" : sessionToString(topSession->key).c_str(),
520               curSession == nullptr ? "null" : sessionToString(curSession->key).c_str());
521 
522         // If current session is running, pause it first. Note this is needed for either
523         // cases: 1) Top session is changing to another session, or 2) Top session is
524         // changing to null (which means we should be globally paused).
525         if (curSession != nullptr && curSession->getState() == Session::RUNNING) {
526             mTranscoder->pause(curSession->key.first, curSession->key.second);
527             setSessionState_l(curSession, Session::PAUSED);
528         }
529 
530         if (topSession == nullptr) {
531             // Nothing more to run (either no session or globally paused).
532             break;
533         }
534 
535         // Otherwise, ensure topSession is running.
536         if (topSession->getState() == Session::NOT_STARTED) {
537             // Check if at least one client has quota to start the session.
538             bool keepForClient = false;
539             for (uid_t uid : topSession->allClientUids) {
540                 if (mPacer->onSessionStarted(uid, topSession->callingUid)) {
541                     keepForClient = true;
542                     // DO NOT break here, because book-keeping still needs to happen
543                     // for the other uids.
544                 }
545             }
546             if (!keepForClient) {
547                 // Unfortunately all uids requesting this session are out of quota.
548                 // Drop this session and try the next one.
549                 {
550                     auto clientCallback = mSessionMap[topSession->key].callback.lock();
551                     if (clientCallback != nullptr) {
552                         clientCallback->onTranscodingFailed(
553                                 topSession->key.second, TranscodingErrorCode::kDroppedByService);
554                     }
555                 }
556                 removeSession_l(topSession->key, Session::DROPPED_BY_PACER);
557                 continue;
558             }
559             mTranscoder->start(topSession->key.first, topSession->key.second, topSession->request,
560                                topSession->callingUid, topSession->callback.lock());
561             setSessionState_l(topSession, Session::RUNNING);
562         } else if (topSession->getState() == Session::PAUSED) {
563             mTranscoder->resume(topSession->key.first, topSession->key.second, topSession->request,
564                                 topSession->callingUid, topSession->callback.lock());
565             setSessionState_l(topSession, Session::RUNNING);
566         }
567         break;
568     }
569     mCurrentSession = topSession;
570 }
571 
addUidToSession_l(uid_t clientUid,const SessionKeyType & sessionKey)572 void TranscodingSessionController::addUidToSession_l(uid_t clientUid,
573                                                      const SessionKeyType& sessionKey) {
574     // If it's an offline session, the queue was already added in constructor.
575     // If it's a real-time sessions, check if a queue is already present for the uid,
576     // and add a new queue if needed.
577     if (clientUid != OFFLINE_UID) {
578         if (mSessionQueues.count(clientUid) == 0) {
579             mUidPolicy->registerMonitorUid(clientUid);
580             if (mUidPolicy->isUidOnTop(clientUid)) {
581                 mUidSortedList.push_front(clientUid);
582             } else {
583                 // Shouldn't be submitting real-time requests from non-top app,
584                 // put it in front of the offline queue.
585                 mUidSortedList.insert(mOfflineUidIterator, clientUid);
586             }
587         } else if (clientUid != *mUidSortedList.begin()) {
588             if (mUidPolicy->isUidOnTop(clientUid)) {
589                 mUidSortedList.remove(clientUid);
590                 mUidSortedList.push_front(clientUid);
591             }
592         }
593     }
594     // Append this session to the uid's queue.
595     mSessionQueues[clientUid].push_back(sessionKey);
596 }
597 
removeSession_l(const SessionKeyType & sessionKey,Session::State finalState,const std::shared_ptr<std::function<bool (uid_t uid)>> & keepUid)598 void TranscodingSessionController::removeSession_l(
599         const SessionKeyType& sessionKey, Session::State finalState,
600         const std::shared_ptr<std::function<bool(uid_t uid)>>& keepUid) {
601     ALOGV("%s: session %s", __FUNCTION__, sessionToString(sessionKey).c_str());
602 
603     if (mSessionMap.count(sessionKey) == 0) {
604         ALOGE("session %s doesn't exist", sessionToString(sessionKey).c_str());
605         return;
606     }
607 
608     // Remove session from uid's queue.
609     bool uidQueueRemoved = false;
610     std::unordered_set<uid_t> remainingUids;
611     for (uid_t uid : mSessionMap[sessionKey].allClientUids) {
612         if (keepUid != nullptr) {
613             if ((*keepUid)(uid)) {
614                 remainingUids.insert(uid);
615                 continue;
616             }
617             // If we have uids to keep, the session is not going to any final
618             // state we can't use onSessionCompleted as the running time will
619             // not be valid. Only notify pacer to stop tracking this session.
620             mPacer->onSessionCancelled(uid);
621         }
622         SessionQueueType& sessionQueue = mSessionQueues[uid];
623         auto it = std::find(sessionQueue.begin(), sessionQueue.end(), sessionKey);
624         if (it == sessionQueue.end()) {
625             ALOGW("couldn't find session %s in queue for uid %d",
626                   sessionToString(sessionKey).c_str(), uid);
627             continue;
628         }
629         sessionQueue.erase(it);
630 
631         // If this is the last session in a real-time queue, remove this uid's queue.
632         if (uid != OFFLINE_UID && sessionQueue.empty()) {
633             mUidSortedList.remove(uid);
634             mSessionQueues.erase(uid);
635             mUidPolicy->unregisterMonitorUid(uid);
636 
637             uidQueueRemoved = true;
638         }
639     }
640 
641     if (uidQueueRemoved) {
642         std::unordered_set<uid_t> topUids = mUidPolicy->getTopUids();
643         moveUidsToTop_l(topUids, false /*preserveTopUid*/);
644     }
645 
646     if (keepUid != nullptr) {
647         mSessionMap[sessionKey].allClientUids = remainingUids;
648         return;
649     }
650 
651     // Clear current session.
652     if (mCurrentSession == &mSessionMap[sessionKey]) {
653         mCurrentSession = nullptr;
654     }
655 
656     setSessionState_l(&mSessionMap[sessionKey], finalState);
657 
658     // We can use onSessionCompleted() even for CANCELLED, because runningTime is
659     // now updated by setSessionState_l().
660     for (uid_t uid : mSessionMap[sessionKey].allClientUids) {
661         mPacer->onSessionCompleted(uid, mSessionMap[sessionKey].runningTime);
662     }
663 
664     mSessionHistory.push_back(mSessionMap[sessionKey]);
665     if (mSessionHistory.size() > kSessionHistoryMax) {
666         mSessionHistory.erase(mSessionHistory.begin());
667     }
668 
669     // Remove session from session map.
670     mSessionMap.erase(sessionKey);
671 }
672 
673 /**
674  * Moves the set of uids to the front of mUidSortedList (which is used to pick
675  * the next session to run).
676  *
677  * This is called when 1) we received a onTopUidsChanged() callback from UidPolicy,
678  * or 2) we removed the session queue for a uid because it becomes empty.
679  *
680  * In case of 1), if there are multiple uids in the set, and the current front
681  * uid in mUidSortedList is still in the set, we try to keep that uid at front
682  * so that current session run is not interrupted. (This is not a concern for case 2)
683  * because the queue for a uid was just removed entirely.)
684  */
moveUidsToTop_l(const std::unordered_set<uid_t> & uids,bool preserveTopUid)685 void TranscodingSessionController::moveUidsToTop_l(const std::unordered_set<uid_t>& uids,
686                                                    bool preserveTopUid) {
687     // If uid set is empty, nothing to do. Do not change the queue status.
688     if (uids.empty()) {
689         return;
690     }
691 
692     // Save the current top uid.
693     uid_t curTopUid = *mUidSortedList.begin();
694     bool pushCurTopToFront = false;
695     int32_t numUidsMoved = 0;
696 
697     // Go through the sorted uid list once, and move the ones in top set to front.
698     for (auto it = mUidSortedList.begin(); it != mUidSortedList.end();) {
699         uid_t uid = *it;
700 
701         if (uid != OFFLINE_UID && uids.count(uid) > 0) {
702             it = mUidSortedList.erase(it);
703 
704             // If this is the top we're preserving, don't push it here, push
705             // it after the for-loop.
706             if (uid == curTopUid && preserveTopUid) {
707                 pushCurTopToFront = true;
708             } else {
709                 mUidSortedList.push_front(uid);
710             }
711 
712             // If we found all uids in the set, break out.
713             if (++numUidsMoved == uids.size()) {
714                 break;
715             }
716         } else {
717             ++it;
718         }
719     }
720 
721     if (pushCurTopToFront) {
722         mUidSortedList.push_front(curTopUid);
723     }
724 }
725 
submit(ClientIdType clientId,SessionIdType sessionId,uid_t callingUid,uid_t clientUid,const TranscodingRequestParcel & request,const std::weak_ptr<ITranscodingClientCallback> & callback)726 bool TranscodingSessionController::submit(
727         ClientIdType clientId, SessionIdType sessionId, uid_t callingUid, uid_t clientUid,
728         const TranscodingRequestParcel& request,
729         const std::weak_ptr<ITranscodingClientCallback>& callback) {
730     SessionKeyType sessionKey = std::make_pair(clientId, sessionId);
731 
732     ALOGV("%s: session %s, uid %d, prioirty %d", __FUNCTION__, sessionToString(sessionKey).c_str(),
733           clientUid, (int32_t)request.priority);
734 
735     std::scoped_lock lock{mLock};
736 
737     if (mSessionMap.count(sessionKey) > 0) {
738         ALOGE("session %s already exists", sessionToString(sessionKey).c_str());
739         return false;
740     }
741 
742     // Add the uid package name to the store of package names we already know.
743     if (mUidPackageNames.count(clientUid) == 0) {
744         mUidPackageNames.emplace(clientUid, request.clientPackageName);
745     }
746 
747     // TODO(chz): only support offline vs real-time for now. All kUnspecified sessions
748     // go to offline queue.
749     if (request.priority == TranscodingSessionPriority::kUnspecified) {
750         clientUid = OFFLINE_UID;
751     }
752 
753     // Add session to session map.
754     mSessionMap[sessionKey].key = sessionKey;
755     mSessionMap[sessionKey].callingUid = callingUid;
756     mSessionMap[sessionKey].allClientUids.insert(clientUid);
757     mSessionMap[sessionKey].request = request;
758     mSessionMap[sessionKey].callback = callback;
759     setSessionState_l(&mSessionMap[sessionKey], Session::NOT_STARTED);
760 
761     addUidToSession_l(clientUid, sessionKey);
762 
763     updateCurrentSession_l();
764 
765     validateState_l();
766     return true;
767 }
768 
cancel(ClientIdType clientId,SessionIdType sessionId)769 bool TranscodingSessionController::cancel(ClientIdType clientId, SessionIdType sessionId) {
770     SessionKeyType sessionKey = std::make_pair(clientId, sessionId);
771 
772     ALOGV("%s: session %s", __FUNCTION__, sessionToString(sessionKey).c_str());
773 
774     std::list<SessionKeyType> sessionsToRemove, sessionsForOffline;
775 
776     std::scoped_lock lock{mLock};
777 
778     if (sessionId < 0) {
779         for (auto it = mSessionMap.begin(); it != mSessionMap.end(); ++it) {
780             if (it->first.first == clientId) {
781                 // If there is offline request, only keep the offline client;
782                 // otherwise remove the session.
783                 if (it->second.allClientUids.count(OFFLINE_UID) > 0) {
784                     sessionsForOffline.push_back(it->first);
785                 } else {
786                     sessionsToRemove.push_back(it->first);
787                 }
788             }
789         }
790     } else {
791         if (mSessionMap.count(sessionKey) == 0) {
792             ALOGE("session %s doesn't exist", sessionToString(sessionKey).c_str());
793             return false;
794         }
795         sessionsToRemove.push_back(sessionKey);
796     }
797 
798     for (auto it = sessionsToRemove.begin(); it != sessionsToRemove.end(); ++it) {
799         // If the session has ever been started, stop it now.
800         // Note that stop() is needed even if the session is currently paused. This instructs
801         // the transcoder to discard any states for the session, otherwise the states may
802         // never be discarded.
803         if (mSessionMap[*it].getState() != Session::NOT_STARTED) {
804             mTranscoder->stop(it->first, it->second);
805         }
806 
807         // Remove the session.
808         removeSession_l(*it, Session::CANCELED);
809     }
810 
811     auto keepUid = std::make_shared<std::function<bool(uid_t)>>(
812             [](uid_t uid) { return uid == OFFLINE_UID; });
813     for (auto it = sessionsForOffline.begin(); it != sessionsForOffline.end(); ++it) {
814         removeSession_l(*it, Session::CANCELED, keepUid);
815     }
816 
817     // Start next session.
818     updateCurrentSession_l();
819 
820     validateState_l();
821     return true;
822 }
823 
addClientUid(ClientIdType clientId,SessionIdType sessionId,uid_t clientUid)824 bool TranscodingSessionController::addClientUid(ClientIdType clientId, SessionIdType sessionId,
825                                                 uid_t clientUid) {
826     SessionKeyType sessionKey = std::make_pair(clientId, sessionId);
827 
828     std::scoped_lock lock{mLock};
829 
830     if (mSessionMap.count(sessionKey) == 0) {
831         ALOGE("session %s doesn't exist", sessionToString(sessionKey).c_str());
832         return false;
833     }
834 
835     if (mSessionMap[sessionKey].allClientUids.count(clientUid) > 0) {
836         ALOGE("session %s already has uid %d", sessionToString(sessionKey).c_str(), clientUid);
837         return false;
838     }
839 
840     mSessionMap[sessionKey].allClientUids.insert(clientUid);
841     addUidToSession_l(clientUid, sessionKey);
842 
843     updateCurrentSession_l();
844 
845     validateState_l();
846     return true;
847 }
848 
getClientUids(ClientIdType clientId,SessionIdType sessionId,std::vector<int32_t> * out_clientUids)849 bool TranscodingSessionController::getClientUids(ClientIdType clientId, SessionIdType sessionId,
850                                                  std::vector<int32_t>* out_clientUids) {
851     SessionKeyType sessionKey = std::make_pair(clientId, sessionId);
852 
853     std::scoped_lock lock{mLock};
854 
855     if (mSessionMap.count(sessionKey) == 0) {
856         ALOGE("session %s doesn't exist", sessionToString(sessionKey).c_str());
857         return false;
858     }
859 
860     out_clientUids->clear();
861     for (uid_t uid : mSessionMap[sessionKey].allClientUids) {
862         if (uid != OFFLINE_UID) {
863             out_clientUids->push_back(uid);
864         }
865     }
866     return true;
867 }
868 
getSession(ClientIdType clientId,SessionIdType sessionId,TranscodingRequestParcel * request)869 bool TranscodingSessionController::getSession(ClientIdType clientId, SessionIdType sessionId,
870                                               TranscodingRequestParcel* request) {
871     SessionKeyType sessionKey = std::make_pair(clientId, sessionId);
872 
873     std::scoped_lock lock{mLock};
874 
875     if (mSessionMap.count(sessionKey) == 0) {
876         ALOGE("session %s doesn't exist", sessionToString(sessionKey).c_str());
877         return false;
878     }
879 
880     *(TranscodingRequest*)request = mSessionMap[sessionKey].request;
881     return true;
882 }
883 
notifyClient(ClientIdType clientId,SessionIdType sessionId,const char * reason,std::function<void (const SessionKeyType &)> func)884 void TranscodingSessionController::notifyClient(ClientIdType clientId, SessionIdType sessionId,
885                                                 const char* reason,
886                                                 std::function<void(const SessionKeyType&)> func) {
887     SessionKeyType sessionKey = std::make_pair(clientId, sessionId);
888 
889     std::scoped_lock lock{mLock};
890 
891     if (mSessionMap.count(sessionKey) == 0) {
892         ALOGW("%s: ignoring %s for session %s that doesn't exist", __FUNCTION__, reason,
893               sessionToString(sessionKey).c_str());
894         return;
895     }
896 
897     // Only ignore if session was never started. In particular, propagate the status
898     // to client if the session is paused. Transcoder could have posted finish when
899     // we're pausing it, and the finish arrived after we changed current session.
900     if (mSessionMap[sessionKey].getState() == Session::NOT_STARTED) {
901         ALOGW("%s: ignoring %s for session %s that was never started", __FUNCTION__, reason,
902               sessionToString(sessionKey).c_str());
903         return;
904     }
905 
906     ALOGV("%s: session %s %s", __FUNCTION__, sessionToString(sessionKey).c_str(), reason);
907     func(sessionKey);
908 }
909 
onStarted(ClientIdType clientId,SessionIdType sessionId)910 void TranscodingSessionController::onStarted(ClientIdType clientId, SessionIdType sessionId) {
911     notifyClient(clientId, sessionId, "started", [=](const SessionKeyType& sessionKey) {
912         auto callback = mSessionMap[sessionKey].callback.lock();
913         if (callback != nullptr) {
914             callback->onTranscodingStarted(sessionId);
915         }
916     });
917 }
918 
onPaused(ClientIdType clientId,SessionIdType sessionId)919 void TranscodingSessionController::onPaused(ClientIdType clientId, SessionIdType sessionId) {
920     notifyClient(clientId, sessionId, "paused", [=](const SessionKeyType& sessionKey) {
921         auto callback = mSessionMap[sessionKey].callback.lock();
922         if (callback != nullptr) {
923             callback->onTranscodingPaused(sessionId);
924         }
925     });
926 }
927 
onResumed(ClientIdType clientId,SessionIdType sessionId)928 void TranscodingSessionController::onResumed(ClientIdType clientId, SessionIdType sessionId) {
929     notifyClient(clientId, sessionId, "resumed", [=](const SessionKeyType& sessionKey) {
930         auto callback = mSessionMap[sessionKey].callback.lock();
931         if (callback != nullptr) {
932             callback->onTranscodingResumed(sessionId);
933         }
934     });
935 }
936 
onFinish(ClientIdType clientId,SessionIdType sessionId)937 void TranscodingSessionController::onFinish(ClientIdType clientId, SessionIdType sessionId) {
938     notifyClient(clientId, sessionId, "finish", [=](const SessionKeyType& sessionKey) {
939         {
940             auto clientCallback = mSessionMap[sessionKey].callback.lock();
941             if (clientCallback != nullptr) {
942                 clientCallback->onTranscodingFinished(
943                         sessionId, TranscodingResultParcel({sessionId, -1 /*actualBitrateBps*/,
944                                                             std::nullopt /*sessionStats*/}));
945             }
946         }
947 
948         // Remove the session.
949         removeSession_l(sessionKey, Session::FINISHED);
950 
951         // Start next session.
952         updateCurrentSession_l();
953 
954         validateState_l();
955     });
956 }
957 
onError(ClientIdType clientId,SessionIdType sessionId,TranscodingErrorCode err)958 void TranscodingSessionController::onError(ClientIdType clientId, SessionIdType sessionId,
959                                            TranscodingErrorCode err) {
960     notifyClient(clientId, sessionId, "error", [=](const SessionKeyType& sessionKey) {
961         if (err == TranscodingErrorCode::kWatchdogTimeout) {
962             // Abandon the transcoder, as its handler thread might be stuck in some call to
963             // MediaTranscoder altogether, and may not be able to handle any new tasks.
964             mTranscoder->stop(clientId, sessionId, true /*abandon*/);
965             // Clear the last ref count before we create new transcoder.
966             mTranscoder = nullptr;
967             mTranscoder = mTranscoderFactory(shared_from_this());
968         }
969 
970         {
971             auto clientCallback = mSessionMap[sessionKey].callback.lock();
972             if (clientCallback != nullptr) {
973                 clientCallback->onTranscodingFailed(sessionId, err);
974             }
975         }
976 
977         // Remove the session.
978         removeSession_l(sessionKey, Session::ERROR);
979 
980         // Start next session.
981         updateCurrentSession_l();
982 
983         validateState_l();
984     });
985 }
986 
onProgressUpdate(ClientIdType clientId,SessionIdType sessionId,int32_t progress)987 void TranscodingSessionController::onProgressUpdate(ClientIdType clientId, SessionIdType sessionId,
988                                                     int32_t progress) {
989     notifyClient(clientId, sessionId, "progress", [=](const SessionKeyType& sessionKey) {
990         auto callback = mSessionMap[sessionKey].callback.lock();
991         if (callback != nullptr) {
992             callback->onProgressUpdate(sessionId, progress);
993         }
994         mSessionMap[sessionKey].lastProgress = progress;
995     });
996 }
997 
onHeartBeat(ClientIdType clientId,SessionIdType sessionId)998 void TranscodingSessionController::onHeartBeat(ClientIdType clientId, SessionIdType sessionId) {
999     notifyClient(clientId, sessionId, "heart-beat",
1000                  [=](const SessionKeyType& /*sessionKey*/) { mWatchdog->keepAlive(); });
1001 }
1002 
onResourceLost(ClientIdType clientId,SessionIdType sessionId)1003 void TranscodingSessionController::onResourceLost(ClientIdType clientId, SessionIdType sessionId) {
1004     ALOGI("%s", __FUNCTION__);
1005 
1006     notifyClient(clientId, sessionId, "resource_lost", [=](const SessionKeyType& sessionKey) {
1007         if (mResourceLost) {
1008             return;
1009         }
1010 
1011         Session* resourceLostSession = &mSessionMap[sessionKey];
1012         if (resourceLostSession->getState() != Session::RUNNING) {
1013             ALOGW("session %s lost resource but is no longer running",
1014                   sessionToString(sessionKey).c_str());
1015             return;
1016         }
1017         // If we receive a resource loss event, the transcoder already paused the transcoding,
1018         // so we don't need to call onPaused() to pause it. However, we still need to notify
1019         // the client and update the session state here.
1020         setSessionState_l(resourceLostSession, Session::PAUSED);
1021         // Notify the client as a paused event.
1022         auto clientCallback = resourceLostSession->callback.lock();
1023         if (clientCallback != nullptr) {
1024             clientCallback->onTranscodingPaused(sessionKey.second);
1025         }
1026         if (mResourcePolicy != nullptr) {
1027             mResourcePolicy->setPidResourceLost(resourceLostSession->request.clientPid);
1028         }
1029         mResourceLost = true;
1030 
1031         validateState_l();
1032     });
1033 }
1034 
onTopUidsChanged(const std::unordered_set<uid_t> & uids)1035 void TranscodingSessionController::onTopUidsChanged(const std::unordered_set<uid_t>& uids) {
1036     if (uids.empty()) {
1037         ALOGW("%s: ignoring empty uids", __FUNCTION__);
1038         return;
1039     }
1040 
1041     std::string uidStr;
1042     for (auto it = uids.begin(); it != uids.end(); it++) {
1043         if (!uidStr.empty()) {
1044             uidStr += ", ";
1045         }
1046         uidStr += std::to_string(*it);
1047     }
1048 
1049     ALOGD("%s: topUids: size %zu, uids: %s", __FUNCTION__, uids.size(), uidStr.c_str());
1050 
1051     std::scoped_lock lock{mLock};
1052 
1053     moveUidsToTop_l(uids, true /*preserveTopUid*/);
1054 
1055     updateCurrentSession_l();
1056 
1057     validateState_l();
1058 }
1059 
onUidGone(uid_t goneUid)1060 void TranscodingSessionController::onUidGone(uid_t goneUid) {
1061     ALOGD("%s: gone uid %u", __FUNCTION__, goneUid);
1062 
1063     std::list<SessionKeyType> sessionsToRemove, sessionsForOtherUids;
1064 
1065     std::scoped_lock lock{mLock};
1066 
1067     for (auto it = mSessionMap.begin(); it != mSessionMap.end(); ++it) {
1068         if (it->second.allClientUids.count(goneUid) > 0) {
1069             // If goneUid is the only uid, remove the session; otherwise, only
1070             // remove the uid from the session.
1071             if (it->second.allClientUids.size() > 1) {
1072                 sessionsForOtherUids.push_back(it->first);
1073             } else {
1074                 sessionsToRemove.push_back(it->first);
1075             }
1076         }
1077     }
1078 
1079     for (auto it = sessionsToRemove.begin(); it != sessionsToRemove.end(); ++it) {
1080         // If the session has ever been started, stop it now.
1081         // Note that stop() is needed even if the session is currently paused. This instructs
1082         // the transcoder to discard any states for the session, otherwise the states may
1083         // never be discarded.
1084         if (mSessionMap[*it].getState() != Session::NOT_STARTED) {
1085             mTranscoder->stop(it->first, it->second);
1086         }
1087 
1088         {
1089             auto clientCallback = mSessionMap[*it].callback.lock();
1090             if (clientCallback != nullptr) {
1091                 clientCallback->onTranscodingFailed(it->second,
1092                                                     TranscodingErrorCode::kUidGoneCancelled);
1093             }
1094         }
1095 
1096         // Remove the session.
1097         removeSession_l(*it, Session::CANCELED);
1098     }
1099 
1100     auto keepUid = std::make_shared<std::function<bool(uid_t)>>(
1101             [goneUid](uid_t uid) { return uid != goneUid; });
1102     for (auto it = sessionsForOtherUids.begin(); it != sessionsForOtherUids.end(); ++it) {
1103         removeSession_l(*it, Session::CANCELED, keepUid);
1104     }
1105 
1106     // Start next session.
1107     updateCurrentSession_l();
1108 
1109     validateState_l();
1110 }
1111 
onResourceAvailable()1112 void TranscodingSessionController::onResourceAvailable() {
1113     std::scoped_lock lock{mLock};
1114 
1115     if (!mResourceLost) {
1116         return;
1117     }
1118 
1119     ALOGI("%s", __FUNCTION__);
1120 
1121     mResourceLost = false;
1122     updateCurrentSession_l();
1123 
1124     validateState_l();
1125 }
1126 
onThrottlingStarted()1127 void TranscodingSessionController::onThrottlingStarted() {
1128     std::scoped_lock lock{mLock};
1129 
1130     if (mThermalThrottling) {
1131         return;
1132     }
1133 
1134     ALOGI("%s", __FUNCTION__);
1135 
1136     mThermalThrottling = true;
1137     updateCurrentSession_l();
1138 
1139     validateState_l();
1140 }
1141 
onThrottlingStopped()1142 void TranscodingSessionController::onThrottlingStopped() {
1143     std::scoped_lock lock{mLock};
1144 
1145     if (!mThermalThrottling) {
1146         return;
1147     }
1148 
1149     ALOGI("%s", __FUNCTION__);
1150 
1151     mThermalThrottling = false;
1152     updateCurrentSession_l();
1153 
1154     validateState_l();
1155 }
1156 
validateState_l()1157 void TranscodingSessionController::validateState_l() {
1158 #ifdef VALIDATE_STATE
1159     LOG_ALWAYS_FATAL_IF(mSessionQueues.count(OFFLINE_UID) != 1,
1160                         "mSessionQueues offline queue number is not 1");
1161     LOG_ALWAYS_FATAL_IF(*mOfflineUidIterator != OFFLINE_UID,
1162                         "mOfflineUidIterator not pointing to offline uid");
1163     LOG_ALWAYS_FATAL_IF(mUidSortedList.size() != mSessionQueues.size(),
1164                         "mUidSortedList and mSessionQueues size mismatch, %zu vs %zu",
1165                         mUidSortedList.size(), mSessionQueues.size());
1166 
1167     int32_t totalSessions = 0;
1168     for (auto uid : mUidSortedList) {
1169         LOG_ALWAYS_FATAL_IF(mSessionQueues.count(uid) != 1,
1170                             "mSessionQueues count for uid %d is not 1", uid);
1171         for (auto& sessionKey : mSessionQueues[uid]) {
1172             LOG_ALWAYS_FATAL_IF(mSessionMap.count(sessionKey) != 1,
1173                                 "mSessions count for session %s is not 1",
1174                                 sessionToString(sessionKey).c_str());
1175         }
1176 
1177         totalSessions += mSessionQueues[uid].size();
1178     }
1179     int32_t totalSessionsAlternative = 0;
1180     for (auto const& s : mSessionMap) {
1181         totalSessionsAlternative += s.second.allClientUids.size();
1182     }
1183     LOG_ALWAYS_FATAL_IF(totalSessions != totalSessionsAlternative,
1184                         "session count (including dup) from mSessionQueues doesn't match that from "
1185                         "mSessionMap, %d vs %d",
1186                         totalSessions, totalSessionsAlternative);
1187 #endif  // VALIDATE_STATE
1188 }
1189 
1190 }  // namespace android
1191