blob: 54357bd1eb1d909b24bcc28b1f345cf4258751d2 [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,
Linus Nilssona99f4042021-02-25 15:49:43 -0800375 topSession->request, topSession->callingUid,
376 topSession->callback.lock());
Chong Zhanga1320c52020-12-15 14:30:12 -0800377 } else if (topSession->getState() == Session::PAUSED) {
Chong Zhangbc062482020-10-14 16:43:53 -0700378 mTranscoder->resume(topSession->key.first, topSession->key.second,
Linus Nilssona99f4042021-02-25 15:49:43 -0800379 topSession->request, topSession->callingUid,
380 topSession->callback.lock());
Chong Zhangbc062482020-10-14 16:43:53 -0700381 }
Chong Zhang457c6892021-02-01 15:34:20 -0800382 setSessionState_l(topSession, Session::RUNNING);
Chong Zhangbc062482020-10-14 16:43:53 -0700383 }
384 }
385 mCurrentSession = topSession;
386}
387
Chong Zhanga1320c52020-12-15 14:30:12 -0800388void TranscodingSessionController::removeSession_l(const SessionKeyType& sessionKey,
389 Session::State finalState) {
Chong Zhangbc062482020-10-14 16:43:53 -0700390 ALOGV("%s: session %s", __FUNCTION__, sessionToString(sessionKey).c_str());
391
392 if (mSessionMap.count(sessionKey) == 0) {
393 ALOGE("session %s doesn't exist", sessionToString(sessionKey).c_str());
394 return;
395 }
396
397 // Remove session from uid's queue.
Linus Nilssona99f4042021-02-25 15:49:43 -0800398 const uid_t uid = mSessionMap[sessionKey].clientUid;
Chong Zhangbc062482020-10-14 16:43:53 -0700399 SessionQueueType& sessionQueue = mSessionQueues[uid];
400 auto it = std::find(sessionQueue.begin(), sessionQueue.end(), sessionKey);
401 if (it == sessionQueue.end()) {
402 ALOGE("couldn't find session %s in queue for uid %d", sessionToString(sessionKey).c_str(),
403 uid);
404 return;
405 }
406 sessionQueue.erase(it);
407
408 // If this is the last session in a real-time queue, remove this uid's queue.
409 if (uid != OFFLINE_UID && sessionQueue.empty()) {
410 mUidSortedList.remove(uid);
411 mSessionQueues.erase(uid);
412 mUidPolicy->unregisterMonitorUid(uid);
413
414 std::unordered_set<uid_t> topUids = mUidPolicy->getTopUids();
415 moveUidsToTop_l(topUids, false /*preserveTopUid*/);
416 }
417
418 // Clear current session.
419 if (mCurrentSession == &mSessionMap[sessionKey]) {
420 mCurrentSession = nullptr;
421 }
422
Chong Zhang457c6892021-02-01 15:34:20 -0800423 setSessionState_l(&mSessionMap[sessionKey], finalState);
Chong Zhanga1320c52020-12-15 14:30:12 -0800424 mSessionHistory.push_back(mSessionMap[sessionKey]);
425 if (mSessionHistory.size() > kSessionHistoryMax) {
426 mSessionHistory.erase(mSessionHistory.begin());
427 }
428
Chong Zhangbc062482020-10-14 16:43:53 -0700429 // Remove session from session map.
430 mSessionMap.erase(sessionKey);
431}
432
433/**
434 * Moves the set of uids to the front of mUidSortedList (which is used to pick
435 * the next session to run).
436 *
437 * This is called when 1) we received a onTopUidsChanged() callback from UidPolicy,
438 * or 2) we removed the session queue for a uid because it becomes empty.
439 *
440 * In case of 1), if there are multiple uids in the set, and the current front
441 * uid in mUidSortedList is still in the set, we try to keep that uid at front
442 * so that current session run is not interrupted. (This is not a concern for case 2)
443 * because the queue for a uid was just removed entirely.)
444 */
445void TranscodingSessionController::moveUidsToTop_l(const std::unordered_set<uid_t>& uids,
446 bool preserveTopUid) {
447 // If uid set is empty, nothing to do. Do not change the queue status.
448 if (uids.empty()) {
449 return;
450 }
451
452 // Save the current top uid.
453 uid_t curTopUid = *mUidSortedList.begin();
454 bool pushCurTopToFront = false;
455 int32_t numUidsMoved = 0;
456
457 // Go through the sorted uid list once, and move the ones in top set to front.
458 for (auto it = mUidSortedList.begin(); it != mUidSortedList.end();) {
459 uid_t uid = *it;
460
461 if (uid != OFFLINE_UID && uids.count(uid) > 0) {
462 it = mUidSortedList.erase(it);
463
464 // If this is the top we're preserving, don't push it here, push
465 // it after the for-loop.
466 if (uid == curTopUid && preserveTopUid) {
467 pushCurTopToFront = true;
468 } else {
469 mUidSortedList.push_front(uid);
470 }
471
472 // If we found all uids in the set, break out.
473 if (++numUidsMoved == uids.size()) {
474 break;
475 }
476 } else {
477 ++it;
478 }
479 }
480
481 if (pushCurTopToFront) {
482 mUidSortedList.push_front(curTopUid);
483 }
484}
485
486bool TranscodingSessionController::submit(
Linus Nilssona99f4042021-02-25 15:49:43 -0800487 ClientIdType clientId, SessionIdType sessionId, uid_t callingUid, uid_t clientUid,
Chong Zhangbc062482020-10-14 16:43:53 -0700488 const TranscodingRequestParcel& request,
489 const std::weak_ptr<ITranscodingClientCallback>& callback) {
490 SessionKeyType sessionKey = std::make_pair(clientId, sessionId);
491
492 ALOGV("%s: session %s, uid %d, prioirty %d", __FUNCTION__, sessionToString(sessionKey).c_str(),
Linus Nilssona99f4042021-02-25 15:49:43 -0800493 clientUid, (int32_t)request.priority);
Chong Zhangbc062482020-10-14 16:43:53 -0700494
495 std::scoped_lock lock{mLock};
496
497 if (mSessionMap.count(sessionKey) > 0) {
498 ALOGE("session %s already exists", sessionToString(sessionKey).c_str());
499 return false;
500 }
501
Chong Zhangcf3f8ee2020-10-29 18:38:37 -0700502 // Add the uid package name to the store of package names we already know.
Linus Nilssona99f4042021-02-25 15:49:43 -0800503 if (mUidPackageNames.count(clientUid) == 0) {
504 mUidPackageNames.emplace(clientUid, request.clientPackageName);
Chong Zhangcf3f8ee2020-10-29 18:38:37 -0700505 }
506
Chong Zhangbc062482020-10-14 16:43:53 -0700507 // TODO(chz): only support offline vs real-time for now. All kUnspecified sessions
508 // go to offline queue.
509 if (request.priority == TranscodingSessionPriority::kUnspecified) {
Linus Nilssona99f4042021-02-25 15:49:43 -0800510 clientUid = OFFLINE_UID;
Chong Zhangbc062482020-10-14 16:43:53 -0700511 }
512
513 // Add session to session map.
514 mSessionMap[sessionKey].key = sessionKey;
Linus Nilssona99f4042021-02-25 15:49:43 -0800515 mSessionMap[sessionKey].clientUid = clientUid;
516 mSessionMap[sessionKey].callingUid = callingUid;
Chong Zhangbc062482020-10-14 16:43:53 -0700517 mSessionMap[sessionKey].lastProgress = 0;
Chong Zhanga1320c52020-12-15 14:30:12 -0800518 mSessionMap[sessionKey].pauseCount = 0;
Chong Zhangbc062482020-10-14 16:43:53 -0700519 mSessionMap[sessionKey].request = request;
520 mSessionMap[sessionKey].callback = callback;
Chong Zhang457c6892021-02-01 15:34:20 -0800521 setSessionState_l(&mSessionMap[sessionKey], Session::NOT_STARTED);
Chong Zhangbc062482020-10-14 16:43:53 -0700522
523 // If it's an offline session, the queue was already added in constructor.
524 // If it's a real-time sessions, check if a queue is already present for the uid,
525 // and add a new queue if needed.
Linus Nilssona99f4042021-02-25 15:49:43 -0800526 if (clientUid != OFFLINE_UID) {
527 if (mSessionQueues.count(clientUid) == 0) {
528 mUidPolicy->registerMonitorUid(clientUid);
529 if (mUidPolicy->isUidOnTop(clientUid)) {
530 mUidSortedList.push_front(clientUid);
Chong Zhangbc062482020-10-14 16:43:53 -0700531 } else {
532 // Shouldn't be submitting real-time requests from non-top app,
533 // put it in front of the offline queue.
Linus Nilssona99f4042021-02-25 15:49:43 -0800534 mUidSortedList.insert(mOfflineUidIterator, clientUid);
Chong Zhangbc062482020-10-14 16:43:53 -0700535 }
Linus Nilssona99f4042021-02-25 15:49:43 -0800536 } else if (clientUid != *mUidSortedList.begin()) {
537 if (mUidPolicy->isUidOnTop(clientUid)) {
538 mUidSortedList.remove(clientUid);
539 mUidSortedList.push_front(clientUid);
Chong Zhangbc062482020-10-14 16:43:53 -0700540 }
541 }
542 }
543 // Append this session to the uid's queue.
Linus Nilssona99f4042021-02-25 15:49:43 -0800544 mSessionQueues[clientUid].push_back(sessionKey);
Chong Zhangbc062482020-10-14 16:43:53 -0700545
546 updateCurrentSession_l();
547
548 validateState_l();
549 return true;
550}
551
552bool TranscodingSessionController::cancel(ClientIdType clientId, SessionIdType sessionId) {
553 SessionKeyType sessionKey = std::make_pair(clientId, sessionId);
554
555 ALOGV("%s: session %s", __FUNCTION__, sessionToString(sessionKey).c_str());
556
557 std::list<SessionKeyType> sessionsToRemove;
558
559 std::scoped_lock lock{mLock};
560
561 if (sessionId < 0) {
562 for (auto it = mSessionMap.begin(); it != mSessionMap.end(); ++it) {
Linus Nilssona99f4042021-02-25 15:49:43 -0800563 if (it->first.first == clientId && it->second.clientUid != OFFLINE_UID) {
Chong Zhangbc062482020-10-14 16:43:53 -0700564 sessionsToRemove.push_back(it->first);
565 }
566 }
567 } else {
568 if (mSessionMap.count(sessionKey) == 0) {
569 ALOGE("session %s doesn't exist", sessionToString(sessionKey).c_str());
570 return false;
571 }
572 sessionsToRemove.push_back(sessionKey);
573 }
574
575 for (auto it = sessionsToRemove.begin(); it != sessionsToRemove.end(); ++it) {
576 // If the session has ever been started, stop it now.
577 // Note that stop() is needed even if the session is currently paused. This instructs
578 // the transcoder to discard any states for the session, otherwise the states may
579 // never be discarded.
Chong Zhanga1320c52020-12-15 14:30:12 -0800580 if (mSessionMap[*it].getState() != Session::NOT_STARTED) {
Chong Zhangbc062482020-10-14 16:43:53 -0700581 mTranscoder->stop(it->first, it->second);
582 }
583
584 // Remove the session.
Chong Zhanga1320c52020-12-15 14:30:12 -0800585 removeSession_l(*it, Session::CANCELED);
Chong Zhangbc062482020-10-14 16:43:53 -0700586 }
587
588 // Start next session.
589 updateCurrentSession_l();
590
591 validateState_l();
592 return true;
593}
594
595bool TranscodingSessionController::getSession(ClientIdType clientId, SessionIdType sessionId,
596 TranscodingRequestParcel* request) {
597 SessionKeyType sessionKey = std::make_pair(clientId, sessionId);
598
599 std::scoped_lock lock{mLock};
600
601 if (mSessionMap.count(sessionKey) == 0) {
602 ALOGE("session %s doesn't exist", sessionToString(sessionKey).c_str());
603 return false;
604 }
605
606 *(TranscodingRequest*)request = mSessionMap[sessionKey].request;
607 return true;
608}
609
610void TranscodingSessionController::notifyClient(ClientIdType clientId, SessionIdType sessionId,
611 const char* reason,
612 std::function<void(const SessionKeyType&)> func) {
613 SessionKeyType sessionKey = std::make_pair(clientId, sessionId);
614
615 std::scoped_lock lock{mLock};
616
617 if (mSessionMap.count(sessionKey) == 0) {
618 ALOGW("%s: ignoring %s for session %s that doesn't exist", __FUNCTION__, reason,
619 sessionToString(sessionKey).c_str());
620 return;
621 }
622
623 // Only ignore if session was never started. In particular, propagate the status
624 // to client if the session is paused. Transcoder could have posted finish when
625 // we're pausing it, and the finish arrived after we changed current session.
Chong Zhanga1320c52020-12-15 14:30:12 -0800626 if (mSessionMap[sessionKey].getState() == Session::NOT_STARTED) {
Chong Zhangbc062482020-10-14 16:43:53 -0700627 ALOGW("%s: ignoring %s for session %s that was never started", __FUNCTION__, reason,
628 sessionToString(sessionKey).c_str());
629 return;
630 }
631
632 ALOGV("%s: session %s %s", __FUNCTION__, sessionToString(sessionKey).c_str(), reason);
633 func(sessionKey);
634}
635
636void TranscodingSessionController::onStarted(ClientIdType clientId, SessionIdType sessionId) {
637 notifyClient(clientId, sessionId, "started", [=](const SessionKeyType& sessionKey) {
638 auto callback = mSessionMap[sessionKey].callback.lock();
639 if (callback != nullptr) {
640 callback->onTranscodingStarted(sessionId);
641 }
642 });
643}
644
645void TranscodingSessionController::onPaused(ClientIdType clientId, SessionIdType sessionId) {
646 notifyClient(clientId, sessionId, "paused", [=](const SessionKeyType& sessionKey) {
647 auto callback = mSessionMap[sessionKey].callback.lock();
648 if (callback != nullptr) {
649 callback->onTranscodingPaused(sessionId);
650 }
651 });
652}
653
654void TranscodingSessionController::onResumed(ClientIdType clientId, SessionIdType sessionId) {
655 notifyClient(clientId, sessionId, "resumed", [=](const SessionKeyType& sessionKey) {
656 auto callback = mSessionMap[sessionKey].callback.lock();
657 if (callback != nullptr) {
658 callback->onTranscodingResumed(sessionId);
659 }
660 });
661}
662
663void TranscodingSessionController::onFinish(ClientIdType clientId, SessionIdType sessionId) {
664 notifyClient(clientId, sessionId, "finish", [=](const SessionKeyType& sessionKey) {
665 {
666 auto clientCallback = mSessionMap[sessionKey].callback.lock();
667 if (clientCallback != nullptr) {
668 clientCallback->onTranscodingFinished(
669 sessionId, TranscodingResultParcel({sessionId, -1 /*actualBitrateBps*/,
670 std::nullopt /*sessionStats*/}));
671 }
672 }
673
674 // Remove the session.
Chong Zhanga1320c52020-12-15 14:30:12 -0800675 removeSession_l(sessionKey, Session::FINISHED);
Chong Zhangbc062482020-10-14 16:43:53 -0700676
677 // Start next session.
678 updateCurrentSession_l();
679
680 validateState_l();
681 });
682}
683
684void TranscodingSessionController::onError(ClientIdType clientId, SessionIdType sessionId,
685 TranscodingErrorCode err) {
686 notifyClient(clientId, sessionId, "error", [=](const SessionKeyType& sessionKey) {
Chong Zhang457c6892021-02-01 15:34:20 -0800687 if (err == TranscodingErrorCode::kWatchdogTimeout) {
688 // Abandon the transcoder, as its handler thread might be stuck in some call to
689 // MediaTranscoder altogether, and may not be able to handle any new tasks.
690 mTranscoder->stop(clientId, sessionId, true /*abandon*/);
691 // Clear the last ref count before we create new transcoder.
692 mTranscoder = nullptr;
693 mTranscoder = mTranscoderFactory(shared_from_this(), kTranscoderHeartBeatIntervalUs);
694 }
695
Chong Zhangbc062482020-10-14 16:43:53 -0700696 {
697 auto clientCallback = mSessionMap[sessionKey].callback.lock();
698 if (clientCallback != nullptr) {
699 clientCallback->onTranscodingFailed(sessionId, err);
700 }
701 }
702
703 // Remove the session.
Chong Zhanga1320c52020-12-15 14:30:12 -0800704 removeSession_l(sessionKey, Session::ERROR);
Chong Zhangbc062482020-10-14 16:43:53 -0700705
706 // Start next session.
707 updateCurrentSession_l();
708
709 validateState_l();
710 });
711}
712
713void TranscodingSessionController::onProgressUpdate(ClientIdType clientId, SessionIdType sessionId,
714 int32_t progress) {
715 notifyClient(clientId, sessionId, "progress", [=](const SessionKeyType& sessionKey) {
716 auto callback = mSessionMap[sessionKey].callback.lock();
717 if (callback != nullptr) {
718 callback->onProgressUpdate(sessionId, progress);
719 }
720 mSessionMap[sessionKey].lastProgress = progress;
721 });
722}
723
Chong Zhang457c6892021-02-01 15:34:20 -0800724void TranscodingSessionController::onHeartBeat(ClientIdType clientId, SessionIdType sessionId) {
725 notifyClient(clientId, sessionId, "heart-beat",
726 [=](const SessionKeyType& /*sessionKey*/) { mWatchdog->keepAlive(); });
727}
728
Chong Zhangeffd8962020-12-02 14:29:09 -0800729void TranscodingSessionController::onResourceLost(ClientIdType clientId, SessionIdType sessionId) {
Chong Zhangbc062482020-10-14 16:43:53 -0700730 ALOGI("%s", __FUNCTION__);
731
Chong Zhangeffd8962020-12-02 14:29:09 -0800732 notifyClient(clientId, sessionId, "resource_lost", [=](const SessionKeyType& sessionKey) {
733 if (mResourceLost) {
734 return;
Chong Zhangbc062482020-10-14 16:43:53 -0700735 }
Chong Zhangbc062482020-10-14 16:43:53 -0700736
Chong Zhangeffd8962020-12-02 14:29:09 -0800737 Session* resourceLostSession = &mSessionMap[sessionKey];
Chong Zhanga1320c52020-12-15 14:30:12 -0800738 if (resourceLostSession->getState() != Session::RUNNING) {
Chong Zhangeffd8962020-12-02 14:29:09 -0800739 ALOGW("session %s lost resource but is no longer running",
Chong Zhanga1320c52020-12-15 14:30:12 -0800740 sessionToString(sessionKey).c_str());
Chong Zhangeffd8962020-12-02 14:29:09 -0800741 return;
742 }
743 // If we receive a resource loss event, the transcoder already paused the transcoding,
744 // so we don't need to call onPaused() to pause it. However, we still need to notify
745 // the client and update the session state here.
Chong Zhang457c6892021-02-01 15:34:20 -0800746 setSessionState_l(resourceLostSession, Session::PAUSED);
Chong Zhangeffd8962020-12-02 14:29:09 -0800747 // Notify the client as a paused event.
748 auto clientCallback = resourceLostSession->callback.lock();
749 if (clientCallback != nullptr) {
750 clientCallback->onTranscodingPaused(sessionKey.second);
751 }
Chong Zhang8677f1f2021-01-21 20:37:35 +0000752 if (mResourcePolicy != nullptr) {
753 mResourcePolicy->setPidResourceLost(resourceLostSession->request.clientPid);
754 }
Chong Zhangeffd8962020-12-02 14:29:09 -0800755 mResourceLost = true;
756
757 validateState_l();
758 });
Chong Zhangbc062482020-10-14 16:43:53 -0700759}
760
761void TranscodingSessionController::onTopUidsChanged(const std::unordered_set<uid_t>& uids) {
762 if (uids.empty()) {
763 ALOGW("%s: ignoring empty uids", __FUNCTION__);
764 return;
765 }
766
767 std::string uidStr;
768 for (auto it = uids.begin(); it != uids.end(); it++) {
769 if (!uidStr.empty()) {
770 uidStr += ", ";
771 }
772 uidStr += std::to_string(*it);
773 }
774
775 ALOGD("%s: topUids: size %zu, uids: %s", __FUNCTION__, uids.size(), uidStr.c_str());
776
777 std::scoped_lock lock{mLock};
778
779 moveUidsToTop_l(uids, true /*preserveTopUid*/);
780
781 updateCurrentSession_l();
782
783 validateState_l();
784}
785
786void TranscodingSessionController::onResourceAvailable() {
787 std::scoped_lock lock{mLock};
788
789 if (!mResourceLost) {
790 return;
791 }
792
793 ALOGI("%s", __FUNCTION__);
794
795 mResourceLost = false;
796 updateCurrentSession_l();
797
798 validateState_l();
799}
800
Chong Zhang8677f1f2021-01-21 20:37:35 +0000801void TranscodingSessionController::onThrottlingStarted() {
802 std::scoped_lock lock{mLock};
803
804 if (mThermalThrottling) {
805 return;
806 }
807
808 ALOGI("%s", __FUNCTION__);
809
810 mThermalThrottling = true;
811 updateCurrentSession_l();
812
813 validateState_l();
814}
815
816void TranscodingSessionController::onThrottlingStopped() {
817 std::scoped_lock lock{mLock};
818
819 if (!mThermalThrottling) {
820 return;
821 }
822
823 ALOGI("%s", __FUNCTION__);
824
825 mThermalThrottling = false;
826 updateCurrentSession_l();
827
828 validateState_l();
829}
830
Chong Zhangbc062482020-10-14 16:43:53 -0700831void TranscodingSessionController::validateState_l() {
832#ifdef VALIDATE_STATE
833 LOG_ALWAYS_FATAL_IF(mSessionQueues.count(OFFLINE_UID) != 1,
834 "mSessionQueues offline queue number is not 1");
835 LOG_ALWAYS_FATAL_IF(*mOfflineUidIterator != OFFLINE_UID,
836 "mOfflineUidIterator not pointing to offline uid");
837 LOG_ALWAYS_FATAL_IF(mUidSortedList.size() != mSessionQueues.size(),
838 "mUidList and mSessionQueues size mismatch");
839
840 int32_t totalSessions = 0;
841 for (auto uid : mUidSortedList) {
842 LOG_ALWAYS_FATAL_IF(mSessionQueues.count(uid) != 1,
843 "mSessionQueues count for uid %d is not 1", uid);
844 for (auto& sessionKey : mSessionQueues[uid]) {
845 LOG_ALWAYS_FATAL_IF(mSessionMap.count(sessionKey) != 1,
846 "mSessions count for session %s is not 1",
847 sessionToString(sessionKey).c_str());
848 }
849
850 totalSessions += mSessionQueues[uid].size();
851 }
852 LOG_ALWAYS_FATAL_IF(mSessionMap.size() != totalSessions,
853 "mSessions size doesn't match total sessions counted from uid queues");
854#endif // VALIDATE_STATE
855}
856
857} // namespace android