blob: 2518e700143be2fc79eedcd8788a7e2b36b55384 [file] [log] [blame]
Chong Zhangbc062482020-10-14 16:43:53 -07001/*
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 <inttypes.h>
23#include <media/TranscodingSessionController.h>
24#include <media/TranscodingUidPolicy.h>
25#include <utils/Log.h>
26
Chong Zhang457c6892021-02-01 15:34:20 -080027#include <thread>
Chong Zhangbc062482020-10-14 16:43:53 -070028#include <utility>
29
30namespace android {
31
32static_assert((SessionIdType)-1 < 0, "SessionIdType should be signed");
33
34constexpr static uid_t OFFLINE_UID = -1;
Chong Zhanga1320c52020-12-15 14:30:12 -080035constexpr static size_t kSessionHistoryMax = 100;
Chong Zhangbc062482020-10-14 16:43:53 -070036
37//static
38String8 TranscodingSessionController::sessionToString(const SessionKeyType& sessionKey) {
39 return String8::format("{client:%lld, session:%d}", (long long)sessionKey.first,
40 sessionKey.second);
41}
42
43//static
44const char* TranscodingSessionController::sessionStateToString(const Session::State sessionState) {
45 switch (sessionState) {
46 case Session::State::NOT_STARTED:
47 return "NOT_STARTED";
48 case Session::State::RUNNING:
49 return "RUNNING";
50 case Session::State::PAUSED:
51 return "PAUSED";
Chong Zhanga1320c52020-12-15 14:30:12 -080052 case Session::State::FINISHED:
53 return "FINISHED";
54 case Session::State::CANCELED:
55 return "CANCELED";
56 case Session::State::ERROR:
57 return "ERROR";
Chong Zhangbc062482020-10-14 16:43:53 -070058 default:
59 break;
60 }
61 return "(unknown)";
62}
63
Chong Zhang457c6892021-02-01 15:34:20 -080064///////////////////////////////////////////////////////////////////////////////
65struct TranscodingSessionController::Watchdog {
66 Watchdog(TranscodingSessionController* owner, int64_t timeoutUs);
67 ~Watchdog();
68
69 // Starts monitoring the session.
70 void start(const SessionKeyType& key);
71 // Stops monitoring the session.
72 void stop();
73 // Signals that the session is still alive. Must be sent at least every mTimeoutUs.
74 // (Timeout will happen if no ping in mTimeoutUs since the last ping.)
75 void keepAlive();
76
77private:
78 void threadLoop();
79 void updateTimer_l();
80
81 TranscodingSessionController* mOwner;
82 const int64_t mTimeoutUs;
83 mutable std::mutex mLock;
84 std::condition_variable mCondition GUARDED_BY(mLock);
85 // Whether watchdog is monitoring a session for timeout.
86 bool mActive GUARDED_BY(mLock);
87 // Whether watchdog is aborted and the monitoring thread should exit.
88 bool mAbort GUARDED_BY(mLock);
89 // When watchdog is active, the next timeout time point.
Chong Zhang87d199c2021-03-01 19:02:18 -080090 std::chrono::steady_clock::time_point mNextTimeoutTime GUARDED_BY(mLock);
Chong Zhang457c6892021-02-01 15:34:20 -080091 // When watchdog is active, the session being watched.
92 SessionKeyType mSessionToWatch GUARDED_BY(mLock);
93 std::thread mThread;
94};
95
Chong Zhang457c6892021-02-01 15:34:20 -080096TranscodingSessionController::Watchdog::Watchdog(TranscodingSessionController* owner,
97 int64_t timeoutUs)
98 : mOwner(owner),
99 mTimeoutUs(timeoutUs),
100 mActive(false),
101 mAbort(false),
102 mThread(&Watchdog::threadLoop, this) {
103 ALOGV("Watchdog CTOR: %p", this);
104}
105
106TranscodingSessionController::Watchdog::~Watchdog() {
107 ALOGV("Watchdog DTOR: %p", this);
108
109 {
110 // Exit the looper thread.
111 std::scoped_lock lock{mLock};
112
113 mAbort = true;
114 mCondition.notify_one();
115 }
116
117 mThread.join();
118 ALOGV("Watchdog DTOR: %p, done.", this);
119}
120
121void TranscodingSessionController::Watchdog::start(const SessionKeyType& key) {
122 std::scoped_lock lock{mLock};
123
124 if (!mActive) {
125 ALOGI("Watchdog start: %s", sessionToString(key).c_str());
126
127 mActive = true;
128 mSessionToWatch = key;
129 updateTimer_l();
130 mCondition.notify_one();
131 }
132}
133
134void TranscodingSessionController::Watchdog::stop() {
135 std::scoped_lock lock{mLock};
136
137 if (mActive) {
138 ALOGI("Watchdog stop: %s", sessionToString(mSessionToWatch).c_str());
139
140 mActive = false;
141 mCondition.notify_one();
142 }
143}
144
145void TranscodingSessionController::Watchdog::keepAlive() {
146 std::scoped_lock lock{mLock};
147
148 if (mActive) {
149 ALOGI("Watchdog keepAlive: %s", sessionToString(mSessionToWatch).c_str());
150
151 updateTimer_l();
152 mCondition.notify_one();
153 }
154}
155
156// updateTimer_l() is only called with lock held.
157void TranscodingSessionController::Watchdog::updateTimer_l() NO_THREAD_SAFETY_ANALYSIS {
158 std::chrono::microseconds timeout(mTimeoutUs);
Chong Zhang87d199c2021-03-01 19:02:18 -0800159 mNextTimeoutTime = std::chrono::steady_clock::now() + timeout;
Chong Zhang457c6892021-02-01 15:34:20 -0800160}
161
162// Unfortunately std::unique_lock is incompatible with -Wthread-safety.
163void TranscodingSessionController::Watchdog::threadLoop() NO_THREAD_SAFETY_ANALYSIS {
164 std::unique_lock<std::mutex> lock{mLock};
165
166 while (!mAbort) {
167 if (!mActive) {
168 mCondition.wait(lock);
169 continue;
170 }
171 // Watchdog active, wait till next timeout time.
172 if (mCondition.wait_until(lock, mNextTimeoutTime) == std::cv_status::timeout) {
173 // If timeout happens, report timeout and deactivate watchdog.
174 mActive = false;
175 // Make a copy of session key, as once we unlock, it could be unprotected.
176 SessionKeyType sessionKey = mSessionToWatch;
177
178 ALOGE("Watchdog timeout: %s", sessionToString(sessionKey).c_str());
179
180 lock.unlock();
181 mOwner->onError(sessionKey.first, sessionKey.second,
182 TranscodingErrorCode::kWatchdogTimeout);
183 lock.lock();
184 }
185 }
186}
187///////////////////////////////////////////////////////////////////////////////
Chong Zhang87d199c2021-03-01 19:02:18 -0800188struct TranscodingSessionController::Pacer {
189 Pacer(const ControllerConfig& config)
190 : mBurstThresholdMs(config.pacerBurstThresholdMs),
191 mBurstCountQuota(config.pacerBurstCountQuota),
192 mBurstTimeQuotaSec(config.pacerBurstTimeQuotaSeconds) {}
193
194 ~Pacer() = default;
195
Chong Zhang87d199c2021-03-01 19:02:18 -0800196 bool onSessionStarted(uid_t uid);
Chong Zhangebd86d32021-03-29 11:30:56 -0700197 void onSessionCompleted(uid_t uid, std::chrono::microseconds runningTime);
Chong Zhang87d199c2021-03-01 19:02:18 -0800198
199private:
200 // Threshold of time between finish/start below which a back-to-back start is counted.
201 int32_t mBurstThresholdMs;
202 // Maximum allowed back-to-back start count.
203 int32_t mBurstCountQuota;
204 // Maximum allowed back-to-back running time.
205 int32_t mBurstTimeQuotaSec;
206
207 struct UidHistoryEntry {
Chong Zhangebd86d32021-03-29 11:30:56 -0700208 bool sessionActive = false;
Chong Zhang87d199c2021-03-01 19:02:18 -0800209 int32_t burstCount = 0;
210 std::chrono::steady_clock::duration burstDuration{0};
Chong Zhangebd86d32021-03-29 11:30:56 -0700211 std::chrono::steady_clock::time_point lastCompletedTime;
Chong Zhang87d199c2021-03-01 19:02:18 -0800212 };
213 std::map<uid_t, UidHistoryEntry> mUidHistoryMap;
214};
215
Chong Zhangebd86d32021-03-29 11:30:56 -0700216bool TranscodingSessionController::Pacer::onSessionStarted(uid_t uid) {
217 // If uid doesn't exist, only insert the entry and mark session active. Skip quota checking.
Chong Zhang87d199c2021-03-01 19:02:18 -0800218 if (mUidHistoryMap.find(uid) == mUidHistoryMap.end()) {
219 mUidHistoryMap.emplace(uid, UidHistoryEntry{});
Chong Zhangebd86d32021-03-29 11:30:56 -0700220 mUidHistoryMap[uid].sessionActive = true;
221 ALOGV("Pacer::onSessionStarted: uid %d: new", uid);
Chong Zhang87d199c2021-03-01 19:02:18 -0800222 return true;
223 }
224
225 // TODO: if Thermal throttling or resoure lost happened to occurr between this start
226 // and the previous completion, we should deduct the paused time from the elapsed time.
227 // (Individual session's pause time, on the other hand, doesn't need to be deducted
228 // because it doesn't affect the gap between last completion and the start.
229 auto timeSinceLastComplete =
230 std::chrono::steady_clock::now() - mUidHistoryMap[uid].lastCompletedTime;
231 if (mUidHistoryMap[uid].burstCount >= mBurstCountQuota &&
232 mUidHistoryMap[uid].burstDuration >= std::chrono::seconds(mBurstTimeQuotaSec)) {
Chong Zhangebd86d32021-03-29 11:30:56 -0700233 ALOGW("Pacer::onSessionStarted: uid %d: over quota, burst count %d, time %lldms", uid,
234 mUidHistoryMap[uid].burstCount,
235 (long long)mUidHistoryMap[uid].burstDuration.count() / 1000000);
Chong Zhang87d199c2021-03-01 19:02:18 -0800236 return false;
237 }
238
239 // If not over quota, allow the session, and reset as long as this is not too close
240 // to previous completion.
241 if (timeSinceLastComplete > std::chrono::milliseconds(mBurstThresholdMs)) {
Chong Zhangebd86d32021-03-29 11:30:56 -0700242 ALOGV("Pacer::onSessionStarted: uid %d: reset quota", uid);
Chong Zhang87d199c2021-03-01 19:02:18 -0800243 mUidHistoryMap[uid].burstCount = 0;
244 mUidHistoryMap[uid].burstDuration = std::chrono::milliseconds(0);
245 } else {
Chong Zhangebd86d32021-03-29 11:30:56 -0700246 ALOGV("Pacer::onSessionStarted: uid %d: burst count %d, time %lldms", uid,
247 mUidHistoryMap[uid].burstCount,
248 (long long)mUidHistoryMap[uid].burstDuration.count() / 1000000);
Chong Zhang87d199c2021-03-01 19:02:18 -0800249 }
250
Chong Zhangebd86d32021-03-29 11:30:56 -0700251 mUidHistoryMap[uid].sessionActive = true;
Chong Zhang87d199c2021-03-01 19:02:18 -0800252 return true;
253}
254
Chong Zhangebd86d32021-03-29 11:30:56 -0700255void TranscodingSessionController::Pacer::onSessionCompleted(
256 uid_t uid, std::chrono::microseconds runningTime) {
257 // Skip quota update if this uid missed the start. (Could happen if the uid is added via
258 // addClientUid() after the session start.)
259 if (mUidHistoryMap.find(uid) == mUidHistoryMap.end() || !mUidHistoryMap[uid].sessionActive) {
260 ALOGV("Pacer::onSessionCompleted: uid %d: not started", uid);
261 return;
262 }
263 ALOGV("Pacer::onSessionCompleted: uid %d: runningTime %lld", uid, runningTime.count() / 1000);
264 mUidHistoryMap[uid].sessionActive = false;
265 mUidHistoryMap[uid].burstCount++;
266 mUidHistoryMap[uid].burstDuration += runningTime;
267 mUidHistoryMap[uid].lastCompletedTime = std::chrono::steady_clock::now();
268}
269
Chong Zhang87d199c2021-03-01 19:02:18 -0800270///////////////////////////////////////////////////////////////////////////////
Chong Zhang457c6892021-02-01 15:34:20 -0800271
Chong Zhangbc062482020-10-14 16:43:53 -0700272TranscodingSessionController::TranscodingSessionController(
Chong Zhang457c6892021-02-01 15:34:20 -0800273 const TranscoderFactoryType& transcoderFactory,
Chong Zhangbc062482020-10-14 16:43:53 -0700274 const std::shared_ptr<UidPolicyInterface>& uidPolicy,
Chong Zhang8677f1f2021-01-21 20:37:35 +0000275 const std::shared_ptr<ResourcePolicyInterface>& resourcePolicy,
Chong Zhang87d199c2021-03-01 19:02:18 -0800276 const std::shared_ptr<ThermalPolicyInterface>& thermalPolicy,
277 const ControllerConfig* config)
Chong Zhang457c6892021-02-01 15:34:20 -0800278 : mTranscoderFactory(transcoderFactory),
Chong Zhangbc062482020-10-14 16:43:53 -0700279 mUidPolicy(uidPolicy),
280 mResourcePolicy(resourcePolicy),
Chong Zhang8677f1f2021-01-21 20:37:35 +0000281 mThermalPolicy(thermalPolicy),
Chong Zhangbc062482020-10-14 16:43:53 -0700282 mCurrentSession(nullptr),
283 mResourceLost(false) {
284 // Only push empty offline queue initially. Realtime queues are added when requests come in.
285 mUidSortedList.push_back(OFFLINE_UID);
286 mOfflineUidIterator = mUidSortedList.begin();
287 mSessionQueues.emplace(OFFLINE_UID, SessionQueueType());
Chong Zhangcf3f8ee2020-10-29 18:38:37 -0700288 mUidPackageNames[OFFLINE_UID] = "(offline)";
Chong Zhang8677f1f2021-01-21 20:37:35 +0000289 mThermalThrottling = thermalPolicy->getThrottlingStatus();
Chong Zhang87d199c2021-03-01 19:02:18 -0800290 if (config != nullptr) {
291 mConfig = *config;
292 }
293 mPacer.reset(new Pacer(mConfig));
294 ALOGD("@@@ watchdog %lld, burst count %d, burst time %d, burst threshold %d",
295 (long long)mConfig.watchdogTimeoutUs, mConfig.pacerBurstCountQuota,
296 mConfig.pacerBurstTimeQuotaSeconds, mConfig.pacerBurstThresholdMs);
Chong Zhangbc062482020-10-14 16:43:53 -0700297}
298
299TranscodingSessionController::~TranscodingSessionController() {}
300
Chong Zhanga1320c52020-12-15 14:30:12 -0800301void TranscodingSessionController::dumpSession_l(const Session& session, String8& result,
302 bool closedSession) {
303 const size_t SIZE = 256;
304 char buffer[SIZE];
305 const TranscodingRequestParcel& request = session.request;
306 snprintf(buffer, SIZE, " Session: %s, %s, %d%%\n", sessionToString(session.key).c_str(),
307 sessionStateToString(session.getState()), session.lastProgress);
308 result.append(buffer);
309 snprintf(buffer, SIZE, " pkg: %s\n", request.clientPackageName.c_str());
310 result.append(buffer);
311 snprintf(buffer, SIZE, " src: %s\n", request.sourceFilePath.c_str());
312 result.append(buffer);
313 snprintf(buffer, SIZE, " dst: %s\n", request.destinationFilePath.c_str());
314 result.append(buffer);
315
316 if (closedSession) {
317 snprintf(buffer, SIZE,
318 " waiting: %.1fs, running: %.1fs, paused: %.1fs, paused count: %d\n",
319 session.waitingTime.count() / 1000000.0f, session.runningTime.count() / 1000000.0f,
320 session.pausedTime.count() / 1000000.0f, session.pauseCount);
321 result.append(buffer);
322 }
323}
324
Chong Zhangbc062482020-10-14 16:43:53 -0700325void TranscodingSessionController::dumpAllSessions(int fd, const Vector<String16>& args __unused) {
326 String8 result;
327
328 const size_t SIZE = 256;
329 char buffer[SIZE];
330 std::scoped_lock lock{mLock};
331
Chong Zhanga1320c52020-12-15 14:30:12 -0800332 snprintf(buffer, SIZE, "\n========== Dumping live sessions queues =========\n");
Chong Zhangbc062482020-10-14 16:43:53 -0700333 result.append(buffer);
334 snprintf(buffer, SIZE, " Total num of Sessions: %zu\n", mSessionMap.size());
335 result.append(buffer);
336
337 std::vector<int32_t> uids(mUidSortedList.begin(), mUidSortedList.end());
Chong Zhangbc062482020-10-14 16:43:53 -0700338
339 for (int32_t i = 0; i < uids.size(); i++) {
340 const uid_t uid = uids[i];
341
342 if (mSessionQueues[uid].empty()) {
343 continue;
344 }
Chong Zhanga1320c52020-12-15 14:30:12 -0800345 snprintf(buffer, SIZE, " uid: %d, pkg: %s\n", uid,
Chong Zhangcf3f8ee2020-10-29 18:38:37 -0700346 mUidPackageNames.count(uid) > 0 ? mUidPackageNames[uid].c_str() : "(unknown)");
Chong Zhangbc062482020-10-14 16:43:53 -0700347 result.append(buffer);
348 snprintf(buffer, SIZE, " Num of sessions: %zu\n", mSessionQueues[uid].size());
349 result.append(buffer);
350 for (auto& sessionKey : mSessionQueues[uid]) {
351 auto sessionIt = mSessionMap.find(sessionKey);
352 if (sessionIt == mSessionMap.end()) {
353 snprintf(buffer, SIZE, "Failed to look up Session %s \n",
354 sessionToString(sessionKey).c_str());
355 result.append(buffer);
356 continue;
357 }
Chong Zhanga1320c52020-12-15 14:30:12 -0800358 dumpSession_l(sessionIt->second, result);
Chong Zhangbc062482020-10-14 16:43:53 -0700359 }
360 }
361
Chong Zhanga1320c52020-12-15 14:30:12 -0800362 snprintf(buffer, SIZE, "\n========== Dumping past sessions =========\n");
363 result.append(buffer);
Chong Zhang8677f1f2021-01-21 20:37:35 +0000364 for (auto& session : mSessionHistory) {
Chong Zhanga1320c52020-12-15 14:30:12 -0800365 dumpSession_l(session, result, true /*closedSession*/);
366 }
367
Chong Zhangbc062482020-10-14 16:43:53 -0700368 write(fd, result.string(), result.size());
369}
370
Chong Zhang87d199c2021-03-01 19:02:18 -0800371/*
372 * Returns nullptr if there is no session, or we're paused globally (due to resource lost,
373 * thermal throttling, etc.). Otherwise, return the session that should be run next.
374 */
Chong Zhangbc062482020-10-14 16:43:53 -0700375TranscodingSessionController::Session* TranscodingSessionController::getTopSession_l() {
376 if (mSessionMap.empty()) {
377 return nullptr;
378 }
Chong Zhang87d199c2021-03-01 19:02:18 -0800379
380 // Return nullptr if we're paused globally due to resource lost or thermal throttling.
381 if (((mResourcePolicy != nullptr && mResourceLost) ||
382 (mThermalPolicy != nullptr && mThermalThrottling))) {
383 return nullptr;
384 }
385
Chong Zhangbc062482020-10-14 16:43:53 -0700386 uid_t topUid = *mUidSortedList.begin();
Chong Zhangebd86d32021-03-29 11:30:56 -0700387 // If the current session is running, and it's in the topUid's queue, let it continue
388 // to run even if it's not the earliest in that uid's queue.
389 // For example, uid(B) is added to a session while it's pending in uid(A)'s queue, then
390 // B is brought to front which caused the session to run, then user switches back to A.
391 if (mCurrentSession != nullptr && mCurrentSession->getState() == Session::RUNNING &&
392 mCurrentSession->allClientUids.count(topUid) > 0) {
393 return mCurrentSession;
394 }
Chong Zhangbc062482020-10-14 16:43:53 -0700395 SessionKeyType topSessionKey = *mSessionQueues[topUid].begin();
396 return &mSessionMap[topSessionKey];
397}
398
Chong Zhang457c6892021-02-01 15:34:20 -0800399void TranscodingSessionController::setSessionState_l(Session* session, Session::State state) {
400 bool wasRunning = (session->getState() == Session::RUNNING);
401 session->setState(state);
402 bool isRunning = (session->getState() == Session::RUNNING);
403
404 if (wasRunning == isRunning) {
405 return;
406 }
407
408 // Currently we only have 1 running session, and we always put the previous
409 // session in non-running state before we run the new session, so it's okay
410 // to start/stop the watchdog here. If this assumption changes, we need to
411 // track the number of running sessions and start/stop watchdog based on that.
412 if (isRunning) {
413 mWatchdog->start(session->key);
414 } else {
415 mWatchdog->stop();
416 }
417}
418
Chong Zhanga1320c52020-12-15 14:30:12 -0800419void TranscodingSessionController::Session::setState(Session::State newState) {
420 if (state == newState) {
421 return;
422 }
Chong Zhang87d199c2021-03-01 19:02:18 -0800423 auto nowTime = std::chrono::steady_clock::now();
Chong Zhanga1320c52020-12-15 14:30:12 -0800424 if (state != INVALID) {
Chong Zhang87d199c2021-03-01 19:02:18 -0800425 std::chrono::microseconds elapsedTime =
426 std::chrono::duration_cast<std::chrono::microseconds>(nowTime - stateEnterTime);
Chong Zhanga1320c52020-12-15 14:30:12 -0800427 switch (state) {
428 case PAUSED:
429 pausedTime = pausedTime + elapsedTime;
430 break;
431 case RUNNING:
432 runningTime = runningTime + elapsedTime;
433 break;
434 case NOT_STARTED:
435 waitingTime = waitingTime + elapsedTime;
436 break;
437 default:
438 break;
439 }
440 }
441 if (newState == PAUSED) {
442 pauseCount++;
443 }
444 stateEnterTime = nowTime;
445 state = newState;
446}
447
Chong Zhangbc062482020-10-14 16:43:53 -0700448void TranscodingSessionController::updateCurrentSession_l() {
Chong Zhangbc062482020-10-14 16:43:53 -0700449 Session* curSession = mCurrentSession;
Chong Zhangebd86d32021-03-29 11:30:56 -0700450 Session* topSession = nullptr;
Chong Zhangbc062482020-10-14 16:43:53 -0700451
Chong Zhang87d199c2021-03-01 19:02:18 -0800452 // Delayed init of transcoder and watchdog.
453 if (mTranscoder == nullptr) {
454 mTranscoder = mTranscoderFactory(shared_from_this());
455 mWatchdog = std::make_shared<Watchdog>(this, mConfig.watchdogTimeoutUs);
Chong Zhang8677f1f2021-01-21 20:37:35 +0000456 }
457
Chong Zhang87d199c2021-03-01 19:02:18 -0800458 // If we found a different top session, or the top session's running state is not
459 // correct. Take some actions to ensure it's correct.
460 while ((topSession = getTopSession_l()) != curSession ||
461 (topSession != nullptr && !topSession->isRunning())) {
462 ALOGV("updateCurrentSession_l: topSession is %s, curSession is %s",
463 topSession == nullptr ? "null" : sessionToString(topSession->key).c_str(),
464 curSession == nullptr ? "null" : sessionToString(curSession->key).c_str());
Chong Zhang457c6892021-02-01 15:34:20 -0800465
Chong Zhang87d199c2021-03-01 19:02:18 -0800466 // If current session is running, pause it first. Note this is needed for either
467 // cases: 1) Top session is changing to another session, or 2) Top session is
468 // changing to null (which means we should be globally paused).
Chong Zhanga1320c52020-12-15 14:30:12 -0800469 if (curSession != nullptr && curSession->getState() == Session::RUNNING) {
Chong Zhangbc062482020-10-14 16:43:53 -0700470 mTranscoder->pause(curSession->key.first, curSession->key.second);
Chong Zhang457c6892021-02-01 15:34:20 -0800471 setSessionState_l(curSession, Session::PAUSED);
Chong Zhangbc062482020-10-14 16:43:53 -0700472 }
Chong Zhang87d199c2021-03-01 19:02:18 -0800473
474 if (topSession == nullptr) {
475 // Nothing more to run (either no session or globally paused).
476 break;
477 }
478
479 // Otherwise, ensure topSession is running.
480 if (topSession->getState() == Session::NOT_STARTED) {
Chong Zhangebd86d32021-03-29 11:30:56 -0700481 // Check if at least one client has quota to start the session.
482 bool keepForClient = false;
483 for (uid_t uid : topSession->allClientUids) {
484 if (mPacer->onSessionStarted(uid)) {
485 keepForClient = true;
486 // DO NOT break here, because book-keeping still needs to happen
487 // for the other uids.
488 }
489 }
490 if (!keepForClient) {
491 // Unfortunately all uids requesting this session are out of quota.
492 // Drop this session and try the next one.
Chong Zhang87d199c2021-03-01 19:02:18 -0800493 {
494 auto clientCallback = mSessionMap[topSession->key].callback.lock();
495 if (clientCallback != nullptr) {
496 clientCallback->onTranscodingFailed(
497 topSession->key.second, TranscodingErrorCode::kDroppedByService);
498 }
499 }
500 removeSession_l(topSession->key, Session::DROPPED_BY_PACER);
501 continue;
Chong Zhangbc062482020-10-14 16:43:53 -0700502 }
Chong Zhang87d199c2021-03-01 19:02:18 -0800503 mTranscoder->start(topSession->key.first, topSession->key.second, topSession->request,
504 topSession->callingUid, topSession->callback.lock());
505 setSessionState_l(topSession, Session::RUNNING);
506 } else if (topSession->getState() == Session::PAUSED) {
507 mTranscoder->resume(topSession->key.first, topSession->key.second, topSession->request,
508 topSession->callingUid, topSession->callback.lock());
Chong Zhang457c6892021-02-01 15:34:20 -0800509 setSessionState_l(topSession, Session::RUNNING);
Chong Zhangbc062482020-10-14 16:43:53 -0700510 }
Chong Zhang87d199c2021-03-01 19:02:18 -0800511 break;
Chong Zhangbc062482020-10-14 16:43:53 -0700512 }
513 mCurrentSession = topSession;
514}
515
Chong Zhangebd86d32021-03-29 11:30:56 -0700516void TranscodingSessionController::addUidToSession_l(uid_t clientUid,
517 const SessionKeyType& sessionKey) {
518 // If it's an offline session, the queue was already added in constructor.
519 // If it's a real-time sessions, check if a queue is already present for the uid,
520 // and add a new queue if needed.
521 if (clientUid != OFFLINE_UID) {
522 if (mSessionQueues.count(clientUid) == 0) {
523 mUidPolicy->registerMonitorUid(clientUid);
524 if (mUidPolicy->isUidOnTop(clientUid)) {
525 mUidSortedList.push_front(clientUid);
526 } else {
527 // Shouldn't be submitting real-time requests from non-top app,
528 // put it in front of the offline queue.
529 mUidSortedList.insert(mOfflineUidIterator, clientUid);
530 }
531 } else if (clientUid != *mUidSortedList.begin()) {
532 if (mUidPolicy->isUidOnTop(clientUid)) {
533 mUidSortedList.remove(clientUid);
534 mUidSortedList.push_front(clientUid);
535 }
536 }
537 }
538 // Append this session to the uid's queue.
539 mSessionQueues[clientUid].push_back(sessionKey);
540}
541
Chong Zhanga1320c52020-12-15 14:30:12 -0800542void TranscodingSessionController::removeSession_l(const SessionKeyType& sessionKey,
Chong Zhangebd86d32021-03-29 11:30:56 -0700543 Session::State finalState, bool keepForOffline) {
Chong Zhangbc062482020-10-14 16:43:53 -0700544 ALOGV("%s: session %s", __FUNCTION__, sessionToString(sessionKey).c_str());
545
546 if (mSessionMap.count(sessionKey) == 0) {
547 ALOGE("session %s doesn't exist", sessionToString(sessionKey).c_str());
548 return;
549 }
550
551 // Remove session from uid's queue.
Chong Zhangebd86d32021-03-29 11:30:56 -0700552 bool uidQueueRemoved = false;
553 for (uid_t uid : mSessionMap[sessionKey].allClientUids) {
554 if (keepForOffline && uid == OFFLINE_UID) {
555 continue;
556 }
557 SessionQueueType& sessionQueue = mSessionQueues[uid];
558 auto it = std::find(sessionQueue.begin(), sessionQueue.end(), sessionKey);
559 if (it == sessionQueue.end()) {
560 ALOGW("couldn't find session %s in queue for uid %d",
561 sessionToString(sessionKey).c_str(), uid);
562 continue;
563 }
564 sessionQueue.erase(it);
565
566 // If this is the last session in a real-time queue, remove this uid's queue.
567 if (uid != OFFLINE_UID && sessionQueue.empty()) {
568 mUidSortedList.remove(uid);
569 mSessionQueues.erase(uid);
570 mUidPolicy->unregisterMonitorUid(uid);
571
572 uidQueueRemoved = true;
573 }
Chong Zhangbc062482020-10-14 16:43:53 -0700574 }
Chong Zhangbc062482020-10-14 16:43:53 -0700575
Chong Zhangebd86d32021-03-29 11:30:56 -0700576 if (uidQueueRemoved) {
Chong Zhangbc062482020-10-14 16:43:53 -0700577 std::unordered_set<uid_t> topUids = mUidPolicy->getTopUids();
578 moveUidsToTop_l(topUids, false /*preserveTopUid*/);
579 }
580
Chong Zhangebd86d32021-03-29 11:30:56 -0700581 if (keepForOffline) {
582 mSessionMap[sessionKey].allClientUids = {OFFLINE_UID};
583 return;
584 }
585
Chong Zhangbc062482020-10-14 16:43:53 -0700586 // Clear current session.
587 if (mCurrentSession == &mSessionMap[sessionKey]) {
588 mCurrentSession = nullptr;
589 }
590
Chong Zhang457c6892021-02-01 15:34:20 -0800591 setSessionState_l(&mSessionMap[sessionKey], finalState);
Chong Zhang87d199c2021-03-01 19:02:18 -0800592
593 if (finalState == Session::FINISHED || finalState == Session::ERROR) {
Chong Zhangebd86d32021-03-29 11:30:56 -0700594 for (uid_t uid : mSessionMap[sessionKey].allClientUids) {
595 mPacer->onSessionCompleted(uid, mSessionMap[sessionKey].runningTime);
596 }
Chong Zhang87d199c2021-03-01 19:02:18 -0800597 }
598
Chong Zhanga1320c52020-12-15 14:30:12 -0800599 mSessionHistory.push_back(mSessionMap[sessionKey]);
600 if (mSessionHistory.size() > kSessionHistoryMax) {
601 mSessionHistory.erase(mSessionHistory.begin());
602 }
603
Chong Zhangbc062482020-10-14 16:43:53 -0700604 // Remove session from session map.
605 mSessionMap.erase(sessionKey);
606}
607
608/**
609 * Moves the set of uids to the front of mUidSortedList (which is used to pick
610 * the next session to run).
611 *
612 * This is called when 1) we received a onTopUidsChanged() callback from UidPolicy,
613 * or 2) we removed the session queue for a uid because it becomes empty.
614 *
615 * In case of 1), if there are multiple uids in the set, and the current front
616 * uid in mUidSortedList is still in the set, we try to keep that uid at front
617 * so that current session run is not interrupted. (This is not a concern for case 2)
618 * because the queue for a uid was just removed entirely.)
619 */
620void TranscodingSessionController::moveUidsToTop_l(const std::unordered_set<uid_t>& uids,
621 bool preserveTopUid) {
622 // If uid set is empty, nothing to do. Do not change the queue status.
623 if (uids.empty()) {
624 return;
625 }
626
627 // Save the current top uid.
628 uid_t curTopUid = *mUidSortedList.begin();
629 bool pushCurTopToFront = false;
630 int32_t numUidsMoved = 0;
631
632 // Go through the sorted uid list once, and move the ones in top set to front.
633 for (auto it = mUidSortedList.begin(); it != mUidSortedList.end();) {
634 uid_t uid = *it;
635
636 if (uid != OFFLINE_UID && uids.count(uid) > 0) {
637 it = mUidSortedList.erase(it);
638
639 // If this is the top we're preserving, don't push it here, push
640 // it after the for-loop.
641 if (uid == curTopUid && preserveTopUid) {
642 pushCurTopToFront = true;
643 } else {
644 mUidSortedList.push_front(uid);
645 }
646
647 // If we found all uids in the set, break out.
648 if (++numUidsMoved == uids.size()) {
649 break;
650 }
651 } else {
652 ++it;
653 }
654 }
655
656 if (pushCurTopToFront) {
657 mUidSortedList.push_front(curTopUid);
658 }
659}
660
661bool TranscodingSessionController::submit(
Linus Nilssona99f4042021-02-25 15:49:43 -0800662 ClientIdType clientId, SessionIdType sessionId, uid_t callingUid, uid_t clientUid,
Chong Zhangbc062482020-10-14 16:43:53 -0700663 const TranscodingRequestParcel& request,
664 const std::weak_ptr<ITranscodingClientCallback>& callback) {
665 SessionKeyType sessionKey = std::make_pair(clientId, sessionId);
666
667 ALOGV("%s: session %s, uid %d, prioirty %d", __FUNCTION__, sessionToString(sessionKey).c_str(),
Linus Nilssona99f4042021-02-25 15:49:43 -0800668 clientUid, (int32_t)request.priority);
Chong Zhangbc062482020-10-14 16:43:53 -0700669
670 std::scoped_lock lock{mLock};
671
672 if (mSessionMap.count(sessionKey) > 0) {
673 ALOGE("session %s already exists", sessionToString(sessionKey).c_str());
674 return false;
675 }
676
Chong Zhangcf3f8ee2020-10-29 18:38:37 -0700677 // Add the uid package name to the store of package names we already know.
Linus Nilssona99f4042021-02-25 15:49:43 -0800678 if (mUidPackageNames.count(clientUid) == 0) {
679 mUidPackageNames.emplace(clientUid, request.clientPackageName);
Chong Zhangcf3f8ee2020-10-29 18:38:37 -0700680 }
681
Chong Zhangbc062482020-10-14 16:43:53 -0700682 // TODO(chz): only support offline vs real-time for now. All kUnspecified sessions
683 // go to offline queue.
684 if (request.priority == TranscodingSessionPriority::kUnspecified) {
Linus Nilssona99f4042021-02-25 15:49:43 -0800685 clientUid = OFFLINE_UID;
Chong Zhangbc062482020-10-14 16:43:53 -0700686 }
687
688 // Add session to session map.
689 mSessionMap[sessionKey].key = sessionKey;
Linus Nilssona99f4042021-02-25 15:49:43 -0800690 mSessionMap[sessionKey].callingUid = callingUid;
Chong Zhangebd86d32021-03-29 11:30:56 -0700691 mSessionMap[sessionKey].allClientUids.insert(clientUid);
Chong Zhangbc062482020-10-14 16:43:53 -0700692 mSessionMap[sessionKey].request = request;
693 mSessionMap[sessionKey].callback = callback;
Chong Zhang457c6892021-02-01 15:34:20 -0800694 setSessionState_l(&mSessionMap[sessionKey], Session::NOT_STARTED);
Chong Zhangbc062482020-10-14 16:43:53 -0700695
Chong Zhangebd86d32021-03-29 11:30:56 -0700696 addUidToSession_l(clientUid, sessionKey);
Chong Zhangbc062482020-10-14 16:43:53 -0700697
698 updateCurrentSession_l();
699
700 validateState_l();
701 return true;
702}
703
704bool TranscodingSessionController::cancel(ClientIdType clientId, SessionIdType sessionId) {
705 SessionKeyType sessionKey = std::make_pair(clientId, sessionId);
706
707 ALOGV("%s: session %s", __FUNCTION__, sessionToString(sessionKey).c_str());
708
Chong Zhangebd86d32021-03-29 11:30:56 -0700709 std::list<SessionKeyType> sessionsToRemove, sessionsForOffline;
Chong Zhangbc062482020-10-14 16:43:53 -0700710
711 std::scoped_lock lock{mLock};
712
713 if (sessionId < 0) {
714 for (auto it = mSessionMap.begin(); it != mSessionMap.end(); ++it) {
Chong Zhangebd86d32021-03-29 11:30:56 -0700715 if (it->first.first == clientId) {
716 // If there is offline request, only keep the offline client;
717 // otherwise remove the session.
718 if (it->second.allClientUids.count(OFFLINE_UID) > 0) {
719 sessionsForOffline.push_back(it->first);
720 } else {
721 sessionsToRemove.push_back(it->first);
722 }
Chong Zhangbc062482020-10-14 16:43:53 -0700723 }
724 }
725 } else {
726 if (mSessionMap.count(sessionKey) == 0) {
727 ALOGE("session %s doesn't exist", sessionToString(sessionKey).c_str());
728 return false;
729 }
730 sessionsToRemove.push_back(sessionKey);
731 }
732
733 for (auto it = sessionsToRemove.begin(); it != sessionsToRemove.end(); ++it) {
734 // If the session has ever been started, stop it now.
735 // Note that stop() is needed even if the session is currently paused. This instructs
736 // the transcoder to discard any states for the session, otherwise the states may
737 // never be discarded.
Chong Zhanga1320c52020-12-15 14:30:12 -0800738 if (mSessionMap[*it].getState() != Session::NOT_STARTED) {
Chong Zhangbc062482020-10-14 16:43:53 -0700739 mTranscoder->stop(it->first, it->second);
740 }
741
742 // Remove the session.
Chong Zhanga1320c52020-12-15 14:30:12 -0800743 removeSession_l(*it, Session::CANCELED);
Chong Zhangbc062482020-10-14 16:43:53 -0700744 }
745
Chong Zhangebd86d32021-03-29 11:30:56 -0700746 for (auto it = sessionsForOffline.begin(); it != sessionsForOffline.end(); ++it) {
747 removeSession_l(*it, Session::CANCELED, true /*keepForOffline*/);
748 }
749
Chong Zhangbc062482020-10-14 16:43:53 -0700750 // Start next session.
751 updateCurrentSession_l();
752
753 validateState_l();
754 return true;
755}
756
Chong Zhangebd86d32021-03-29 11:30:56 -0700757bool TranscodingSessionController::addClientUid(ClientIdType clientId, SessionIdType sessionId,
758 uid_t clientUid) {
759 SessionKeyType sessionKey = std::make_pair(clientId, sessionId);
760
761 std::scoped_lock lock{mLock};
762
763 if (mSessionMap.count(sessionKey) == 0) {
764 ALOGE("session %s doesn't exist", sessionToString(sessionKey).c_str());
765 return false;
766 }
767
768 if (mSessionMap[sessionKey].allClientUids.count(clientUid) > 0) {
769 ALOGE("session %s already has uid %d", sessionToString(sessionKey).c_str(), clientUid);
770 return false;
771 }
772
773 mSessionMap[sessionKey].allClientUids.insert(clientUid);
774 addUidToSession_l(clientUid, sessionKey);
775
776 updateCurrentSession_l();
777
778 validateState_l();
779 return true;
780}
781
782bool TranscodingSessionController::getClientUids(ClientIdType clientId, SessionIdType sessionId,
783 std::vector<int32_t>* out_clientUids) {
784 SessionKeyType sessionKey = std::make_pair(clientId, sessionId);
785
786 std::scoped_lock lock{mLock};
787
788 if (mSessionMap.count(sessionKey) == 0) {
789 ALOGE("session %s doesn't exist", sessionToString(sessionKey).c_str());
790 return false;
791 }
792
793 out_clientUids->clear();
794 for (uid_t uid : mSessionMap[sessionKey].allClientUids) {
795 if (uid != OFFLINE_UID) {
796 out_clientUids->push_back(uid);
797 }
798 }
799 return true;
800}
801
Chong Zhangbc062482020-10-14 16:43:53 -0700802bool TranscodingSessionController::getSession(ClientIdType clientId, SessionIdType sessionId,
803 TranscodingRequestParcel* request) {
804 SessionKeyType sessionKey = std::make_pair(clientId, sessionId);
805
806 std::scoped_lock lock{mLock};
807
808 if (mSessionMap.count(sessionKey) == 0) {
809 ALOGE("session %s doesn't exist", sessionToString(sessionKey).c_str());
810 return false;
811 }
812
813 *(TranscodingRequest*)request = mSessionMap[sessionKey].request;
814 return true;
815}
816
817void TranscodingSessionController::notifyClient(ClientIdType clientId, SessionIdType sessionId,
818 const char* reason,
819 std::function<void(const SessionKeyType&)> func) {
820 SessionKeyType sessionKey = std::make_pair(clientId, sessionId);
821
822 std::scoped_lock lock{mLock};
823
824 if (mSessionMap.count(sessionKey) == 0) {
825 ALOGW("%s: ignoring %s for session %s that doesn't exist", __FUNCTION__, reason,
826 sessionToString(sessionKey).c_str());
827 return;
828 }
829
830 // Only ignore if session was never started. In particular, propagate the status
831 // to client if the session is paused. Transcoder could have posted finish when
832 // we're pausing it, and the finish arrived after we changed current session.
Chong Zhanga1320c52020-12-15 14:30:12 -0800833 if (mSessionMap[sessionKey].getState() == Session::NOT_STARTED) {
Chong Zhangbc062482020-10-14 16:43:53 -0700834 ALOGW("%s: ignoring %s for session %s that was never started", __FUNCTION__, reason,
835 sessionToString(sessionKey).c_str());
836 return;
837 }
838
839 ALOGV("%s: session %s %s", __FUNCTION__, sessionToString(sessionKey).c_str(), reason);
840 func(sessionKey);
841}
842
843void TranscodingSessionController::onStarted(ClientIdType clientId, SessionIdType sessionId) {
844 notifyClient(clientId, sessionId, "started", [=](const SessionKeyType& sessionKey) {
845 auto callback = mSessionMap[sessionKey].callback.lock();
846 if (callback != nullptr) {
847 callback->onTranscodingStarted(sessionId);
848 }
849 });
850}
851
852void TranscodingSessionController::onPaused(ClientIdType clientId, SessionIdType sessionId) {
853 notifyClient(clientId, sessionId, "paused", [=](const SessionKeyType& sessionKey) {
854 auto callback = mSessionMap[sessionKey].callback.lock();
855 if (callback != nullptr) {
856 callback->onTranscodingPaused(sessionId);
857 }
858 });
859}
860
861void TranscodingSessionController::onResumed(ClientIdType clientId, SessionIdType sessionId) {
862 notifyClient(clientId, sessionId, "resumed", [=](const SessionKeyType& sessionKey) {
863 auto callback = mSessionMap[sessionKey].callback.lock();
864 if (callback != nullptr) {
865 callback->onTranscodingResumed(sessionId);
866 }
867 });
868}
869
870void TranscodingSessionController::onFinish(ClientIdType clientId, SessionIdType sessionId) {
871 notifyClient(clientId, sessionId, "finish", [=](const SessionKeyType& sessionKey) {
872 {
873 auto clientCallback = mSessionMap[sessionKey].callback.lock();
874 if (clientCallback != nullptr) {
875 clientCallback->onTranscodingFinished(
876 sessionId, TranscodingResultParcel({sessionId, -1 /*actualBitrateBps*/,
877 std::nullopt /*sessionStats*/}));
878 }
879 }
880
881 // Remove the session.
Chong Zhanga1320c52020-12-15 14:30:12 -0800882 removeSession_l(sessionKey, Session::FINISHED);
Chong Zhangbc062482020-10-14 16:43:53 -0700883
884 // Start next session.
885 updateCurrentSession_l();
886
887 validateState_l();
888 });
889}
890
891void TranscodingSessionController::onError(ClientIdType clientId, SessionIdType sessionId,
892 TranscodingErrorCode err) {
893 notifyClient(clientId, sessionId, "error", [=](const SessionKeyType& sessionKey) {
Chong Zhang457c6892021-02-01 15:34:20 -0800894 if (err == TranscodingErrorCode::kWatchdogTimeout) {
895 // Abandon the transcoder, as its handler thread might be stuck in some call to
896 // MediaTranscoder altogether, and may not be able to handle any new tasks.
897 mTranscoder->stop(clientId, sessionId, true /*abandon*/);
898 // Clear the last ref count before we create new transcoder.
899 mTranscoder = nullptr;
Chong Zhang87d199c2021-03-01 19:02:18 -0800900 mTranscoder = mTranscoderFactory(shared_from_this());
Chong Zhang457c6892021-02-01 15:34:20 -0800901 }
902
Chong Zhangbc062482020-10-14 16:43:53 -0700903 {
904 auto clientCallback = mSessionMap[sessionKey].callback.lock();
905 if (clientCallback != nullptr) {
906 clientCallback->onTranscodingFailed(sessionId, err);
907 }
908 }
909
910 // Remove the session.
Chong Zhanga1320c52020-12-15 14:30:12 -0800911 removeSession_l(sessionKey, Session::ERROR);
Chong Zhangbc062482020-10-14 16:43:53 -0700912
913 // Start next session.
914 updateCurrentSession_l();
915
916 validateState_l();
917 });
918}
919
920void TranscodingSessionController::onProgressUpdate(ClientIdType clientId, SessionIdType sessionId,
921 int32_t progress) {
922 notifyClient(clientId, sessionId, "progress", [=](const SessionKeyType& sessionKey) {
923 auto callback = mSessionMap[sessionKey].callback.lock();
924 if (callback != nullptr) {
925 callback->onProgressUpdate(sessionId, progress);
926 }
927 mSessionMap[sessionKey].lastProgress = progress;
928 });
929}
930
Chong Zhang457c6892021-02-01 15:34:20 -0800931void TranscodingSessionController::onHeartBeat(ClientIdType clientId, SessionIdType sessionId) {
932 notifyClient(clientId, sessionId, "heart-beat",
933 [=](const SessionKeyType& /*sessionKey*/) { mWatchdog->keepAlive(); });
934}
935
Chong Zhangeffd8962020-12-02 14:29:09 -0800936void TranscodingSessionController::onResourceLost(ClientIdType clientId, SessionIdType sessionId) {
Chong Zhangbc062482020-10-14 16:43:53 -0700937 ALOGI("%s", __FUNCTION__);
938
Chong Zhangeffd8962020-12-02 14:29:09 -0800939 notifyClient(clientId, sessionId, "resource_lost", [=](const SessionKeyType& sessionKey) {
940 if (mResourceLost) {
941 return;
Chong Zhangbc062482020-10-14 16:43:53 -0700942 }
Chong Zhangbc062482020-10-14 16:43:53 -0700943
Chong Zhangeffd8962020-12-02 14:29:09 -0800944 Session* resourceLostSession = &mSessionMap[sessionKey];
Chong Zhanga1320c52020-12-15 14:30:12 -0800945 if (resourceLostSession->getState() != Session::RUNNING) {
Chong Zhangeffd8962020-12-02 14:29:09 -0800946 ALOGW("session %s lost resource but is no longer running",
Chong Zhanga1320c52020-12-15 14:30:12 -0800947 sessionToString(sessionKey).c_str());
Chong Zhangeffd8962020-12-02 14:29:09 -0800948 return;
949 }
950 // If we receive a resource loss event, the transcoder already paused the transcoding,
951 // so we don't need to call onPaused() to pause it. However, we still need to notify
952 // the client and update the session state here.
Chong Zhang457c6892021-02-01 15:34:20 -0800953 setSessionState_l(resourceLostSession, Session::PAUSED);
Chong Zhangeffd8962020-12-02 14:29:09 -0800954 // Notify the client as a paused event.
955 auto clientCallback = resourceLostSession->callback.lock();
956 if (clientCallback != nullptr) {
957 clientCallback->onTranscodingPaused(sessionKey.second);
958 }
Chong Zhang8677f1f2021-01-21 20:37:35 +0000959 if (mResourcePolicy != nullptr) {
960 mResourcePolicy->setPidResourceLost(resourceLostSession->request.clientPid);
961 }
Chong Zhangeffd8962020-12-02 14:29:09 -0800962 mResourceLost = true;
963
964 validateState_l();
965 });
Chong Zhangbc062482020-10-14 16:43:53 -0700966}
967
968void TranscodingSessionController::onTopUidsChanged(const std::unordered_set<uid_t>& uids) {
969 if (uids.empty()) {
970 ALOGW("%s: ignoring empty uids", __FUNCTION__);
971 return;
972 }
973
974 std::string uidStr;
975 for (auto it = uids.begin(); it != uids.end(); it++) {
976 if (!uidStr.empty()) {
977 uidStr += ", ";
978 }
979 uidStr += std::to_string(*it);
980 }
981
982 ALOGD("%s: topUids: size %zu, uids: %s", __FUNCTION__, uids.size(), uidStr.c_str());
983
984 std::scoped_lock lock{mLock};
985
986 moveUidsToTop_l(uids, true /*preserveTopUid*/);
987
988 updateCurrentSession_l();
989
990 validateState_l();
991}
992
993void TranscodingSessionController::onResourceAvailable() {
994 std::scoped_lock lock{mLock};
995
996 if (!mResourceLost) {
997 return;
998 }
999
1000 ALOGI("%s", __FUNCTION__);
1001
1002 mResourceLost = false;
1003 updateCurrentSession_l();
1004
1005 validateState_l();
1006}
1007
Chong Zhang8677f1f2021-01-21 20:37:35 +00001008void TranscodingSessionController::onThrottlingStarted() {
1009 std::scoped_lock lock{mLock};
1010
1011 if (mThermalThrottling) {
1012 return;
1013 }
1014
1015 ALOGI("%s", __FUNCTION__);
1016
1017 mThermalThrottling = true;
1018 updateCurrentSession_l();
1019
1020 validateState_l();
1021}
1022
1023void TranscodingSessionController::onThrottlingStopped() {
1024 std::scoped_lock lock{mLock};
1025
1026 if (!mThermalThrottling) {
1027 return;
1028 }
1029
1030 ALOGI("%s", __FUNCTION__);
1031
1032 mThermalThrottling = false;
1033 updateCurrentSession_l();
1034
1035 validateState_l();
1036}
1037
Chong Zhangbc062482020-10-14 16:43:53 -07001038void TranscodingSessionController::validateState_l() {
1039#ifdef VALIDATE_STATE
1040 LOG_ALWAYS_FATAL_IF(mSessionQueues.count(OFFLINE_UID) != 1,
1041 "mSessionQueues offline queue number is not 1");
1042 LOG_ALWAYS_FATAL_IF(*mOfflineUidIterator != OFFLINE_UID,
1043 "mOfflineUidIterator not pointing to offline uid");
1044 LOG_ALWAYS_FATAL_IF(mUidSortedList.size() != mSessionQueues.size(),
Chong Zhangebd86d32021-03-29 11:30:56 -07001045 "mUidSortedList and mSessionQueues size mismatch, %zu vs %zu",
1046 mUidSortedList.size(), mSessionQueues.size());
Chong Zhangbc062482020-10-14 16:43:53 -07001047
1048 int32_t totalSessions = 0;
1049 for (auto uid : mUidSortedList) {
1050 LOG_ALWAYS_FATAL_IF(mSessionQueues.count(uid) != 1,
1051 "mSessionQueues count for uid %d is not 1", uid);
1052 for (auto& sessionKey : mSessionQueues[uid]) {
1053 LOG_ALWAYS_FATAL_IF(mSessionMap.count(sessionKey) != 1,
1054 "mSessions count for session %s is not 1",
1055 sessionToString(sessionKey).c_str());
1056 }
1057
1058 totalSessions += mSessionQueues[uid].size();
1059 }
Chong Zhangebd86d32021-03-29 11:30:56 -07001060 int32_t totalSessionsAlternative = 0;
1061 for (auto const& s : mSessionMap) {
1062 totalSessionsAlternative += s.second.allClientUids.size();
1063 }
1064 LOG_ALWAYS_FATAL_IF(totalSessions != totalSessionsAlternative,
1065 "session count (including dup) from mSessionQueues doesn't match that from "
1066 "mSessionMap, %d vs %d",
1067 totalSessions, totalSessionsAlternative);
Chong Zhangbc062482020-10-14 16:43:53 -07001068#endif // VALIDATE_STATE
1069}
1070
1071} // namespace android