blob: d12af2181478841305829855c3ee5b0046d739bc [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.
90 std::chrono::system_clock::time_point mNextTimeoutTime GUARDED_BY(mLock);
91 // When watchdog is active, the session being watched.
92 SessionKeyType mSessionToWatch GUARDED_BY(mLock);
93 std::thread mThread;
94};
95
96static constexpr int64_t kWatchdogTimeoutUs = 3000000LL;
97static constexpr int64_t kTranscoderHeartBeatIntervalUs = 1000000LL;
98
99TranscodingSessionController::Watchdog::Watchdog(TranscodingSessionController* owner,
100 int64_t timeoutUs)
101 : mOwner(owner),
102 mTimeoutUs(timeoutUs),
103 mActive(false),
104 mAbort(false),
105 mThread(&Watchdog::threadLoop, this) {
106 ALOGV("Watchdog CTOR: %p", this);
107}
108
109TranscodingSessionController::Watchdog::~Watchdog() {
110 ALOGV("Watchdog DTOR: %p", this);
111
112 {
113 // Exit the looper thread.
114 std::scoped_lock lock{mLock};
115
116 mAbort = true;
117 mCondition.notify_one();
118 }
119
120 mThread.join();
121 ALOGV("Watchdog DTOR: %p, done.", this);
122}
123
124void TranscodingSessionController::Watchdog::start(const SessionKeyType& key) {
125 std::scoped_lock lock{mLock};
126
127 if (!mActive) {
128 ALOGI("Watchdog start: %s", sessionToString(key).c_str());
129
130 mActive = true;
131 mSessionToWatch = key;
132 updateTimer_l();
133 mCondition.notify_one();
134 }
135}
136
137void TranscodingSessionController::Watchdog::stop() {
138 std::scoped_lock lock{mLock};
139
140 if (mActive) {
141 ALOGI("Watchdog stop: %s", sessionToString(mSessionToWatch).c_str());
142
143 mActive = false;
144 mCondition.notify_one();
145 }
146}
147
148void TranscodingSessionController::Watchdog::keepAlive() {
149 std::scoped_lock lock{mLock};
150
151 if (mActive) {
152 ALOGI("Watchdog keepAlive: %s", sessionToString(mSessionToWatch).c_str());
153
154 updateTimer_l();
155 mCondition.notify_one();
156 }
157}
158
159// updateTimer_l() is only called with lock held.
160void TranscodingSessionController::Watchdog::updateTimer_l() NO_THREAD_SAFETY_ANALYSIS {
161 std::chrono::microseconds timeout(mTimeoutUs);
162 mNextTimeoutTime = std::chrono::system_clock::now() + timeout;
163}
164
165// Unfortunately std::unique_lock is incompatible with -Wthread-safety.
166void TranscodingSessionController::Watchdog::threadLoop() NO_THREAD_SAFETY_ANALYSIS {
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
Chong Zhangbc062482020-10-14 16:43:53 -0700192TranscodingSessionController::TranscodingSessionController(
Chong Zhang457c6892021-02-01 15:34:20 -0800193 const TranscoderFactoryType& transcoderFactory,
Chong Zhangbc062482020-10-14 16:43:53 -0700194 const std::shared_ptr<UidPolicyInterface>& uidPolicy,
Chong Zhang8677f1f2021-01-21 20:37:35 +0000195 const std::shared_ptr<ResourcePolicyInterface>& resourcePolicy,
196 const std::shared_ptr<ThermalPolicyInterface>& thermalPolicy)
Chong Zhang457c6892021-02-01 15:34:20 -0800197 : mTranscoderFactory(transcoderFactory),
Chong Zhangbc062482020-10-14 16:43:53 -0700198 mUidPolicy(uidPolicy),
199 mResourcePolicy(resourcePolicy),
Chong Zhang8677f1f2021-01-21 20:37:35 +0000200 mThermalPolicy(thermalPolicy),
Chong Zhangbc062482020-10-14 16:43:53 -0700201 mCurrentSession(nullptr),
202 mResourceLost(false) {
203 // Only push empty offline queue initially. Realtime queues are added when requests come in.
204 mUidSortedList.push_back(OFFLINE_UID);
205 mOfflineUidIterator = mUidSortedList.begin();
206 mSessionQueues.emplace(OFFLINE_UID, SessionQueueType());
Chong Zhangcf3f8ee2020-10-29 18:38:37 -0700207 mUidPackageNames[OFFLINE_UID] = "(offline)";
Chong Zhang8677f1f2021-01-21 20:37:35 +0000208 mThermalThrottling = thermalPolicy->getThrottlingStatus();
Chong Zhangbc062482020-10-14 16:43:53 -0700209}
210
211TranscodingSessionController::~TranscodingSessionController() {}
212
Chong Zhanga1320c52020-12-15 14:30:12 -0800213void TranscodingSessionController::dumpSession_l(const Session& session, String8& result,
214 bool closedSession) {
215 const size_t SIZE = 256;
216 char buffer[SIZE];
217 const TranscodingRequestParcel& request = session.request;
218 snprintf(buffer, SIZE, " Session: %s, %s, %d%%\n", sessionToString(session.key).c_str(),
219 sessionStateToString(session.getState()), session.lastProgress);
220 result.append(buffer);
221 snprintf(buffer, SIZE, " pkg: %s\n", request.clientPackageName.c_str());
222 result.append(buffer);
223 snprintf(buffer, SIZE, " src: %s\n", request.sourceFilePath.c_str());
224 result.append(buffer);
225 snprintf(buffer, SIZE, " dst: %s\n", request.destinationFilePath.c_str());
226 result.append(buffer);
227
228 if (closedSession) {
229 snprintf(buffer, SIZE,
230 " waiting: %.1fs, running: %.1fs, paused: %.1fs, paused count: %d\n",
231 session.waitingTime.count() / 1000000.0f, session.runningTime.count() / 1000000.0f,
232 session.pausedTime.count() / 1000000.0f, session.pauseCount);
233 result.append(buffer);
234 }
235}
236
Chong Zhangbc062482020-10-14 16:43:53 -0700237void TranscodingSessionController::dumpAllSessions(int fd, const Vector<String16>& args __unused) {
238 String8 result;
239
240 const size_t SIZE = 256;
241 char buffer[SIZE];
242 std::scoped_lock lock{mLock};
243
Chong Zhanga1320c52020-12-15 14:30:12 -0800244 snprintf(buffer, SIZE, "\n========== Dumping live sessions queues =========\n");
Chong Zhangbc062482020-10-14 16:43:53 -0700245 result.append(buffer);
246 snprintf(buffer, SIZE, " Total num of Sessions: %zu\n", mSessionMap.size());
247 result.append(buffer);
248
249 std::vector<int32_t> uids(mUidSortedList.begin(), mUidSortedList.end());
Chong Zhangbc062482020-10-14 16:43:53 -0700250
251 for (int32_t i = 0; i < uids.size(); i++) {
252 const uid_t uid = uids[i];
253
254 if (mSessionQueues[uid].empty()) {
255 continue;
256 }
Chong Zhanga1320c52020-12-15 14:30:12 -0800257 snprintf(buffer, SIZE, " uid: %d, pkg: %s\n", uid,
Chong Zhangcf3f8ee2020-10-29 18:38:37 -0700258 mUidPackageNames.count(uid) > 0 ? mUidPackageNames[uid].c_str() : "(unknown)");
Chong Zhangbc062482020-10-14 16:43:53 -0700259 result.append(buffer);
260 snprintf(buffer, SIZE, " Num of sessions: %zu\n", mSessionQueues[uid].size());
261 result.append(buffer);
262 for (auto& sessionKey : mSessionQueues[uid]) {
263 auto sessionIt = mSessionMap.find(sessionKey);
264 if (sessionIt == mSessionMap.end()) {
265 snprintf(buffer, SIZE, "Failed to look up Session %s \n",
266 sessionToString(sessionKey).c_str());
267 result.append(buffer);
268 continue;
269 }
Chong Zhanga1320c52020-12-15 14:30:12 -0800270 dumpSession_l(sessionIt->second, result);
Chong Zhangbc062482020-10-14 16:43:53 -0700271 }
272 }
273
Chong Zhanga1320c52020-12-15 14:30:12 -0800274 snprintf(buffer, SIZE, "\n========== Dumping past sessions =========\n");
275 result.append(buffer);
Chong Zhang8677f1f2021-01-21 20:37:35 +0000276 for (auto& session : mSessionHistory) {
Chong Zhanga1320c52020-12-15 14:30:12 -0800277 dumpSession_l(session, result, true /*closedSession*/);
278 }
279
Chong Zhangbc062482020-10-14 16:43:53 -0700280 write(fd, result.string(), result.size());
281}
282
283TranscodingSessionController::Session* TranscodingSessionController::getTopSession_l() {
284 if (mSessionMap.empty()) {
285 return nullptr;
286 }
287 uid_t topUid = *mUidSortedList.begin();
288 SessionKeyType topSessionKey = *mSessionQueues[topUid].begin();
289 return &mSessionMap[topSessionKey];
290}
291
Chong Zhang457c6892021-02-01 15:34:20 -0800292void TranscodingSessionController::setSessionState_l(Session* session, Session::State state) {
293 bool wasRunning = (session->getState() == Session::RUNNING);
294 session->setState(state);
295 bool isRunning = (session->getState() == Session::RUNNING);
296
297 if (wasRunning == isRunning) {
298 return;
299 }
300
301 // Currently we only have 1 running session, and we always put the previous
302 // session in non-running state before we run the new session, so it's okay
303 // to start/stop the watchdog here. If this assumption changes, we need to
304 // track the number of running sessions and start/stop watchdog based on that.
305 if (isRunning) {
306 mWatchdog->start(session->key);
307 } else {
308 mWatchdog->stop();
309 }
310}
311
Chong Zhanga1320c52020-12-15 14:30:12 -0800312void TranscodingSessionController::Session::setState(Session::State newState) {
313 if (state == newState) {
314 return;
315 }
316 auto nowTime = std::chrono::system_clock::now();
317 if (state != INVALID) {
318 std::chrono::microseconds elapsedTime = (nowTime - stateEnterTime);
319 switch (state) {
320 case PAUSED:
321 pausedTime = pausedTime + elapsedTime;
322 break;
323 case RUNNING:
324 runningTime = runningTime + elapsedTime;
325 break;
326 case NOT_STARTED:
327 waitingTime = waitingTime + elapsedTime;
328 break;
329 default:
330 break;
331 }
332 }
333 if (newState == PAUSED) {
334 pauseCount++;
335 }
336 stateEnterTime = nowTime;
337 state = newState;
338}
339
Chong Zhangbc062482020-10-14 16:43:53 -0700340void TranscodingSessionController::updateCurrentSession_l() {
341 Session* topSession = getTopSession_l();
342 Session* curSession = mCurrentSession;
343 ALOGV("updateCurrentSession: topSession is %s, curSession is %s",
344 topSession == nullptr ? "null" : sessionToString(topSession->key).c_str(),
345 curSession == nullptr ? "null" : sessionToString(curSession->key).c_str());
346
Chong Zhang8677f1f2021-01-21 20:37:35 +0000347 if (topSession == nullptr) {
348 mCurrentSession = nullptr;
349 return;
350 }
351
352 bool shouldBeRunning = !((mResourcePolicy != nullptr && mResourceLost) ||
353 (mThermalPolicy != nullptr && mThermalThrottling));
Chong Zhangbc062482020-10-14 16:43:53 -0700354 // If we found a topSession that should be run, and it's not already running,
355 // take some actions to ensure it's running.
Chong Zhang8677f1f2021-01-21 20:37:35 +0000356 if (topSession != curSession ||
357 (shouldBeRunning ^ (topSession->getState() == Session::RUNNING))) {
Chong Zhang457c6892021-02-01 15:34:20 -0800358 if (mTranscoder == nullptr) {
359 mTranscoder = mTranscoderFactory(shared_from_this(), kTranscoderHeartBeatIntervalUs);
360 mWatchdog = std::make_shared<Watchdog>(this, kWatchdogTimeoutUs);
361 }
362
Chong Zhang8677f1f2021-01-21 20:37:35 +0000363 // If current session is running, pause it first. Note this is true for either
364 // cases: 1) If top session is changing, or 2) if top session is not changing but
365 // the topSession's state is changing.
Chong Zhanga1320c52020-12-15 14:30:12 -0800366 if (curSession != nullptr && curSession->getState() == Session::RUNNING) {
Chong Zhangbc062482020-10-14 16:43:53 -0700367 mTranscoder->pause(curSession->key.first, curSession->key.second);
Chong Zhang457c6892021-02-01 15:34:20 -0800368 setSessionState_l(curSession, Session::PAUSED);
Chong Zhangbc062482020-10-14 16:43:53 -0700369 }
Chong Zhang8677f1f2021-01-21 20:37:35 +0000370 // If we are not experiencing resource loss nor thermal throttling, we can start
371 // or resume the topSession now.
372 if (shouldBeRunning) {
Chong Zhanga1320c52020-12-15 14:30:12 -0800373 if (topSession->getState() == Session::NOT_STARTED) {
Chong Zhangbc062482020-10-14 16:43:53 -0700374 mTranscoder->start(topSession->key.first, topSession->key.second,
375 topSession->request, topSession->callback.lock());
Chong Zhanga1320c52020-12-15 14:30:12 -0800376 } else if (topSession->getState() == Session::PAUSED) {
Chong Zhangbc062482020-10-14 16:43:53 -0700377 mTranscoder->resume(topSession->key.first, topSession->key.second,
378 topSession->request, topSession->callback.lock());
379 }
Chong Zhang457c6892021-02-01 15:34:20 -0800380 setSessionState_l(topSession, Session::RUNNING);
Chong Zhangbc062482020-10-14 16:43:53 -0700381 }
382 }
383 mCurrentSession = topSession;
384}
385
Chong Zhanga1320c52020-12-15 14:30:12 -0800386void TranscodingSessionController::removeSession_l(const SessionKeyType& sessionKey,
387 Session::State finalState) {
Chong Zhangbc062482020-10-14 16:43:53 -0700388 ALOGV("%s: session %s", __FUNCTION__, sessionToString(sessionKey).c_str());
389
390 if (mSessionMap.count(sessionKey) == 0) {
391 ALOGE("session %s doesn't exist", sessionToString(sessionKey).c_str());
392 return;
393 }
394
395 // Remove session from uid's queue.
396 const uid_t uid = mSessionMap[sessionKey].uid;
397 SessionQueueType& sessionQueue = mSessionQueues[uid];
398 auto it = std::find(sessionQueue.begin(), sessionQueue.end(), sessionKey);
399 if (it == sessionQueue.end()) {
400 ALOGE("couldn't find session %s in queue for uid %d", sessionToString(sessionKey).c_str(),
401 uid);
402 return;
403 }
404 sessionQueue.erase(it);
405
406 // If this is the last session in a real-time queue, remove this uid's queue.
407 if (uid != OFFLINE_UID && sessionQueue.empty()) {
408 mUidSortedList.remove(uid);
409 mSessionQueues.erase(uid);
410 mUidPolicy->unregisterMonitorUid(uid);
411
412 std::unordered_set<uid_t> topUids = mUidPolicy->getTopUids();
413 moveUidsToTop_l(topUids, false /*preserveTopUid*/);
414 }
415
416 // Clear current session.
417 if (mCurrentSession == &mSessionMap[sessionKey]) {
418 mCurrentSession = nullptr;
419 }
420
Chong Zhang457c6892021-02-01 15:34:20 -0800421 setSessionState_l(&mSessionMap[sessionKey], finalState);
Chong Zhanga1320c52020-12-15 14:30:12 -0800422 mSessionHistory.push_back(mSessionMap[sessionKey]);
423 if (mSessionHistory.size() > kSessionHistoryMax) {
424 mSessionHistory.erase(mSessionHistory.begin());
425 }
426
Chong Zhangbc062482020-10-14 16:43:53 -0700427 // Remove session from session map.
428 mSessionMap.erase(sessionKey);
429}
430
431/**
432 * Moves the set of uids to the front of mUidSortedList (which is used to pick
433 * the next session to run).
434 *
435 * This is called when 1) we received a onTopUidsChanged() callback from UidPolicy,
436 * or 2) we removed the session queue for a uid because it becomes empty.
437 *
438 * In case of 1), if there are multiple uids in the set, and the current front
439 * uid in mUidSortedList is still in the set, we try to keep that uid at front
440 * so that current session run is not interrupted. (This is not a concern for case 2)
441 * because the queue for a uid was just removed entirely.)
442 */
443void TranscodingSessionController::moveUidsToTop_l(const std::unordered_set<uid_t>& uids,
444 bool preserveTopUid) {
445 // If uid set is empty, nothing to do. Do not change the queue status.
446 if (uids.empty()) {
447 return;
448 }
449
450 // Save the current top uid.
451 uid_t curTopUid = *mUidSortedList.begin();
452 bool pushCurTopToFront = false;
453 int32_t numUidsMoved = 0;
454
455 // Go through the sorted uid list once, and move the ones in top set to front.
456 for (auto it = mUidSortedList.begin(); it != mUidSortedList.end();) {
457 uid_t uid = *it;
458
459 if (uid != OFFLINE_UID && uids.count(uid) > 0) {
460 it = mUidSortedList.erase(it);
461
462 // If this is the top we're preserving, don't push it here, push
463 // it after the for-loop.
464 if (uid == curTopUid && preserveTopUid) {
465 pushCurTopToFront = true;
466 } else {
467 mUidSortedList.push_front(uid);
468 }
469
470 // If we found all uids in the set, break out.
471 if (++numUidsMoved == uids.size()) {
472 break;
473 }
474 } else {
475 ++it;
476 }
477 }
478
479 if (pushCurTopToFront) {
480 mUidSortedList.push_front(curTopUid);
481 }
482}
483
484bool TranscodingSessionController::submit(
485 ClientIdType clientId, SessionIdType sessionId, uid_t uid,
486 const TranscodingRequestParcel& request,
487 const std::weak_ptr<ITranscodingClientCallback>& callback) {
488 SessionKeyType sessionKey = std::make_pair(clientId, sessionId);
489
490 ALOGV("%s: session %s, uid %d, prioirty %d", __FUNCTION__, sessionToString(sessionKey).c_str(),
491 uid, (int32_t)request.priority);
492
493 std::scoped_lock lock{mLock};
494
495 if (mSessionMap.count(sessionKey) > 0) {
496 ALOGE("session %s already exists", sessionToString(sessionKey).c_str());
497 return false;
498 }
499
Chong Zhangcf3f8ee2020-10-29 18:38:37 -0700500 // Add the uid package name to the store of package names we already know.
501 if (mUidPackageNames.count(uid) == 0) {
502 mUidPackageNames.emplace(uid, request.clientPackageName);
503 }
504
Chong Zhangbc062482020-10-14 16:43:53 -0700505 // TODO(chz): only support offline vs real-time for now. All kUnspecified sessions
506 // go to offline queue.
507 if (request.priority == TranscodingSessionPriority::kUnspecified) {
508 uid = OFFLINE_UID;
509 }
510
511 // Add session to session map.
512 mSessionMap[sessionKey].key = sessionKey;
513 mSessionMap[sessionKey].uid = uid;
Chong Zhangbc062482020-10-14 16:43:53 -0700514 mSessionMap[sessionKey].lastProgress = 0;
Chong Zhanga1320c52020-12-15 14:30:12 -0800515 mSessionMap[sessionKey].pauseCount = 0;
Chong Zhangbc062482020-10-14 16:43:53 -0700516 mSessionMap[sessionKey].request = request;
517 mSessionMap[sessionKey].callback = callback;
Chong Zhang457c6892021-02-01 15:34:20 -0800518 setSessionState_l(&mSessionMap[sessionKey], Session::NOT_STARTED);
Chong Zhangbc062482020-10-14 16:43:53 -0700519
520 // If it's an offline session, the queue was already added in constructor.
521 // If it's a real-time sessions, check if a queue is already present for the uid,
522 // and add a new queue if needed.
523 if (uid != OFFLINE_UID) {
524 if (mSessionQueues.count(uid) == 0) {
525 mUidPolicy->registerMonitorUid(uid);
526 if (mUidPolicy->isUidOnTop(uid)) {
527 mUidSortedList.push_front(uid);
528 } else {
529 // Shouldn't be submitting real-time requests from non-top app,
530 // put it in front of the offline queue.
531 mUidSortedList.insert(mOfflineUidIterator, uid);
532 }
533 } else if (uid != *mUidSortedList.begin()) {
534 if (mUidPolicy->isUidOnTop(uid)) {
535 mUidSortedList.remove(uid);
536 mUidSortedList.push_front(uid);
537 }
538 }
539 }
540 // Append this session to the uid's queue.
541 mSessionQueues[uid].push_back(sessionKey);
542
543 updateCurrentSession_l();
544
545 validateState_l();
546 return true;
547}
548
549bool TranscodingSessionController::cancel(ClientIdType clientId, SessionIdType sessionId) {
550 SessionKeyType sessionKey = std::make_pair(clientId, sessionId);
551
552 ALOGV("%s: session %s", __FUNCTION__, sessionToString(sessionKey).c_str());
553
554 std::list<SessionKeyType> sessionsToRemove;
555
556 std::scoped_lock lock{mLock};
557
558 if (sessionId < 0) {
559 for (auto it = mSessionMap.begin(); it != mSessionMap.end(); ++it) {
560 if (it->first.first == clientId && it->second.uid != OFFLINE_UID) {
561 sessionsToRemove.push_back(it->first);
562 }
563 }
564 } else {
565 if (mSessionMap.count(sessionKey) == 0) {
566 ALOGE("session %s doesn't exist", sessionToString(sessionKey).c_str());
567 return false;
568 }
569 sessionsToRemove.push_back(sessionKey);
570 }
571
572 for (auto it = sessionsToRemove.begin(); it != sessionsToRemove.end(); ++it) {
573 // If the session has ever been started, stop it now.
574 // Note that stop() is needed even if the session is currently paused. This instructs
575 // the transcoder to discard any states for the session, otherwise the states may
576 // never be discarded.
Chong Zhanga1320c52020-12-15 14:30:12 -0800577 if (mSessionMap[*it].getState() != Session::NOT_STARTED) {
Chong Zhangbc062482020-10-14 16:43:53 -0700578 mTranscoder->stop(it->first, it->second);
579 }
580
581 // Remove the session.
Chong Zhanga1320c52020-12-15 14:30:12 -0800582 removeSession_l(*it, Session::CANCELED);
Chong Zhangbc062482020-10-14 16:43:53 -0700583 }
584
585 // Start next session.
586 updateCurrentSession_l();
587
588 validateState_l();
589 return true;
590}
591
592bool TranscodingSessionController::getSession(ClientIdType clientId, SessionIdType sessionId,
593 TranscodingRequestParcel* request) {
594 SessionKeyType sessionKey = std::make_pair(clientId, sessionId);
595
596 std::scoped_lock lock{mLock};
597
598 if (mSessionMap.count(sessionKey) == 0) {
599 ALOGE("session %s doesn't exist", sessionToString(sessionKey).c_str());
600 return false;
601 }
602
603 *(TranscodingRequest*)request = mSessionMap[sessionKey].request;
604 return true;
605}
606
607void TranscodingSessionController::notifyClient(ClientIdType clientId, SessionIdType sessionId,
608 const char* reason,
609 std::function<void(const SessionKeyType&)> func) {
610 SessionKeyType sessionKey = std::make_pair(clientId, sessionId);
611
612 std::scoped_lock lock{mLock};
613
614 if (mSessionMap.count(sessionKey) == 0) {
615 ALOGW("%s: ignoring %s for session %s that doesn't exist", __FUNCTION__, reason,
616 sessionToString(sessionKey).c_str());
617 return;
618 }
619
620 // Only ignore if session was never started. In particular, propagate the status
621 // to client if the session is paused. Transcoder could have posted finish when
622 // we're pausing it, and the finish arrived after we changed current session.
Chong Zhanga1320c52020-12-15 14:30:12 -0800623 if (mSessionMap[sessionKey].getState() == Session::NOT_STARTED) {
Chong Zhangbc062482020-10-14 16:43:53 -0700624 ALOGW("%s: ignoring %s for session %s that was never started", __FUNCTION__, reason,
625 sessionToString(sessionKey).c_str());
626 return;
627 }
628
629 ALOGV("%s: session %s %s", __FUNCTION__, sessionToString(sessionKey).c_str(), reason);
630 func(sessionKey);
631}
632
633void TranscodingSessionController::onStarted(ClientIdType clientId, SessionIdType sessionId) {
634 notifyClient(clientId, sessionId, "started", [=](const SessionKeyType& sessionKey) {
635 auto callback = mSessionMap[sessionKey].callback.lock();
636 if (callback != nullptr) {
637 callback->onTranscodingStarted(sessionId);
638 }
639 });
640}
641
642void TranscodingSessionController::onPaused(ClientIdType clientId, SessionIdType sessionId) {
643 notifyClient(clientId, sessionId, "paused", [=](const SessionKeyType& sessionKey) {
644 auto callback = mSessionMap[sessionKey].callback.lock();
645 if (callback != nullptr) {
646 callback->onTranscodingPaused(sessionId);
647 }
648 });
649}
650
651void TranscodingSessionController::onResumed(ClientIdType clientId, SessionIdType sessionId) {
652 notifyClient(clientId, sessionId, "resumed", [=](const SessionKeyType& sessionKey) {
653 auto callback = mSessionMap[sessionKey].callback.lock();
654 if (callback != nullptr) {
655 callback->onTranscodingResumed(sessionId);
656 }
657 });
658}
659
660void TranscodingSessionController::onFinish(ClientIdType clientId, SessionIdType sessionId) {
661 notifyClient(clientId, sessionId, "finish", [=](const SessionKeyType& sessionKey) {
662 {
663 auto clientCallback = mSessionMap[sessionKey].callback.lock();
664 if (clientCallback != nullptr) {
665 clientCallback->onTranscodingFinished(
666 sessionId, TranscodingResultParcel({sessionId, -1 /*actualBitrateBps*/,
667 std::nullopt /*sessionStats*/}));
668 }
669 }
670
671 // Remove the session.
Chong Zhanga1320c52020-12-15 14:30:12 -0800672 removeSession_l(sessionKey, Session::FINISHED);
Chong Zhangbc062482020-10-14 16:43:53 -0700673
674 // Start next session.
675 updateCurrentSession_l();
676
677 validateState_l();
678 });
679}
680
681void TranscodingSessionController::onError(ClientIdType clientId, SessionIdType sessionId,
682 TranscodingErrorCode err) {
683 notifyClient(clientId, sessionId, "error", [=](const SessionKeyType& sessionKey) {
Chong Zhang457c6892021-02-01 15:34:20 -0800684 if (err == TranscodingErrorCode::kWatchdogTimeout) {
685 // Abandon the transcoder, as its handler thread might be stuck in some call to
686 // MediaTranscoder altogether, and may not be able to handle any new tasks.
687 mTranscoder->stop(clientId, sessionId, true /*abandon*/);
688 // Clear the last ref count before we create new transcoder.
689 mTranscoder = nullptr;
690 mTranscoder = mTranscoderFactory(shared_from_this(), kTranscoderHeartBeatIntervalUs);
691 }
692
Chong Zhangbc062482020-10-14 16:43:53 -0700693 {
694 auto clientCallback = mSessionMap[sessionKey].callback.lock();
695 if (clientCallback != nullptr) {
696 clientCallback->onTranscodingFailed(sessionId, err);
697 }
698 }
699
700 // Remove the session.
Chong Zhanga1320c52020-12-15 14:30:12 -0800701 removeSession_l(sessionKey, Session::ERROR);
Chong Zhangbc062482020-10-14 16:43:53 -0700702
703 // Start next session.
704 updateCurrentSession_l();
705
706 validateState_l();
707 });
708}
709
710void TranscodingSessionController::onProgressUpdate(ClientIdType clientId, SessionIdType sessionId,
711 int32_t progress) {
712 notifyClient(clientId, sessionId, "progress", [=](const SessionKeyType& sessionKey) {
713 auto callback = mSessionMap[sessionKey].callback.lock();
714 if (callback != nullptr) {
715 callback->onProgressUpdate(sessionId, progress);
716 }
717 mSessionMap[sessionKey].lastProgress = progress;
718 });
719}
720
Chong Zhang457c6892021-02-01 15:34:20 -0800721void TranscodingSessionController::onHeartBeat(ClientIdType clientId, SessionIdType sessionId) {
722 notifyClient(clientId, sessionId, "heart-beat",
723 [=](const SessionKeyType& /*sessionKey*/) { mWatchdog->keepAlive(); });
724}
725
Chong Zhangeffd8962020-12-02 14:29:09 -0800726void TranscodingSessionController::onResourceLost(ClientIdType clientId, SessionIdType sessionId) {
Chong Zhangbc062482020-10-14 16:43:53 -0700727 ALOGI("%s", __FUNCTION__);
728
Chong Zhangeffd8962020-12-02 14:29:09 -0800729 notifyClient(clientId, sessionId, "resource_lost", [=](const SessionKeyType& sessionKey) {
730 if (mResourceLost) {
731 return;
Chong Zhangbc062482020-10-14 16:43:53 -0700732 }
Chong Zhangbc062482020-10-14 16:43:53 -0700733
Chong Zhangeffd8962020-12-02 14:29:09 -0800734 Session* resourceLostSession = &mSessionMap[sessionKey];
Chong Zhanga1320c52020-12-15 14:30:12 -0800735 if (resourceLostSession->getState() != Session::RUNNING) {
Chong Zhangeffd8962020-12-02 14:29:09 -0800736 ALOGW("session %s lost resource but is no longer running",
Chong Zhanga1320c52020-12-15 14:30:12 -0800737 sessionToString(sessionKey).c_str());
Chong Zhangeffd8962020-12-02 14:29:09 -0800738 return;
739 }
740 // If we receive a resource loss event, the transcoder already paused the transcoding,
741 // so we don't need to call onPaused() to pause it. However, we still need to notify
742 // the client and update the session state here.
Chong Zhang457c6892021-02-01 15:34:20 -0800743 setSessionState_l(resourceLostSession, Session::PAUSED);
Chong Zhangeffd8962020-12-02 14:29:09 -0800744 // Notify the client as a paused event.
745 auto clientCallback = resourceLostSession->callback.lock();
746 if (clientCallback != nullptr) {
747 clientCallback->onTranscodingPaused(sessionKey.second);
748 }
Chong Zhang8677f1f2021-01-21 20:37:35 +0000749 if (mResourcePolicy != nullptr) {
750 mResourcePolicy->setPidResourceLost(resourceLostSession->request.clientPid);
751 }
Chong Zhangeffd8962020-12-02 14:29:09 -0800752 mResourceLost = true;
753
754 validateState_l();
755 });
Chong Zhangbc062482020-10-14 16:43:53 -0700756}
757
758void TranscodingSessionController::onTopUidsChanged(const std::unordered_set<uid_t>& uids) {
759 if (uids.empty()) {
760 ALOGW("%s: ignoring empty uids", __FUNCTION__);
761 return;
762 }
763
764 std::string uidStr;
765 for (auto it = uids.begin(); it != uids.end(); it++) {
766 if (!uidStr.empty()) {
767 uidStr += ", ";
768 }
769 uidStr += std::to_string(*it);
770 }
771
772 ALOGD("%s: topUids: size %zu, uids: %s", __FUNCTION__, uids.size(), uidStr.c_str());
773
774 std::scoped_lock lock{mLock};
775
776 moveUidsToTop_l(uids, true /*preserveTopUid*/);
777
778 updateCurrentSession_l();
779
780 validateState_l();
781}
782
783void TranscodingSessionController::onResourceAvailable() {
784 std::scoped_lock lock{mLock};
785
786 if (!mResourceLost) {
787 return;
788 }
789
790 ALOGI("%s", __FUNCTION__);
791
792 mResourceLost = false;
793 updateCurrentSession_l();
794
795 validateState_l();
796}
797
Chong Zhang8677f1f2021-01-21 20:37:35 +0000798void TranscodingSessionController::onThrottlingStarted() {
799 std::scoped_lock lock{mLock};
800
801 if (mThermalThrottling) {
802 return;
803 }
804
805 ALOGI("%s", __FUNCTION__);
806
807 mThermalThrottling = true;
808 updateCurrentSession_l();
809
810 validateState_l();
811}
812
813void TranscodingSessionController::onThrottlingStopped() {
814 std::scoped_lock lock{mLock};
815
816 if (!mThermalThrottling) {
817 return;
818 }
819
820 ALOGI("%s", __FUNCTION__);
821
822 mThermalThrottling = false;
823 updateCurrentSession_l();
824
825 validateState_l();
826}
827
Chong Zhangbc062482020-10-14 16:43:53 -0700828void TranscodingSessionController::validateState_l() {
829#ifdef VALIDATE_STATE
830 LOG_ALWAYS_FATAL_IF(mSessionQueues.count(OFFLINE_UID) != 1,
831 "mSessionQueues offline queue number is not 1");
832 LOG_ALWAYS_FATAL_IF(*mOfflineUidIterator != OFFLINE_UID,
833 "mOfflineUidIterator not pointing to offline uid");
834 LOG_ALWAYS_FATAL_IF(mUidSortedList.size() != mSessionQueues.size(),
835 "mUidList and mSessionQueues size mismatch");
836
837 int32_t totalSessions = 0;
838 for (auto uid : mUidSortedList) {
839 LOG_ALWAYS_FATAL_IF(mSessionQueues.count(uid) != 1,
840 "mSessionQueues count for uid %d is not 1", uid);
841 for (auto& sessionKey : mSessionQueues[uid]) {
842 LOG_ALWAYS_FATAL_IF(mSessionMap.count(sessionKey) != 1,
843 "mSessions count for session %s is not 1",
844 sessionToString(sessionKey).c_str());
845 }
846
847 totalSessions += mSessionQueues[uid].size();
848 }
849 LOG_ALWAYS_FATAL_IF(mSessionMap.size() != totalSessions,
850 "mSessions size doesn't match total sessions counted from uid queues");
851#endif // VALIDATE_STATE
852}
853
854} // namespace android