blob: b77a3a425be8263250e605ea18cf8edf080cfe4d [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
27#include <utility>
28
29namespace android {
30
31static_assert((SessionIdType)-1 < 0, "SessionIdType should be signed");
32
33constexpr static uid_t OFFLINE_UID = -1;
Chong Zhanga1320c52020-12-15 14:30:12 -080034constexpr static size_t kSessionHistoryMax = 100;
Chong Zhangbc062482020-10-14 16:43:53 -070035
36//static
37String8 TranscodingSessionController::sessionToString(const SessionKeyType& sessionKey) {
38 return String8::format("{client:%lld, session:%d}", (long long)sessionKey.first,
39 sessionKey.second);
40}
41
42//static
43const char* TranscodingSessionController::sessionStateToString(const Session::State sessionState) {
44 switch (sessionState) {
45 case Session::State::NOT_STARTED:
46 return "NOT_STARTED";
47 case Session::State::RUNNING:
48 return "RUNNING";
49 case Session::State::PAUSED:
50 return "PAUSED";
Chong Zhanga1320c52020-12-15 14:30:12 -080051 case Session::State::FINISHED:
52 return "FINISHED";
53 case Session::State::CANCELED:
54 return "CANCELED";
55 case Session::State::ERROR:
56 return "ERROR";
Chong Zhangbc062482020-10-14 16:43:53 -070057 default:
58 break;
59 }
60 return "(unknown)";
61}
62
63TranscodingSessionController::TranscodingSessionController(
64 const std::shared_ptr<TranscoderInterface>& transcoder,
65 const std::shared_ptr<UidPolicyInterface>& uidPolicy,
66 const std::shared_ptr<ResourcePolicyInterface>& resourcePolicy)
67 : mTranscoder(transcoder),
68 mUidPolicy(uidPolicy),
69 mResourcePolicy(resourcePolicy),
70 mCurrentSession(nullptr),
71 mResourceLost(false) {
72 // Only push empty offline queue initially. Realtime queues are added when requests come in.
73 mUidSortedList.push_back(OFFLINE_UID);
74 mOfflineUidIterator = mUidSortedList.begin();
75 mSessionQueues.emplace(OFFLINE_UID, SessionQueueType());
Chong Zhangcf3f8ee2020-10-29 18:38:37 -070076 mUidPackageNames[OFFLINE_UID] = "(offline)";
Chong Zhangbc062482020-10-14 16:43:53 -070077}
78
79TranscodingSessionController::~TranscodingSessionController() {}
80
Chong Zhanga1320c52020-12-15 14:30:12 -080081void TranscodingSessionController::dumpSession_l(const Session& session, String8& result,
82 bool closedSession) {
83 const size_t SIZE = 256;
84 char buffer[SIZE];
85 const TranscodingRequestParcel& request = session.request;
86 snprintf(buffer, SIZE, " Session: %s, %s, %d%%\n", sessionToString(session.key).c_str(),
87 sessionStateToString(session.getState()), session.lastProgress);
88 result.append(buffer);
89 snprintf(buffer, SIZE, " pkg: %s\n", request.clientPackageName.c_str());
90 result.append(buffer);
91 snprintf(buffer, SIZE, " src: %s\n", request.sourceFilePath.c_str());
92 result.append(buffer);
93 snprintf(buffer, SIZE, " dst: %s\n", request.destinationFilePath.c_str());
94 result.append(buffer);
95
96 if (closedSession) {
97 snprintf(buffer, SIZE,
98 " waiting: %.1fs, running: %.1fs, paused: %.1fs, paused count: %d\n",
99 session.waitingTime.count() / 1000000.0f, session.runningTime.count() / 1000000.0f,
100 session.pausedTime.count() / 1000000.0f, session.pauseCount);
101 result.append(buffer);
102 }
103}
104
Chong Zhangbc062482020-10-14 16:43:53 -0700105void TranscodingSessionController::dumpAllSessions(int fd, const Vector<String16>& args __unused) {
106 String8 result;
107
108 const size_t SIZE = 256;
109 char buffer[SIZE];
110 std::scoped_lock lock{mLock};
111
Chong Zhanga1320c52020-12-15 14:30:12 -0800112 snprintf(buffer, SIZE, "\n========== Dumping live sessions queues =========\n");
Chong Zhangbc062482020-10-14 16:43:53 -0700113 result.append(buffer);
114 snprintf(buffer, SIZE, " Total num of Sessions: %zu\n", mSessionMap.size());
115 result.append(buffer);
116
117 std::vector<int32_t> uids(mUidSortedList.begin(), mUidSortedList.end());
Chong Zhangbc062482020-10-14 16:43:53 -0700118
119 for (int32_t i = 0; i < uids.size(); i++) {
120 const uid_t uid = uids[i];
121
122 if (mSessionQueues[uid].empty()) {
123 continue;
124 }
Chong Zhanga1320c52020-12-15 14:30:12 -0800125 snprintf(buffer, SIZE, " uid: %d, pkg: %s\n", uid,
Chong Zhangcf3f8ee2020-10-29 18:38:37 -0700126 mUidPackageNames.count(uid) > 0 ? mUidPackageNames[uid].c_str() : "(unknown)");
Chong Zhangbc062482020-10-14 16:43:53 -0700127 result.append(buffer);
128 snprintf(buffer, SIZE, " Num of sessions: %zu\n", mSessionQueues[uid].size());
129 result.append(buffer);
130 for (auto& sessionKey : mSessionQueues[uid]) {
131 auto sessionIt = mSessionMap.find(sessionKey);
132 if (sessionIt == mSessionMap.end()) {
133 snprintf(buffer, SIZE, "Failed to look up Session %s \n",
134 sessionToString(sessionKey).c_str());
135 result.append(buffer);
136 continue;
137 }
Chong Zhanga1320c52020-12-15 14:30:12 -0800138 dumpSession_l(sessionIt->second, result);
Chong Zhangbc062482020-10-14 16:43:53 -0700139 }
140 }
141
Chong Zhanga1320c52020-12-15 14:30:12 -0800142 snprintf(buffer, SIZE, "\n========== Dumping past sessions =========\n");
143 result.append(buffer);
144 for (auto &session : mSessionHistory) {
145 dumpSession_l(session, result, true /*closedSession*/);
146 }
147
Chong Zhangbc062482020-10-14 16:43:53 -0700148 write(fd, result.string(), result.size());
149}
150
151TranscodingSessionController::Session* TranscodingSessionController::getTopSession_l() {
152 if (mSessionMap.empty()) {
153 return nullptr;
154 }
155 uid_t topUid = *mUidSortedList.begin();
156 SessionKeyType topSessionKey = *mSessionQueues[topUid].begin();
157 return &mSessionMap[topSessionKey];
158}
159
Chong Zhanga1320c52020-12-15 14:30:12 -0800160void TranscodingSessionController::Session::setState(Session::State newState) {
161 if (state == newState) {
162 return;
163 }
164 auto nowTime = std::chrono::system_clock::now();
165 if (state != INVALID) {
166 std::chrono::microseconds elapsedTime = (nowTime - stateEnterTime);
167 switch (state) {
168 case PAUSED:
169 pausedTime = pausedTime + elapsedTime;
170 break;
171 case RUNNING:
172 runningTime = runningTime + elapsedTime;
173 break;
174 case NOT_STARTED:
175 waitingTime = waitingTime + elapsedTime;
176 break;
177 default:
178 break;
179 }
180 }
181 if (newState == PAUSED) {
182 pauseCount++;
183 }
184 stateEnterTime = nowTime;
185 state = newState;
186}
187
Chong Zhangbc062482020-10-14 16:43:53 -0700188void TranscodingSessionController::updateCurrentSession_l() {
189 Session* topSession = getTopSession_l();
190 Session* curSession = mCurrentSession;
191 ALOGV("updateCurrentSession: topSession is %s, curSession is %s",
192 topSession == nullptr ? "null" : sessionToString(topSession->key).c_str(),
193 curSession == nullptr ? "null" : sessionToString(curSession->key).c_str());
194
195 // If we found a topSession that should be run, and it's not already running,
196 // take some actions to ensure it's running.
197 if (topSession != nullptr &&
Chong Zhanga1320c52020-12-15 14:30:12 -0800198 (topSession != curSession || topSession->getState() != Session::RUNNING)) {
Chong Zhangbc062482020-10-14 16:43:53 -0700199 // If another session is currently running, pause it first.
Chong Zhanga1320c52020-12-15 14:30:12 -0800200 if (curSession != nullptr && curSession->getState() == Session::RUNNING) {
Chong Zhangbc062482020-10-14 16:43:53 -0700201 mTranscoder->pause(curSession->key.first, curSession->key.second);
Chong Zhanga1320c52020-12-15 14:30:12 -0800202 curSession->setState(Session::PAUSED);
Chong Zhangbc062482020-10-14 16:43:53 -0700203 }
204 // If we are not experiencing resource loss, we can start or resume
205 // the topSession now.
206 if (!mResourceLost) {
Chong Zhanga1320c52020-12-15 14:30:12 -0800207 if (topSession->getState() == Session::NOT_STARTED) {
Chong Zhangbc062482020-10-14 16:43:53 -0700208 mTranscoder->start(topSession->key.first, topSession->key.second,
209 topSession->request, topSession->callback.lock());
Chong Zhanga1320c52020-12-15 14:30:12 -0800210 } else if (topSession->getState() == Session::PAUSED) {
Chong Zhangbc062482020-10-14 16:43:53 -0700211 mTranscoder->resume(topSession->key.first, topSession->key.second,
212 topSession->request, topSession->callback.lock());
213 }
Chong Zhanga1320c52020-12-15 14:30:12 -0800214 topSession->setState(Session::RUNNING);
Chong Zhangbc062482020-10-14 16:43:53 -0700215 }
216 }
217 mCurrentSession = topSession;
218}
219
Chong Zhanga1320c52020-12-15 14:30:12 -0800220void TranscodingSessionController::removeSession_l(const SessionKeyType& sessionKey,
221 Session::State finalState) {
Chong Zhangbc062482020-10-14 16:43:53 -0700222 ALOGV("%s: session %s", __FUNCTION__, sessionToString(sessionKey).c_str());
223
224 if (mSessionMap.count(sessionKey) == 0) {
225 ALOGE("session %s doesn't exist", sessionToString(sessionKey).c_str());
226 return;
227 }
228
229 // Remove session from uid's queue.
230 const uid_t uid = mSessionMap[sessionKey].uid;
231 SessionQueueType& sessionQueue = mSessionQueues[uid];
232 auto it = std::find(sessionQueue.begin(), sessionQueue.end(), sessionKey);
233 if (it == sessionQueue.end()) {
234 ALOGE("couldn't find session %s in queue for uid %d", sessionToString(sessionKey).c_str(),
235 uid);
236 return;
237 }
238 sessionQueue.erase(it);
239
240 // If this is the last session in a real-time queue, remove this uid's queue.
241 if (uid != OFFLINE_UID && sessionQueue.empty()) {
242 mUidSortedList.remove(uid);
243 mSessionQueues.erase(uid);
244 mUidPolicy->unregisterMonitorUid(uid);
245
246 std::unordered_set<uid_t> topUids = mUidPolicy->getTopUids();
247 moveUidsToTop_l(topUids, false /*preserveTopUid*/);
248 }
249
250 // Clear current session.
251 if (mCurrentSession == &mSessionMap[sessionKey]) {
252 mCurrentSession = nullptr;
253 }
254
Chong Zhanga1320c52020-12-15 14:30:12 -0800255 mSessionMap[sessionKey].setState(finalState);
256 mSessionHistory.push_back(mSessionMap[sessionKey]);
257 if (mSessionHistory.size() > kSessionHistoryMax) {
258 mSessionHistory.erase(mSessionHistory.begin());
259 }
260
Chong Zhangbc062482020-10-14 16:43:53 -0700261 // Remove session from session map.
262 mSessionMap.erase(sessionKey);
263}
264
265/**
266 * Moves the set of uids to the front of mUidSortedList (which is used to pick
267 * the next session to run).
268 *
269 * This is called when 1) we received a onTopUidsChanged() callback from UidPolicy,
270 * or 2) we removed the session queue for a uid because it becomes empty.
271 *
272 * In case of 1), if there are multiple uids in the set, and the current front
273 * uid in mUidSortedList is still in the set, we try to keep that uid at front
274 * so that current session run is not interrupted. (This is not a concern for case 2)
275 * because the queue for a uid was just removed entirely.)
276 */
277void TranscodingSessionController::moveUidsToTop_l(const std::unordered_set<uid_t>& uids,
278 bool preserveTopUid) {
279 // If uid set is empty, nothing to do. Do not change the queue status.
280 if (uids.empty()) {
281 return;
282 }
283
284 // Save the current top uid.
285 uid_t curTopUid = *mUidSortedList.begin();
286 bool pushCurTopToFront = false;
287 int32_t numUidsMoved = 0;
288
289 // Go through the sorted uid list once, and move the ones in top set to front.
290 for (auto it = mUidSortedList.begin(); it != mUidSortedList.end();) {
291 uid_t uid = *it;
292
293 if (uid != OFFLINE_UID && uids.count(uid) > 0) {
294 it = mUidSortedList.erase(it);
295
296 // If this is the top we're preserving, don't push it here, push
297 // it after the for-loop.
298 if (uid == curTopUid && preserveTopUid) {
299 pushCurTopToFront = true;
300 } else {
301 mUidSortedList.push_front(uid);
302 }
303
304 // If we found all uids in the set, break out.
305 if (++numUidsMoved == uids.size()) {
306 break;
307 }
308 } else {
309 ++it;
310 }
311 }
312
313 if (pushCurTopToFront) {
314 mUidSortedList.push_front(curTopUid);
315 }
316}
317
318bool TranscodingSessionController::submit(
319 ClientIdType clientId, SessionIdType sessionId, uid_t uid,
320 const TranscodingRequestParcel& request,
321 const std::weak_ptr<ITranscodingClientCallback>& callback) {
322 SessionKeyType sessionKey = std::make_pair(clientId, sessionId);
323
324 ALOGV("%s: session %s, uid %d, prioirty %d", __FUNCTION__, sessionToString(sessionKey).c_str(),
325 uid, (int32_t)request.priority);
326
327 std::scoped_lock lock{mLock};
328
329 if (mSessionMap.count(sessionKey) > 0) {
330 ALOGE("session %s already exists", sessionToString(sessionKey).c_str());
331 return false;
332 }
333
Chong Zhangcf3f8ee2020-10-29 18:38:37 -0700334 // Add the uid package name to the store of package names we already know.
335 if (mUidPackageNames.count(uid) == 0) {
336 mUidPackageNames.emplace(uid, request.clientPackageName);
337 }
338
Chong Zhangbc062482020-10-14 16:43:53 -0700339 // TODO(chz): only support offline vs real-time for now. All kUnspecified sessions
340 // go to offline queue.
341 if (request.priority == TranscodingSessionPriority::kUnspecified) {
342 uid = OFFLINE_UID;
343 }
344
345 // Add session to session map.
346 mSessionMap[sessionKey].key = sessionKey;
347 mSessionMap[sessionKey].uid = uid;
Chong Zhangbc062482020-10-14 16:43:53 -0700348 mSessionMap[sessionKey].lastProgress = 0;
Chong Zhanga1320c52020-12-15 14:30:12 -0800349 mSessionMap[sessionKey].pauseCount = 0;
Chong Zhangbc062482020-10-14 16:43:53 -0700350 mSessionMap[sessionKey].request = request;
351 mSessionMap[sessionKey].callback = callback;
Chong Zhanga1320c52020-12-15 14:30:12 -0800352 mSessionMap[sessionKey].setState(Session::NOT_STARTED);
Chong Zhangbc062482020-10-14 16:43:53 -0700353
354 // If it's an offline session, the queue was already added in constructor.
355 // If it's a real-time sessions, check if a queue is already present for the uid,
356 // and add a new queue if needed.
357 if (uid != OFFLINE_UID) {
358 if (mSessionQueues.count(uid) == 0) {
359 mUidPolicy->registerMonitorUid(uid);
360 if (mUidPolicy->isUidOnTop(uid)) {
361 mUidSortedList.push_front(uid);
362 } else {
363 // Shouldn't be submitting real-time requests from non-top app,
364 // put it in front of the offline queue.
365 mUidSortedList.insert(mOfflineUidIterator, uid);
366 }
367 } else if (uid != *mUidSortedList.begin()) {
368 if (mUidPolicy->isUidOnTop(uid)) {
369 mUidSortedList.remove(uid);
370 mUidSortedList.push_front(uid);
371 }
372 }
373 }
374 // Append this session to the uid's queue.
375 mSessionQueues[uid].push_back(sessionKey);
376
377 updateCurrentSession_l();
378
379 validateState_l();
380 return true;
381}
382
383bool TranscodingSessionController::cancel(ClientIdType clientId, SessionIdType sessionId) {
384 SessionKeyType sessionKey = std::make_pair(clientId, sessionId);
385
386 ALOGV("%s: session %s", __FUNCTION__, sessionToString(sessionKey).c_str());
387
388 std::list<SessionKeyType> sessionsToRemove;
389
390 std::scoped_lock lock{mLock};
391
392 if (sessionId < 0) {
393 for (auto it = mSessionMap.begin(); it != mSessionMap.end(); ++it) {
394 if (it->first.first == clientId && it->second.uid != OFFLINE_UID) {
395 sessionsToRemove.push_back(it->first);
396 }
397 }
398 } else {
399 if (mSessionMap.count(sessionKey) == 0) {
400 ALOGE("session %s doesn't exist", sessionToString(sessionKey).c_str());
401 return false;
402 }
403 sessionsToRemove.push_back(sessionKey);
404 }
405
406 for (auto it = sessionsToRemove.begin(); it != sessionsToRemove.end(); ++it) {
407 // If the session has ever been started, stop it now.
408 // Note that stop() is needed even if the session is currently paused. This instructs
409 // the transcoder to discard any states for the session, otherwise the states may
410 // never be discarded.
Chong Zhanga1320c52020-12-15 14:30:12 -0800411 if (mSessionMap[*it].getState() != Session::NOT_STARTED) {
Chong Zhangbc062482020-10-14 16:43:53 -0700412 mTranscoder->stop(it->first, it->second);
413 }
414
415 // Remove the session.
Chong Zhanga1320c52020-12-15 14:30:12 -0800416 removeSession_l(*it, Session::CANCELED);
Chong Zhangbc062482020-10-14 16:43:53 -0700417 }
418
419 // Start next session.
420 updateCurrentSession_l();
421
422 validateState_l();
423 return true;
424}
425
426bool TranscodingSessionController::getSession(ClientIdType clientId, SessionIdType sessionId,
427 TranscodingRequestParcel* request) {
428 SessionKeyType sessionKey = std::make_pair(clientId, sessionId);
429
430 std::scoped_lock lock{mLock};
431
432 if (mSessionMap.count(sessionKey) == 0) {
433 ALOGE("session %s doesn't exist", sessionToString(sessionKey).c_str());
434 return false;
435 }
436
437 *(TranscodingRequest*)request = mSessionMap[sessionKey].request;
438 return true;
439}
440
441void TranscodingSessionController::notifyClient(ClientIdType clientId, SessionIdType sessionId,
442 const char* reason,
443 std::function<void(const SessionKeyType&)> func) {
444 SessionKeyType sessionKey = std::make_pair(clientId, sessionId);
445
446 std::scoped_lock lock{mLock};
447
448 if (mSessionMap.count(sessionKey) == 0) {
449 ALOGW("%s: ignoring %s for session %s that doesn't exist", __FUNCTION__, reason,
450 sessionToString(sessionKey).c_str());
451 return;
452 }
453
454 // Only ignore if session was never started. In particular, propagate the status
455 // to client if the session is paused. Transcoder could have posted finish when
456 // we're pausing it, and the finish arrived after we changed current session.
Chong Zhanga1320c52020-12-15 14:30:12 -0800457 if (mSessionMap[sessionKey].getState() == Session::NOT_STARTED) {
Chong Zhangbc062482020-10-14 16:43:53 -0700458 ALOGW("%s: ignoring %s for session %s that was never started", __FUNCTION__, reason,
459 sessionToString(sessionKey).c_str());
460 return;
461 }
462
463 ALOGV("%s: session %s %s", __FUNCTION__, sessionToString(sessionKey).c_str(), reason);
464 func(sessionKey);
465}
466
467void TranscodingSessionController::onStarted(ClientIdType clientId, SessionIdType sessionId) {
468 notifyClient(clientId, sessionId, "started", [=](const SessionKeyType& sessionKey) {
469 auto callback = mSessionMap[sessionKey].callback.lock();
470 if (callback != nullptr) {
471 callback->onTranscodingStarted(sessionId);
472 }
473 });
474}
475
476void TranscodingSessionController::onPaused(ClientIdType clientId, SessionIdType sessionId) {
477 notifyClient(clientId, sessionId, "paused", [=](const SessionKeyType& sessionKey) {
478 auto callback = mSessionMap[sessionKey].callback.lock();
479 if (callback != nullptr) {
480 callback->onTranscodingPaused(sessionId);
481 }
482 });
483}
484
485void TranscodingSessionController::onResumed(ClientIdType clientId, SessionIdType sessionId) {
486 notifyClient(clientId, sessionId, "resumed", [=](const SessionKeyType& sessionKey) {
487 auto callback = mSessionMap[sessionKey].callback.lock();
488 if (callback != nullptr) {
489 callback->onTranscodingResumed(sessionId);
490 }
491 });
492}
493
494void TranscodingSessionController::onFinish(ClientIdType clientId, SessionIdType sessionId) {
495 notifyClient(clientId, sessionId, "finish", [=](const SessionKeyType& sessionKey) {
496 {
497 auto clientCallback = mSessionMap[sessionKey].callback.lock();
498 if (clientCallback != nullptr) {
499 clientCallback->onTranscodingFinished(
500 sessionId, TranscodingResultParcel({sessionId, -1 /*actualBitrateBps*/,
501 std::nullopt /*sessionStats*/}));
502 }
503 }
504
505 // Remove the session.
Chong Zhanga1320c52020-12-15 14:30:12 -0800506 removeSession_l(sessionKey, Session::FINISHED);
Chong Zhangbc062482020-10-14 16:43:53 -0700507
508 // Start next session.
509 updateCurrentSession_l();
510
511 validateState_l();
512 });
513}
514
515void TranscodingSessionController::onError(ClientIdType clientId, SessionIdType sessionId,
516 TranscodingErrorCode err) {
517 notifyClient(clientId, sessionId, "error", [=](const SessionKeyType& sessionKey) {
518 {
519 auto clientCallback = mSessionMap[sessionKey].callback.lock();
520 if (clientCallback != nullptr) {
521 clientCallback->onTranscodingFailed(sessionId, err);
522 }
523 }
524
525 // Remove the session.
Chong Zhanga1320c52020-12-15 14:30:12 -0800526 removeSession_l(sessionKey, Session::ERROR);
Chong Zhangbc062482020-10-14 16:43:53 -0700527
528 // Start next session.
529 updateCurrentSession_l();
530
531 validateState_l();
532 });
533}
534
535void TranscodingSessionController::onProgressUpdate(ClientIdType clientId, SessionIdType sessionId,
536 int32_t progress) {
537 notifyClient(clientId, sessionId, "progress", [=](const SessionKeyType& sessionKey) {
538 auto callback = mSessionMap[sessionKey].callback.lock();
539 if (callback != nullptr) {
540 callback->onProgressUpdate(sessionId, progress);
541 }
542 mSessionMap[sessionKey].lastProgress = progress;
543 });
544}
545
Chong Zhangeffd8962020-12-02 14:29:09 -0800546void TranscodingSessionController::onResourceLost(ClientIdType clientId, SessionIdType sessionId) {
Chong Zhangbc062482020-10-14 16:43:53 -0700547 ALOGI("%s", __FUNCTION__);
548
Chong Zhangeffd8962020-12-02 14:29:09 -0800549 notifyClient(clientId, sessionId, "resource_lost", [=](const SessionKeyType& sessionKey) {
550 if (mResourceLost) {
551 return;
Chong Zhangbc062482020-10-14 16:43:53 -0700552 }
Chong Zhangbc062482020-10-14 16:43:53 -0700553
Chong Zhangeffd8962020-12-02 14:29:09 -0800554 Session* resourceLostSession = &mSessionMap[sessionKey];
Chong Zhanga1320c52020-12-15 14:30:12 -0800555 if (resourceLostSession->getState() != Session::RUNNING) {
Chong Zhangeffd8962020-12-02 14:29:09 -0800556 ALOGW("session %s lost resource but is no longer running",
Chong Zhanga1320c52020-12-15 14:30:12 -0800557 sessionToString(sessionKey).c_str());
Chong Zhangeffd8962020-12-02 14:29:09 -0800558 return;
559 }
560 // If we receive a resource loss event, the transcoder already paused the transcoding,
561 // so we don't need to call onPaused() to pause it. However, we still need to notify
562 // the client and update the session state here.
Chong Zhanga1320c52020-12-15 14:30:12 -0800563 resourceLostSession->setState(Session::PAUSED);
Chong Zhangeffd8962020-12-02 14:29:09 -0800564 // Notify the client as a paused event.
565 auto clientCallback = resourceLostSession->callback.lock();
566 if (clientCallback != nullptr) {
567 clientCallback->onTranscodingPaused(sessionKey.second);
568 }
569 mResourcePolicy->setPidResourceLost(resourceLostSession->request.clientPid);
570 mResourceLost = true;
571
572 validateState_l();
573 });
Chong Zhangbc062482020-10-14 16:43:53 -0700574}
575
576void TranscodingSessionController::onTopUidsChanged(const std::unordered_set<uid_t>& uids) {
577 if (uids.empty()) {
578 ALOGW("%s: ignoring empty uids", __FUNCTION__);
579 return;
580 }
581
582 std::string uidStr;
583 for (auto it = uids.begin(); it != uids.end(); it++) {
584 if (!uidStr.empty()) {
585 uidStr += ", ";
586 }
587 uidStr += std::to_string(*it);
588 }
589
590 ALOGD("%s: topUids: size %zu, uids: %s", __FUNCTION__, uids.size(), uidStr.c_str());
591
592 std::scoped_lock lock{mLock};
593
594 moveUidsToTop_l(uids, true /*preserveTopUid*/);
595
596 updateCurrentSession_l();
597
598 validateState_l();
599}
600
601void TranscodingSessionController::onResourceAvailable() {
602 std::scoped_lock lock{mLock};
603
604 if (!mResourceLost) {
605 return;
606 }
607
608 ALOGI("%s", __FUNCTION__);
609
610 mResourceLost = false;
611 updateCurrentSession_l();
612
613 validateState_l();
614}
615
616void TranscodingSessionController::validateState_l() {
617#ifdef VALIDATE_STATE
618 LOG_ALWAYS_FATAL_IF(mSessionQueues.count(OFFLINE_UID) != 1,
619 "mSessionQueues offline queue number is not 1");
620 LOG_ALWAYS_FATAL_IF(*mOfflineUidIterator != OFFLINE_UID,
621 "mOfflineUidIterator not pointing to offline uid");
622 LOG_ALWAYS_FATAL_IF(mUidSortedList.size() != mSessionQueues.size(),
623 "mUidList and mSessionQueues size mismatch");
624
625 int32_t totalSessions = 0;
626 for (auto uid : mUidSortedList) {
627 LOG_ALWAYS_FATAL_IF(mSessionQueues.count(uid) != 1,
628 "mSessionQueues count for uid %d is not 1", uid);
629 for (auto& sessionKey : mSessionQueues[uid]) {
630 LOG_ALWAYS_FATAL_IF(mSessionMap.count(sessionKey) != 1,
631 "mSessions count for session %s is not 1",
632 sessionToString(sessionKey).c_str());
633 }
634
635 totalSessions += mSessionQueues[uid].size();
636 }
637 LOG_ALWAYS_FATAL_IF(mSessionMap.size() != totalSessions,
638 "mSessions size doesn't match total sessions counted from uid queues");
639#endif // VALIDATE_STATE
640}
641
642} // namespace android