blob: aeabe0f1609a88327842ad40560f2a31c0078378 [file] [log] [blame]
Chong Zhangbc062482020-10-14 16:43:53 -07001/*
2 * Copyright (C) 2020 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17//#define LOG_NDEBUG 0
18#define LOG_TAG "TranscodingSessionController"
19
20#define VALIDATE_STATE 1
21
22#include <inttypes.h>
23#include <media/TranscodingSessionController.h>
24#include <media/TranscodingUidPolicy.h>
25#include <utils/Log.h>
26
Chong Zhang457c6892021-02-01 15:34:20 -080027#include <thread>
Chong Zhangbc062482020-10-14 16:43:53 -070028#include <utility>
29
30namespace android {
31
32static_assert((SessionIdType)-1 < 0, "SessionIdType should be signed");
33
34constexpr static uid_t OFFLINE_UID = -1;
Chong Zhanga1320c52020-12-15 14:30:12 -080035constexpr static size_t kSessionHistoryMax = 100;
Chong Zhangbc062482020-10-14 16:43:53 -070036
37//static
38String8 TranscodingSessionController::sessionToString(const SessionKeyType& sessionKey) {
39 return String8::format("{client:%lld, session:%d}", (long long)sessionKey.first,
40 sessionKey.second);
41}
42
43//static
44const char* TranscodingSessionController::sessionStateToString(const Session::State sessionState) {
45 switch (sessionState) {
46 case Session::State::NOT_STARTED:
47 return "NOT_STARTED";
48 case Session::State::RUNNING:
49 return "RUNNING";
50 case Session::State::PAUSED:
51 return "PAUSED";
Chong Zhanga1320c52020-12-15 14:30:12 -080052 case Session::State::FINISHED:
53 return "FINISHED";
54 case Session::State::CANCELED:
55 return "CANCELED";
56 case Session::State::ERROR:
57 return "ERROR";
Chong Zhangbc062482020-10-14 16:43:53 -070058 default:
59 break;
60 }
61 return "(unknown)";
62}
63
Chong Zhang457c6892021-02-01 15:34:20 -080064///////////////////////////////////////////////////////////////////////////////
65struct TranscodingSessionController::Watchdog {
66 Watchdog(TranscodingSessionController* owner, int64_t timeoutUs);
67 ~Watchdog();
68
69 // Starts monitoring the session.
70 void start(const SessionKeyType& key);
71 // Stops monitoring the session.
72 void stop();
73 // Signals that the session is still alive. Must be sent at least every mTimeoutUs.
74 // (Timeout will happen if no ping in mTimeoutUs since the last ping.)
75 void keepAlive();
76
77private:
78 void threadLoop();
79 void updateTimer_l();
80
81 TranscodingSessionController* mOwner;
82 const int64_t mTimeoutUs;
83 mutable std::mutex mLock;
84 std::condition_variable mCondition GUARDED_BY(mLock);
85 // Whether watchdog is monitoring a session for timeout.
86 bool mActive GUARDED_BY(mLock);
87 // Whether watchdog is aborted and the monitoring thread should exit.
88 bool mAbort GUARDED_BY(mLock);
89 // When watchdog is active, the next timeout time point.
Chong Zhang87d199c2021-03-01 19:02:18 -080090 std::chrono::steady_clock::time_point mNextTimeoutTime GUARDED_BY(mLock);
Chong Zhang457c6892021-02-01 15:34:20 -080091 // When watchdog is active, the session being watched.
92 SessionKeyType mSessionToWatch GUARDED_BY(mLock);
93 std::thread mThread;
94};
95
Chong Zhang457c6892021-02-01 15:34:20 -080096TranscodingSessionController::Watchdog::Watchdog(TranscodingSessionController* owner,
97 int64_t timeoutUs)
98 : mOwner(owner),
99 mTimeoutUs(timeoutUs),
100 mActive(false),
101 mAbort(false),
102 mThread(&Watchdog::threadLoop, this) {
103 ALOGV("Watchdog CTOR: %p", this);
104}
105
106TranscodingSessionController::Watchdog::~Watchdog() {
107 ALOGV("Watchdog DTOR: %p", this);
108
109 {
110 // Exit the looper thread.
111 std::scoped_lock lock{mLock};
112
113 mAbort = true;
114 mCondition.notify_one();
115 }
116
117 mThread.join();
118 ALOGV("Watchdog DTOR: %p, done.", this);
119}
120
121void TranscodingSessionController::Watchdog::start(const SessionKeyType& key) {
122 std::scoped_lock lock{mLock};
123
124 if (!mActive) {
125 ALOGI("Watchdog start: %s", sessionToString(key).c_str());
126
127 mActive = true;
128 mSessionToWatch = key;
129 updateTimer_l();
130 mCondition.notify_one();
131 }
132}
133
134void TranscodingSessionController::Watchdog::stop() {
135 std::scoped_lock lock{mLock};
136
137 if (mActive) {
138 ALOGI("Watchdog stop: %s", sessionToString(mSessionToWatch).c_str());
139
140 mActive = false;
141 mCondition.notify_one();
142 }
143}
144
145void TranscodingSessionController::Watchdog::keepAlive() {
146 std::scoped_lock lock{mLock};
147
148 if (mActive) {
149 ALOGI("Watchdog keepAlive: %s", sessionToString(mSessionToWatch).c_str());
150
151 updateTimer_l();
152 mCondition.notify_one();
153 }
154}
155
156// updateTimer_l() is only called with lock held.
157void TranscodingSessionController::Watchdog::updateTimer_l() NO_THREAD_SAFETY_ANALYSIS {
158 std::chrono::microseconds timeout(mTimeoutUs);
Chong Zhang87d199c2021-03-01 19:02:18 -0800159 mNextTimeoutTime = std::chrono::steady_clock::now() + timeout;
Chong Zhang457c6892021-02-01 15:34:20 -0800160}
161
162// Unfortunately std::unique_lock is incompatible with -Wthread-safety.
163void TranscodingSessionController::Watchdog::threadLoop() NO_THREAD_SAFETY_ANALYSIS {
164 std::unique_lock<std::mutex> lock{mLock};
165
166 while (!mAbort) {
167 if (!mActive) {
168 mCondition.wait(lock);
169 continue;
170 }
171 // Watchdog active, wait till next timeout time.
172 if (mCondition.wait_until(lock, mNextTimeoutTime) == std::cv_status::timeout) {
173 // If timeout happens, report timeout and deactivate watchdog.
174 mActive = false;
175 // Make a copy of session key, as once we unlock, it could be unprotected.
176 SessionKeyType sessionKey = mSessionToWatch;
177
178 ALOGE("Watchdog timeout: %s", sessionToString(sessionKey).c_str());
179
180 lock.unlock();
181 mOwner->onError(sessionKey.first, sessionKey.second,
182 TranscodingErrorCode::kWatchdogTimeout);
183 lock.lock();
184 }
185 }
186}
187///////////////////////////////////////////////////////////////////////////////
Chong Zhang87d199c2021-03-01 19:02:18 -0800188struct TranscodingSessionController::Pacer {
189 Pacer(const ControllerConfig& config)
190 : mBurstThresholdMs(config.pacerBurstThresholdMs),
191 mBurstCountQuota(config.pacerBurstCountQuota),
192 mBurstTimeQuotaSec(config.pacerBurstTimeQuotaSeconds) {}
193
194 ~Pacer() = default;
195
196 void onSessionCompleted(uid_t uid, std::chrono::microseconds runningTime);
197 bool onSessionStarted(uid_t uid);
198
199private:
200 // Threshold of time between finish/start below which a back-to-back start is counted.
201 int32_t mBurstThresholdMs;
202 // Maximum allowed back-to-back start count.
203 int32_t mBurstCountQuota;
204 // Maximum allowed back-to-back running time.
205 int32_t mBurstTimeQuotaSec;
206
207 struct UidHistoryEntry {
208 std::chrono::steady_clock::time_point lastCompletedTime;
209 int32_t burstCount = 0;
210 std::chrono::steady_clock::duration burstDuration{0};
211 };
212 std::map<uid_t, UidHistoryEntry> mUidHistoryMap;
213};
214
215void TranscodingSessionController::Pacer::onSessionCompleted(
216 uid_t uid, std::chrono::microseconds runningTime) {
217 if (mUidHistoryMap.find(uid) == mUidHistoryMap.end()) {
218 mUidHistoryMap.emplace(uid, UidHistoryEntry{});
219 }
220 mUidHistoryMap[uid].lastCompletedTime = std::chrono::steady_clock::now();
221 mUidHistoryMap[uid].burstCount++;
222 mUidHistoryMap[uid].burstDuration += runningTime;
223}
224
225bool TranscodingSessionController::Pacer::onSessionStarted(uid_t uid) {
226 // If uid doesn't exist, this uid has no completed sessions. Skip.
227 if (mUidHistoryMap.find(uid) == mUidHistoryMap.end()) {
228 return true;
229 }
230
231 // TODO: if Thermal throttling or resoure lost happened to occurr between this start
232 // and the previous completion, we should deduct the paused time from the elapsed time.
233 // (Individual session's pause time, on the other hand, doesn't need to be deducted
234 // because it doesn't affect the gap between last completion and the start.
235 auto timeSinceLastComplete =
236 std::chrono::steady_clock::now() - mUidHistoryMap[uid].lastCompletedTime;
237 if (mUidHistoryMap[uid].burstCount >= mBurstCountQuota &&
238 mUidHistoryMap[uid].burstDuration >= std::chrono::seconds(mBurstTimeQuotaSec)) {
239 ALOGW("Pacer: uid %d: over quota, burst count %d, time %lldms", uid,
240 mUidHistoryMap[uid].burstCount, (long long)mUidHistoryMap[uid].burstDuration.count());
241 return false;
242 }
243
244 // If not over quota, allow the session, and reset as long as this is not too close
245 // to previous completion.
246 if (timeSinceLastComplete > std::chrono::milliseconds(mBurstThresholdMs)) {
247 ALOGV("Pacer: uid %d: reset quota", uid);
248 mUidHistoryMap[uid].burstCount = 0;
249 mUidHistoryMap[uid].burstDuration = std::chrono::milliseconds(0);
250 } else {
251 ALOGV("Pacer: uid %d: burst count %d, time %lldms", uid, mUidHistoryMap[uid].burstCount,
252 (long long)mUidHistoryMap[uid].burstDuration.count());
253 }
254
255 return true;
256}
257
258///////////////////////////////////////////////////////////////////////////////
Chong Zhang457c6892021-02-01 15:34:20 -0800259
Chong Zhangbc062482020-10-14 16:43:53 -0700260TranscodingSessionController::TranscodingSessionController(
Chong Zhang457c6892021-02-01 15:34:20 -0800261 const TranscoderFactoryType& transcoderFactory,
Chong Zhangbc062482020-10-14 16:43:53 -0700262 const std::shared_ptr<UidPolicyInterface>& uidPolicy,
Chong Zhang8677f1f2021-01-21 20:37:35 +0000263 const std::shared_ptr<ResourcePolicyInterface>& resourcePolicy,
Chong Zhang87d199c2021-03-01 19:02:18 -0800264 const std::shared_ptr<ThermalPolicyInterface>& thermalPolicy,
265 const ControllerConfig* config)
Chong Zhang457c6892021-02-01 15:34:20 -0800266 : mTranscoderFactory(transcoderFactory),
Chong Zhangbc062482020-10-14 16:43:53 -0700267 mUidPolicy(uidPolicy),
268 mResourcePolicy(resourcePolicy),
Chong Zhang8677f1f2021-01-21 20:37:35 +0000269 mThermalPolicy(thermalPolicy),
Chong Zhangbc062482020-10-14 16:43:53 -0700270 mCurrentSession(nullptr),
271 mResourceLost(false) {
272 // Only push empty offline queue initially. Realtime queues are added when requests come in.
273 mUidSortedList.push_back(OFFLINE_UID);
274 mOfflineUidIterator = mUidSortedList.begin();
275 mSessionQueues.emplace(OFFLINE_UID, SessionQueueType());
Chong Zhangcf3f8ee2020-10-29 18:38:37 -0700276 mUidPackageNames[OFFLINE_UID] = "(offline)";
Chong Zhang8677f1f2021-01-21 20:37:35 +0000277 mThermalThrottling = thermalPolicy->getThrottlingStatus();
Chong Zhang87d199c2021-03-01 19:02:18 -0800278 if (config != nullptr) {
279 mConfig = *config;
280 }
281 mPacer.reset(new Pacer(mConfig));
282 ALOGD("@@@ watchdog %lld, burst count %d, burst time %d, burst threshold %d",
283 (long long)mConfig.watchdogTimeoutUs, mConfig.pacerBurstCountQuota,
284 mConfig.pacerBurstTimeQuotaSeconds, mConfig.pacerBurstThresholdMs);
Chong Zhangbc062482020-10-14 16:43:53 -0700285}
286
287TranscodingSessionController::~TranscodingSessionController() {}
288
Chong Zhanga1320c52020-12-15 14:30:12 -0800289void TranscodingSessionController::dumpSession_l(const Session& session, String8& result,
290 bool closedSession) {
291 const size_t SIZE = 256;
292 char buffer[SIZE];
293 const TranscodingRequestParcel& request = session.request;
294 snprintf(buffer, SIZE, " Session: %s, %s, %d%%\n", sessionToString(session.key).c_str(),
295 sessionStateToString(session.getState()), session.lastProgress);
296 result.append(buffer);
297 snprintf(buffer, SIZE, " pkg: %s\n", request.clientPackageName.c_str());
298 result.append(buffer);
299 snprintf(buffer, SIZE, " src: %s\n", request.sourceFilePath.c_str());
300 result.append(buffer);
301 snprintf(buffer, SIZE, " dst: %s\n", request.destinationFilePath.c_str());
302 result.append(buffer);
303
304 if (closedSession) {
305 snprintf(buffer, SIZE,
306 " waiting: %.1fs, running: %.1fs, paused: %.1fs, paused count: %d\n",
307 session.waitingTime.count() / 1000000.0f, session.runningTime.count() / 1000000.0f,
308 session.pausedTime.count() / 1000000.0f, session.pauseCount);
309 result.append(buffer);
310 }
311}
312
Chong Zhangbc062482020-10-14 16:43:53 -0700313void TranscodingSessionController::dumpAllSessions(int fd, const Vector<String16>& args __unused) {
314 String8 result;
315
316 const size_t SIZE = 256;
317 char buffer[SIZE];
318 std::scoped_lock lock{mLock};
319
Chong Zhanga1320c52020-12-15 14:30:12 -0800320 snprintf(buffer, SIZE, "\n========== Dumping live sessions queues =========\n");
Chong Zhangbc062482020-10-14 16:43:53 -0700321 result.append(buffer);
322 snprintf(buffer, SIZE, " Total num of Sessions: %zu\n", mSessionMap.size());
323 result.append(buffer);
324
325 std::vector<int32_t> uids(mUidSortedList.begin(), mUidSortedList.end());
Chong Zhangbc062482020-10-14 16:43:53 -0700326
327 for (int32_t i = 0; i < uids.size(); i++) {
328 const uid_t uid = uids[i];
329
330 if (mSessionQueues[uid].empty()) {
331 continue;
332 }
Chong Zhanga1320c52020-12-15 14:30:12 -0800333 snprintf(buffer, SIZE, " uid: %d, pkg: %s\n", uid,
Chong Zhangcf3f8ee2020-10-29 18:38:37 -0700334 mUidPackageNames.count(uid) > 0 ? mUidPackageNames[uid].c_str() : "(unknown)");
Chong Zhangbc062482020-10-14 16:43:53 -0700335 result.append(buffer);
336 snprintf(buffer, SIZE, " Num of sessions: %zu\n", mSessionQueues[uid].size());
337 result.append(buffer);
338 for (auto& sessionKey : mSessionQueues[uid]) {
339 auto sessionIt = mSessionMap.find(sessionKey);
340 if (sessionIt == mSessionMap.end()) {
341 snprintf(buffer, SIZE, "Failed to look up Session %s \n",
342 sessionToString(sessionKey).c_str());
343 result.append(buffer);
344 continue;
345 }
Chong Zhanga1320c52020-12-15 14:30:12 -0800346 dumpSession_l(sessionIt->second, result);
Chong Zhangbc062482020-10-14 16:43:53 -0700347 }
348 }
349
Chong Zhanga1320c52020-12-15 14:30:12 -0800350 snprintf(buffer, SIZE, "\n========== Dumping past sessions =========\n");
351 result.append(buffer);
Chong Zhang8677f1f2021-01-21 20:37:35 +0000352 for (auto& session : mSessionHistory) {
Chong Zhanga1320c52020-12-15 14:30:12 -0800353 dumpSession_l(session, result, true /*closedSession*/);
354 }
355
Chong Zhangbc062482020-10-14 16:43:53 -0700356 write(fd, result.string(), result.size());
357}
358
Chong Zhang87d199c2021-03-01 19:02:18 -0800359/*
360 * Returns nullptr if there is no session, or we're paused globally (due to resource lost,
361 * thermal throttling, etc.). Otherwise, return the session that should be run next.
362 */
Chong Zhangbc062482020-10-14 16:43:53 -0700363TranscodingSessionController::Session* TranscodingSessionController::getTopSession_l() {
364 if (mSessionMap.empty()) {
365 return nullptr;
366 }
Chong Zhang87d199c2021-03-01 19:02:18 -0800367
368 // Return nullptr if we're paused globally due to resource lost or thermal throttling.
369 if (((mResourcePolicy != nullptr && mResourceLost) ||
370 (mThermalPolicy != nullptr && mThermalThrottling))) {
371 return nullptr;
372 }
373
Chong Zhangbc062482020-10-14 16:43:53 -0700374 uid_t topUid = *mUidSortedList.begin();
375 SessionKeyType topSessionKey = *mSessionQueues[topUid].begin();
376 return &mSessionMap[topSessionKey];
377}
378
Chong Zhang457c6892021-02-01 15:34:20 -0800379void TranscodingSessionController::setSessionState_l(Session* session, Session::State state) {
380 bool wasRunning = (session->getState() == Session::RUNNING);
381 session->setState(state);
382 bool isRunning = (session->getState() == Session::RUNNING);
383
384 if (wasRunning == isRunning) {
385 return;
386 }
387
388 // Currently we only have 1 running session, and we always put the previous
389 // session in non-running state before we run the new session, so it's okay
390 // to start/stop the watchdog here. If this assumption changes, we need to
391 // track the number of running sessions and start/stop watchdog based on that.
392 if (isRunning) {
393 mWatchdog->start(session->key);
394 } else {
395 mWatchdog->stop();
396 }
397}
398
Chong Zhanga1320c52020-12-15 14:30:12 -0800399void TranscodingSessionController::Session::setState(Session::State newState) {
400 if (state == newState) {
401 return;
402 }
Chong Zhang87d199c2021-03-01 19:02:18 -0800403 auto nowTime = std::chrono::steady_clock::now();
Chong Zhanga1320c52020-12-15 14:30:12 -0800404 if (state != INVALID) {
Chong Zhang87d199c2021-03-01 19:02:18 -0800405 std::chrono::microseconds elapsedTime =
406 std::chrono::duration_cast<std::chrono::microseconds>(nowTime - stateEnterTime);
Chong Zhanga1320c52020-12-15 14:30:12 -0800407 switch (state) {
408 case PAUSED:
409 pausedTime = pausedTime + elapsedTime;
410 break;
411 case RUNNING:
412 runningTime = runningTime + elapsedTime;
413 break;
414 case NOT_STARTED:
415 waitingTime = waitingTime + elapsedTime;
416 break;
417 default:
418 break;
419 }
420 }
421 if (newState == PAUSED) {
422 pauseCount++;
423 }
424 stateEnterTime = nowTime;
425 state = newState;
426}
427
Chong Zhangbc062482020-10-14 16:43:53 -0700428void TranscodingSessionController::updateCurrentSession_l() {
Chong Zhangbc062482020-10-14 16:43:53 -0700429 Session* curSession = mCurrentSession;
Chong Zhang87d199c2021-03-01 19:02:18 -0800430 Session* topSession = getTopSession_l();
Chong Zhangbc062482020-10-14 16:43:53 -0700431
Chong Zhang87d199c2021-03-01 19:02:18 -0800432 // Delayed init of transcoder and watchdog.
433 if (mTranscoder == nullptr) {
434 mTranscoder = mTranscoderFactory(shared_from_this());
435 mWatchdog = std::make_shared<Watchdog>(this, mConfig.watchdogTimeoutUs);
Chong Zhang8677f1f2021-01-21 20:37:35 +0000436 }
437
Chong Zhang87d199c2021-03-01 19:02:18 -0800438 // If we found a different top session, or the top session's running state is not
439 // correct. Take some actions to ensure it's correct.
440 while ((topSession = getTopSession_l()) != curSession ||
441 (topSession != nullptr && !topSession->isRunning())) {
442 ALOGV("updateCurrentSession_l: topSession is %s, curSession is %s",
443 topSession == nullptr ? "null" : sessionToString(topSession->key).c_str(),
444 curSession == nullptr ? "null" : sessionToString(curSession->key).c_str());
Chong Zhang457c6892021-02-01 15:34:20 -0800445
Chong Zhang87d199c2021-03-01 19:02:18 -0800446 // If current session is running, pause it first. Note this is needed for either
447 // cases: 1) Top session is changing to another session, or 2) Top session is
448 // changing to null (which means we should be globally paused).
Chong Zhanga1320c52020-12-15 14:30:12 -0800449 if (curSession != nullptr && curSession->getState() == Session::RUNNING) {
Chong Zhangbc062482020-10-14 16:43:53 -0700450 mTranscoder->pause(curSession->key.first, curSession->key.second);
Chong Zhang457c6892021-02-01 15:34:20 -0800451 setSessionState_l(curSession, Session::PAUSED);
Chong Zhangbc062482020-10-14 16:43:53 -0700452 }
Chong Zhang87d199c2021-03-01 19:02:18 -0800453
454 if (topSession == nullptr) {
455 // Nothing more to run (either no session or globally paused).
456 break;
457 }
458
459 // Otherwise, ensure topSession is running.
460 if (topSession->getState() == Session::NOT_STARTED) {
461 if (!mPacer->onSessionStarted(topSession->clientUid)) {
462 // Unfortunately this uid is out of quota for new sessions.
463 // Drop this sesion and try another one.
464 {
465 auto clientCallback = mSessionMap[topSession->key].callback.lock();
466 if (clientCallback != nullptr) {
467 clientCallback->onTranscodingFailed(
468 topSession->key.second, TranscodingErrorCode::kDroppedByService);
469 }
470 }
471 removeSession_l(topSession->key, Session::DROPPED_BY_PACER);
472 continue;
Chong Zhangbc062482020-10-14 16:43:53 -0700473 }
Chong Zhang87d199c2021-03-01 19:02:18 -0800474 mTranscoder->start(topSession->key.first, topSession->key.second, topSession->request,
475 topSession->callingUid, topSession->callback.lock());
476 setSessionState_l(topSession, Session::RUNNING);
477 } else if (topSession->getState() == Session::PAUSED) {
478 mTranscoder->resume(topSession->key.first, topSession->key.second, topSession->request,
479 topSession->callingUid, topSession->callback.lock());
Chong Zhang457c6892021-02-01 15:34:20 -0800480 setSessionState_l(topSession, Session::RUNNING);
Chong Zhangbc062482020-10-14 16:43:53 -0700481 }
Chong Zhang87d199c2021-03-01 19:02:18 -0800482 break;
Chong Zhangbc062482020-10-14 16:43:53 -0700483 }
484 mCurrentSession = topSession;
485}
486
Chong Zhanga1320c52020-12-15 14:30:12 -0800487void TranscodingSessionController::removeSession_l(const SessionKeyType& sessionKey,
488 Session::State finalState) {
Chong Zhangbc062482020-10-14 16:43:53 -0700489 ALOGV("%s: session %s", __FUNCTION__, sessionToString(sessionKey).c_str());
490
491 if (mSessionMap.count(sessionKey) == 0) {
492 ALOGE("session %s doesn't exist", sessionToString(sessionKey).c_str());
493 return;
494 }
495
496 // Remove session from uid's queue.
Linus Nilssona99f4042021-02-25 15:49:43 -0800497 const uid_t uid = mSessionMap[sessionKey].clientUid;
Chong Zhangbc062482020-10-14 16:43:53 -0700498 SessionQueueType& sessionQueue = mSessionQueues[uid];
499 auto it = std::find(sessionQueue.begin(), sessionQueue.end(), sessionKey);
500 if (it == sessionQueue.end()) {
501 ALOGE("couldn't find session %s in queue for uid %d", sessionToString(sessionKey).c_str(),
502 uid);
503 return;
504 }
505 sessionQueue.erase(it);
506
507 // If this is the last session in a real-time queue, remove this uid's queue.
508 if (uid != OFFLINE_UID && sessionQueue.empty()) {
509 mUidSortedList.remove(uid);
510 mSessionQueues.erase(uid);
511 mUidPolicy->unregisterMonitorUid(uid);
512
513 std::unordered_set<uid_t> topUids = mUidPolicy->getTopUids();
514 moveUidsToTop_l(topUids, false /*preserveTopUid*/);
515 }
516
517 // Clear current session.
518 if (mCurrentSession == &mSessionMap[sessionKey]) {
519 mCurrentSession = nullptr;
520 }
521
Chong Zhang457c6892021-02-01 15:34:20 -0800522 setSessionState_l(&mSessionMap[sessionKey], finalState);
Chong Zhang87d199c2021-03-01 19:02:18 -0800523
524 if (finalState == Session::FINISHED || finalState == Session::ERROR) {
525 mPacer->onSessionCompleted(mSessionMap[sessionKey].clientUid,
526 mSessionMap[sessionKey].runningTime);
527 }
528
Chong Zhanga1320c52020-12-15 14:30:12 -0800529 mSessionHistory.push_back(mSessionMap[sessionKey]);
530 if (mSessionHistory.size() > kSessionHistoryMax) {
531 mSessionHistory.erase(mSessionHistory.begin());
532 }
533
Chong Zhangbc062482020-10-14 16:43:53 -0700534 // Remove session from session map.
535 mSessionMap.erase(sessionKey);
536}
537
538/**
539 * Moves the set of uids to the front of mUidSortedList (which is used to pick
540 * the next session to run).
541 *
542 * This is called when 1) we received a onTopUidsChanged() callback from UidPolicy,
543 * or 2) we removed the session queue for a uid because it becomes empty.
544 *
545 * In case of 1), if there are multiple uids in the set, and the current front
546 * uid in mUidSortedList is still in the set, we try to keep that uid at front
547 * so that current session run is not interrupted. (This is not a concern for case 2)
548 * because the queue for a uid was just removed entirely.)
549 */
550void TranscodingSessionController::moveUidsToTop_l(const std::unordered_set<uid_t>& uids,
551 bool preserveTopUid) {
552 // If uid set is empty, nothing to do. Do not change the queue status.
553 if (uids.empty()) {
554 return;
555 }
556
557 // Save the current top uid.
558 uid_t curTopUid = *mUidSortedList.begin();
559 bool pushCurTopToFront = false;
560 int32_t numUidsMoved = 0;
561
562 // Go through the sorted uid list once, and move the ones in top set to front.
563 for (auto it = mUidSortedList.begin(); it != mUidSortedList.end();) {
564 uid_t uid = *it;
565
566 if (uid != OFFLINE_UID && uids.count(uid) > 0) {
567 it = mUidSortedList.erase(it);
568
569 // If this is the top we're preserving, don't push it here, push
570 // it after the for-loop.
571 if (uid == curTopUid && preserveTopUid) {
572 pushCurTopToFront = true;
573 } else {
574 mUidSortedList.push_front(uid);
575 }
576
577 // If we found all uids in the set, break out.
578 if (++numUidsMoved == uids.size()) {
579 break;
580 }
581 } else {
582 ++it;
583 }
584 }
585
586 if (pushCurTopToFront) {
587 mUidSortedList.push_front(curTopUid);
588 }
589}
590
591bool TranscodingSessionController::submit(
Linus Nilssona99f4042021-02-25 15:49:43 -0800592 ClientIdType clientId, SessionIdType sessionId, uid_t callingUid, uid_t clientUid,
Chong Zhangbc062482020-10-14 16:43:53 -0700593 const TranscodingRequestParcel& request,
594 const std::weak_ptr<ITranscodingClientCallback>& callback) {
595 SessionKeyType sessionKey = std::make_pair(clientId, sessionId);
596
597 ALOGV("%s: session %s, uid %d, prioirty %d", __FUNCTION__, sessionToString(sessionKey).c_str(),
Linus Nilssona99f4042021-02-25 15:49:43 -0800598 clientUid, (int32_t)request.priority);
Chong Zhangbc062482020-10-14 16:43:53 -0700599
600 std::scoped_lock lock{mLock};
601
602 if (mSessionMap.count(sessionKey) > 0) {
603 ALOGE("session %s already exists", sessionToString(sessionKey).c_str());
604 return false;
605 }
606
Chong Zhangcf3f8ee2020-10-29 18:38:37 -0700607 // Add the uid package name to the store of package names we already know.
Linus Nilssona99f4042021-02-25 15:49:43 -0800608 if (mUidPackageNames.count(clientUid) == 0) {
609 mUidPackageNames.emplace(clientUid, request.clientPackageName);
Chong Zhangcf3f8ee2020-10-29 18:38:37 -0700610 }
611
Chong Zhangbc062482020-10-14 16:43:53 -0700612 // TODO(chz): only support offline vs real-time for now. All kUnspecified sessions
613 // go to offline queue.
614 if (request.priority == TranscodingSessionPriority::kUnspecified) {
Linus Nilssona99f4042021-02-25 15:49:43 -0800615 clientUid = OFFLINE_UID;
Chong Zhangbc062482020-10-14 16:43:53 -0700616 }
617
618 // Add session to session map.
619 mSessionMap[sessionKey].key = sessionKey;
Linus Nilssona99f4042021-02-25 15:49:43 -0800620 mSessionMap[sessionKey].clientUid = clientUid;
621 mSessionMap[sessionKey].callingUid = callingUid;
Chong Zhangbc062482020-10-14 16:43:53 -0700622 mSessionMap[sessionKey].request = request;
623 mSessionMap[sessionKey].callback = callback;
Chong Zhang457c6892021-02-01 15:34:20 -0800624 setSessionState_l(&mSessionMap[sessionKey], Session::NOT_STARTED);
Chong Zhangbc062482020-10-14 16:43:53 -0700625
626 // If it's an offline session, the queue was already added in constructor.
627 // If it's a real-time sessions, check if a queue is already present for the uid,
628 // and add a new queue if needed.
Linus Nilssona99f4042021-02-25 15:49:43 -0800629 if (clientUid != OFFLINE_UID) {
630 if (mSessionQueues.count(clientUid) == 0) {
631 mUidPolicy->registerMonitorUid(clientUid);
632 if (mUidPolicy->isUidOnTop(clientUid)) {
633 mUidSortedList.push_front(clientUid);
Chong Zhangbc062482020-10-14 16:43:53 -0700634 } else {
635 // Shouldn't be submitting real-time requests from non-top app,
636 // put it in front of the offline queue.
Linus Nilssona99f4042021-02-25 15:49:43 -0800637 mUidSortedList.insert(mOfflineUidIterator, clientUid);
Chong Zhangbc062482020-10-14 16:43:53 -0700638 }
Linus Nilssona99f4042021-02-25 15:49:43 -0800639 } else if (clientUid != *mUidSortedList.begin()) {
640 if (mUidPolicy->isUidOnTop(clientUid)) {
641 mUidSortedList.remove(clientUid);
642 mUidSortedList.push_front(clientUid);
Chong Zhangbc062482020-10-14 16:43:53 -0700643 }
644 }
645 }
646 // Append this session to the uid's queue.
Linus Nilssona99f4042021-02-25 15:49:43 -0800647 mSessionQueues[clientUid].push_back(sessionKey);
Chong Zhangbc062482020-10-14 16:43:53 -0700648
649 updateCurrentSession_l();
650
651 validateState_l();
652 return true;
653}
654
655bool TranscodingSessionController::cancel(ClientIdType clientId, SessionIdType sessionId) {
656 SessionKeyType sessionKey = std::make_pair(clientId, sessionId);
657
658 ALOGV("%s: session %s", __FUNCTION__, sessionToString(sessionKey).c_str());
659
660 std::list<SessionKeyType> sessionsToRemove;
661
662 std::scoped_lock lock{mLock};
663
664 if (sessionId < 0) {
665 for (auto it = mSessionMap.begin(); it != mSessionMap.end(); ++it) {
Linus Nilssona99f4042021-02-25 15:49:43 -0800666 if (it->first.first == clientId && it->second.clientUid != OFFLINE_UID) {
Chong Zhangbc062482020-10-14 16:43:53 -0700667 sessionsToRemove.push_back(it->first);
668 }
669 }
670 } else {
671 if (mSessionMap.count(sessionKey) == 0) {
672 ALOGE("session %s doesn't exist", sessionToString(sessionKey).c_str());
673 return false;
674 }
675 sessionsToRemove.push_back(sessionKey);
676 }
677
678 for (auto it = sessionsToRemove.begin(); it != sessionsToRemove.end(); ++it) {
679 // If the session has ever been started, stop it now.
680 // Note that stop() is needed even if the session is currently paused. This instructs
681 // the transcoder to discard any states for the session, otherwise the states may
682 // never be discarded.
Chong Zhanga1320c52020-12-15 14:30:12 -0800683 if (mSessionMap[*it].getState() != Session::NOT_STARTED) {
Chong Zhangbc062482020-10-14 16:43:53 -0700684 mTranscoder->stop(it->first, it->second);
685 }
686
687 // Remove the session.
Chong Zhanga1320c52020-12-15 14:30:12 -0800688 removeSession_l(*it, Session::CANCELED);
Chong Zhangbc062482020-10-14 16:43:53 -0700689 }
690
691 // Start next session.
692 updateCurrentSession_l();
693
694 validateState_l();
695 return true;
696}
697
698bool TranscodingSessionController::getSession(ClientIdType clientId, SessionIdType sessionId,
699 TranscodingRequestParcel* request) {
700 SessionKeyType sessionKey = std::make_pair(clientId, sessionId);
701
702 std::scoped_lock lock{mLock};
703
704 if (mSessionMap.count(sessionKey) == 0) {
705 ALOGE("session %s doesn't exist", sessionToString(sessionKey).c_str());
706 return false;
707 }
708
709 *(TranscodingRequest*)request = mSessionMap[sessionKey].request;
710 return true;
711}
712
713void TranscodingSessionController::notifyClient(ClientIdType clientId, SessionIdType sessionId,
714 const char* reason,
715 std::function<void(const SessionKeyType&)> func) {
716 SessionKeyType sessionKey = std::make_pair(clientId, sessionId);
717
718 std::scoped_lock lock{mLock};
719
720 if (mSessionMap.count(sessionKey) == 0) {
721 ALOGW("%s: ignoring %s for session %s that doesn't exist", __FUNCTION__, reason,
722 sessionToString(sessionKey).c_str());
723 return;
724 }
725
726 // Only ignore if session was never started. In particular, propagate the status
727 // to client if the session is paused. Transcoder could have posted finish when
728 // we're pausing it, and the finish arrived after we changed current session.
Chong Zhanga1320c52020-12-15 14:30:12 -0800729 if (mSessionMap[sessionKey].getState() == Session::NOT_STARTED) {
Chong Zhangbc062482020-10-14 16:43:53 -0700730 ALOGW("%s: ignoring %s for session %s that was never started", __FUNCTION__, reason,
731 sessionToString(sessionKey).c_str());
732 return;
733 }
734
735 ALOGV("%s: session %s %s", __FUNCTION__, sessionToString(sessionKey).c_str(), reason);
736 func(sessionKey);
737}
738
739void TranscodingSessionController::onStarted(ClientIdType clientId, SessionIdType sessionId) {
740 notifyClient(clientId, sessionId, "started", [=](const SessionKeyType& sessionKey) {
741 auto callback = mSessionMap[sessionKey].callback.lock();
742 if (callback != nullptr) {
743 callback->onTranscodingStarted(sessionId);
744 }
745 });
746}
747
748void TranscodingSessionController::onPaused(ClientIdType clientId, SessionIdType sessionId) {
749 notifyClient(clientId, sessionId, "paused", [=](const SessionKeyType& sessionKey) {
750 auto callback = mSessionMap[sessionKey].callback.lock();
751 if (callback != nullptr) {
752 callback->onTranscodingPaused(sessionId);
753 }
754 });
755}
756
757void TranscodingSessionController::onResumed(ClientIdType clientId, SessionIdType sessionId) {
758 notifyClient(clientId, sessionId, "resumed", [=](const SessionKeyType& sessionKey) {
759 auto callback = mSessionMap[sessionKey].callback.lock();
760 if (callback != nullptr) {
761 callback->onTranscodingResumed(sessionId);
762 }
763 });
764}
765
766void TranscodingSessionController::onFinish(ClientIdType clientId, SessionIdType sessionId) {
767 notifyClient(clientId, sessionId, "finish", [=](const SessionKeyType& sessionKey) {
768 {
769 auto clientCallback = mSessionMap[sessionKey].callback.lock();
770 if (clientCallback != nullptr) {
771 clientCallback->onTranscodingFinished(
772 sessionId, TranscodingResultParcel({sessionId, -1 /*actualBitrateBps*/,
773 std::nullopt /*sessionStats*/}));
774 }
775 }
776
777 // Remove the session.
Chong Zhanga1320c52020-12-15 14:30:12 -0800778 removeSession_l(sessionKey, Session::FINISHED);
Chong Zhangbc062482020-10-14 16:43:53 -0700779
780 // Start next session.
781 updateCurrentSession_l();
782
783 validateState_l();
784 });
785}
786
787void TranscodingSessionController::onError(ClientIdType clientId, SessionIdType sessionId,
788 TranscodingErrorCode err) {
789 notifyClient(clientId, sessionId, "error", [=](const SessionKeyType& sessionKey) {
Chong Zhang457c6892021-02-01 15:34:20 -0800790 if (err == TranscodingErrorCode::kWatchdogTimeout) {
791 // Abandon the transcoder, as its handler thread might be stuck in some call to
792 // MediaTranscoder altogether, and may not be able to handle any new tasks.
793 mTranscoder->stop(clientId, sessionId, true /*abandon*/);
794 // Clear the last ref count before we create new transcoder.
795 mTranscoder = nullptr;
Chong Zhang87d199c2021-03-01 19:02:18 -0800796 mTranscoder = mTranscoderFactory(shared_from_this());
Chong Zhang457c6892021-02-01 15:34:20 -0800797 }
798
Chong Zhangbc062482020-10-14 16:43:53 -0700799 {
800 auto clientCallback = mSessionMap[sessionKey].callback.lock();
801 if (clientCallback != nullptr) {
802 clientCallback->onTranscodingFailed(sessionId, err);
803 }
804 }
805
806 // Remove the session.
Chong Zhanga1320c52020-12-15 14:30:12 -0800807 removeSession_l(sessionKey, Session::ERROR);
Chong Zhangbc062482020-10-14 16:43:53 -0700808
809 // Start next session.
810 updateCurrentSession_l();
811
812 validateState_l();
813 });
814}
815
816void TranscodingSessionController::onProgressUpdate(ClientIdType clientId, SessionIdType sessionId,
817 int32_t progress) {
818 notifyClient(clientId, sessionId, "progress", [=](const SessionKeyType& sessionKey) {
819 auto callback = mSessionMap[sessionKey].callback.lock();
820 if (callback != nullptr) {
821 callback->onProgressUpdate(sessionId, progress);
822 }
823 mSessionMap[sessionKey].lastProgress = progress;
824 });
825}
826
Chong Zhang457c6892021-02-01 15:34:20 -0800827void TranscodingSessionController::onHeartBeat(ClientIdType clientId, SessionIdType sessionId) {
828 notifyClient(clientId, sessionId, "heart-beat",
829 [=](const SessionKeyType& /*sessionKey*/) { mWatchdog->keepAlive(); });
830}
831
Chong Zhangeffd8962020-12-02 14:29:09 -0800832void TranscodingSessionController::onResourceLost(ClientIdType clientId, SessionIdType sessionId) {
Chong Zhangbc062482020-10-14 16:43:53 -0700833 ALOGI("%s", __FUNCTION__);
834
Chong Zhangeffd8962020-12-02 14:29:09 -0800835 notifyClient(clientId, sessionId, "resource_lost", [=](const SessionKeyType& sessionKey) {
836 if (mResourceLost) {
837 return;
Chong Zhangbc062482020-10-14 16:43:53 -0700838 }
Chong Zhangbc062482020-10-14 16:43:53 -0700839
Chong Zhangeffd8962020-12-02 14:29:09 -0800840 Session* resourceLostSession = &mSessionMap[sessionKey];
Chong Zhanga1320c52020-12-15 14:30:12 -0800841 if (resourceLostSession->getState() != Session::RUNNING) {
Chong Zhangeffd8962020-12-02 14:29:09 -0800842 ALOGW("session %s lost resource but is no longer running",
Chong Zhanga1320c52020-12-15 14:30:12 -0800843 sessionToString(sessionKey).c_str());
Chong Zhangeffd8962020-12-02 14:29:09 -0800844 return;
845 }
846 // If we receive a resource loss event, the transcoder already paused the transcoding,
847 // so we don't need to call onPaused() to pause it. However, we still need to notify
848 // the client and update the session state here.
Chong Zhang457c6892021-02-01 15:34:20 -0800849 setSessionState_l(resourceLostSession, Session::PAUSED);
Chong Zhangeffd8962020-12-02 14:29:09 -0800850 // Notify the client as a paused event.
851 auto clientCallback = resourceLostSession->callback.lock();
852 if (clientCallback != nullptr) {
853 clientCallback->onTranscodingPaused(sessionKey.second);
854 }
Chong Zhang8677f1f2021-01-21 20:37:35 +0000855 if (mResourcePolicy != nullptr) {
856 mResourcePolicy->setPidResourceLost(resourceLostSession->request.clientPid);
857 }
Chong Zhangeffd8962020-12-02 14:29:09 -0800858 mResourceLost = true;
859
860 validateState_l();
861 });
Chong Zhangbc062482020-10-14 16:43:53 -0700862}
863
864void TranscodingSessionController::onTopUidsChanged(const std::unordered_set<uid_t>& uids) {
865 if (uids.empty()) {
866 ALOGW("%s: ignoring empty uids", __FUNCTION__);
867 return;
868 }
869
870 std::string uidStr;
871 for (auto it = uids.begin(); it != uids.end(); it++) {
872 if (!uidStr.empty()) {
873 uidStr += ", ";
874 }
875 uidStr += std::to_string(*it);
876 }
877
878 ALOGD("%s: topUids: size %zu, uids: %s", __FUNCTION__, uids.size(), uidStr.c_str());
879
880 std::scoped_lock lock{mLock};
881
882 moveUidsToTop_l(uids, true /*preserveTopUid*/);
883
884 updateCurrentSession_l();
885
886 validateState_l();
887}
888
889void TranscodingSessionController::onResourceAvailable() {
890 std::scoped_lock lock{mLock};
891
892 if (!mResourceLost) {
893 return;
894 }
895
896 ALOGI("%s", __FUNCTION__);
897
898 mResourceLost = false;
899 updateCurrentSession_l();
900
901 validateState_l();
902}
903
Chong Zhang8677f1f2021-01-21 20:37:35 +0000904void TranscodingSessionController::onThrottlingStarted() {
905 std::scoped_lock lock{mLock};
906
907 if (mThermalThrottling) {
908 return;
909 }
910
911 ALOGI("%s", __FUNCTION__);
912
913 mThermalThrottling = true;
914 updateCurrentSession_l();
915
916 validateState_l();
917}
918
919void TranscodingSessionController::onThrottlingStopped() {
920 std::scoped_lock lock{mLock};
921
922 if (!mThermalThrottling) {
923 return;
924 }
925
926 ALOGI("%s", __FUNCTION__);
927
928 mThermalThrottling = false;
929 updateCurrentSession_l();
930
931 validateState_l();
932}
933
Chong Zhangbc062482020-10-14 16:43:53 -0700934void TranscodingSessionController::validateState_l() {
935#ifdef VALIDATE_STATE
936 LOG_ALWAYS_FATAL_IF(mSessionQueues.count(OFFLINE_UID) != 1,
937 "mSessionQueues offline queue number is not 1");
938 LOG_ALWAYS_FATAL_IF(*mOfflineUidIterator != OFFLINE_UID,
939 "mOfflineUidIterator not pointing to offline uid");
940 LOG_ALWAYS_FATAL_IF(mUidSortedList.size() != mSessionQueues.size(),
941 "mUidList and mSessionQueues size mismatch");
942
943 int32_t totalSessions = 0;
944 for (auto uid : mUidSortedList) {
945 LOG_ALWAYS_FATAL_IF(mSessionQueues.count(uid) != 1,
946 "mSessionQueues count for uid %d is not 1", uid);
947 for (auto& sessionKey : mSessionQueues[uid]) {
948 LOG_ALWAYS_FATAL_IF(mSessionMap.count(sessionKey) != 1,
949 "mSessions count for session %s is not 1",
950 sessionToString(sessionKey).c_str());
951 }
952
953 totalSessions += mSessionQueues[uid].size();
954 }
955 LOG_ALWAYS_FATAL_IF(mSessionMap.size() != totalSessions,
956 "mSessions size doesn't match total sessions counted from uid queues");
957#endif // VALIDATE_STATE
958}
959
960} // namespace android