blob: 68e2875c98b6b051db37dd2901a2c577d6fb7926 [file] [log] [blame]
Chong Zhangbc062482020-10-14 16:43:53 -07001/*
2 * Copyright (C) 2020 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17//#define LOG_NDEBUG 0
18#define LOG_TAG "TranscodingSessionController"
19
20#define VALIDATE_STATE 1
21
22#include <inttypes.h>
23#include <media/TranscodingSessionController.h>
24#include <media/TranscodingUidPolicy.h>
25#include <utils/Log.h>
26
Chong Zhang457c6892021-02-01 15:34:20 -080027#include <thread>
Chong Zhangbc062482020-10-14 16:43:53 -070028#include <utility>
29
30namespace android {
31
32static_assert((SessionIdType)-1 < 0, "SessionIdType should be signed");
33
34constexpr static uid_t OFFLINE_UID = -1;
Chong Zhanga1320c52020-12-15 14:30:12 -080035constexpr static size_t kSessionHistoryMax = 100;
Chong Zhangbc062482020-10-14 16:43:53 -070036
37//static
38String8 TranscodingSessionController::sessionToString(const SessionKeyType& sessionKey) {
39 return String8::format("{client:%lld, session:%d}", (long long)sessionKey.first,
40 sessionKey.second);
41}
42
43//static
44const char* TranscodingSessionController::sessionStateToString(const Session::State sessionState) {
45 switch (sessionState) {
46 case Session::State::NOT_STARTED:
47 return "NOT_STARTED";
48 case Session::State::RUNNING:
49 return "RUNNING";
50 case Session::State::PAUSED:
51 return "PAUSED";
Chong Zhanga1320c52020-12-15 14:30:12 -080052 case Session::State::FINISHED:
53 return "FINISHED";
54 case Session::State::CANCELED:
55 return "CANCELED";
56 case Session::State::ERROR:
57 return "ERROR";
Chong Zhangbc062482020-10-14 16:43:53 -070058 default:
59 break;
60 }
61 return "(unknown)";
62}
63
Chong Zhang457c6892021-02-01 15:34:20 -080064///////////////////////////////////////////////////////////////////////////////
65struct TranscodingSessionController::Watchdog {
66 Watchdog(TranscodingSessionController* owner, int64_t timeoutUs);
67 ~Watchdog();
68
69 // Starts monitoring the session.
70 void start(const SessionKeyType& key);
71 // Stops monitoring the session.
72 void stop();
73 // Signals that the session is still alive. Must be sent at least every mTimeoutUs.
74 // (Timeout will happen if no ping in mTimeoutUs since the last ping.)
75 void keepAlive();
76
77private:
78 void threadLoop();
79 void updateTimer_l();
80
81 TranscodingSessionController* mOwner;
82 const int64_t mTimeoutUs;
83 mutable std::mutex mLock;
84 std::condition_variable mCondition GUARDED_BY(mLock);
85 // Whether watchdog is monitoring a session for timeout.
86 bool mActive GUARDED_BY(mLock);
87 // Whether watchdog is aborted and the monitoring thread should exit.
88 bool mAbort GUARDED_BY(mLock);
89 // When watchdog is active, the next timeout time point.
Chong Zhang87d199c2021-03-01 19:02:18 -080090 std::chrono::steady_clock::time_point mNextTimeoutTime GUARDED_BY(mLock);
Chong Zhang457c6892021-02-01 15:34:20 -080091 // When watchdog is active, the session being watched.
92 SessionKeyType mSessionToWatch GUARDED_BY(mLock);
93 std::thread mThread;
94};
95
Chong Zhang457c6892021-02-01 15:34:20 -080096TranscodingSessionController::Watchdog::Watchdog(TranscodingSessionController* owner,
97 int64_t timeoutUs)
98 : mOwner(owner),
99 mTimeoutUs(timeoutUs),
100 mActive(false),
101 mAbort(false),
102 mThread(&Watchdog::threadLoop, this) {
103 ALOGV("Watchdog CTOR: %p", this);
104}
105
106TranscodingSessionController::Watchdog::~Watchdog() {
107 ALOGV("Watchdog DTOR: %p", this);
108
109 {
110 // Exit the looper thread.
111 std::scoped_lock lock{mLock};
112
113 mAbort = true;
114 mCondition.notify_one();
115 }
116
117 mThread.join();
118 ALOGV("Watchdog DTOR: %p, done.", this);
119}
120
121void TranscodingSessionController::Watchdog::start(const SessionKeyType& key) {
122 std::scoped_lock lock{mLock};
123
124 if (!mActive) {
125 ALOGI("Watchdog start: %s", sessionToString(key).c_str());
126
127 mActive = true;
128 mSessionToWatch = key;
129 updateTimer_l();
130 mCondition.notify_one();
131 }
132}
133
134void TranscodingSessionController::Watchdog::stop() {
135 std::scoped_lock lock{mLock};
136
137 if (mActive) {
138 ALOGI("Watchdog stop: %s", sessionToString(mSessionToWatch).c_str());
139
140 mActive = false;
141 mCondition.notify_one();
142 }
143}
144
145void TranscodingSessionController::Watchdog::keepAlive() {
146 std::scoped_lock lock{mLock};
147
148 if (mActive) {
149 ALOGI("Watchdog keepAlive: %s", sessionToString(mSessionToWatch).c_str());
150
151 updateTimer_l();
152 mCondition.notify_one();
153 }
154}
155
156// updateTimer_l() is only called with lock held.
157void TranscodingSessionController::Watchdog::updateTimer_l() NO_THREAD_SAFETY_ANALYSIS {
158 std::chrono::microseconds timeout(mTimeoutUs);
Chong Zhang87d199c2021-03-01 19:02:18 -0800159 mNextTimeoutTime = std::chrono::steady_clock::now() + timeout;
Chong Zhang457c6892021-02-01 15:34:20 -0800160}
161
162// Unfortunately std::unique_lock is incompatible with -Wthread-safety.
163void TranscodingSessionController::Watchdog::threadLoop() NO_THREAD_SAFETY_ANALYSIS {
164 std::unique_lock<std::mutex> lock{mLock};
165
166 while (!mAbort) {
167 if (!mActive) {
168 mCondition.wait(lock);
169 continue;
170 }
171 // Watchdog active, wait till next timeout time.
172 if (mCondition.wait_until(lock, mNextTimeoutTime) == std::cv_status::timeout) {
173 // If timeout happens, report timeout and deactivate watchdog.
174 mActive = false;
175 // Make a copy of session key, as once we unlock, it could be unprotected.
176 SessionKeyType sessionKey = mSessionToWatch;
177
178 ALOGE("Watchdog timeout: %s", sessionToString(sessionKey).c_str());
179
180 lock.unlock();
181 mOwner->onError(sessionKey.first, sessionKey.second,
182 TranscodingErrorCode::kWatchdogTimeout);
183 lock.lock();
184 }
185 }
186}
187///////////////////////////////////////////////////////////////////////////////
Chong Zhang87d199c2021-03-01 19:02:18 -0800188struct TranscodingSessionController::Pacer {
189 Pacer(const ControllerConfig& config)
190 : mBurstThresholdMs(config.pacerBurstThresholdMs),
191 mBurstCountQuota(config.pacerBurstCountQuota),
192 mBurstTimeQuotaSec(config.pacerBurstTimeQuotaSeconds) {}
193
194 ~Pacer() = default;
195
Chong Zhang87d199c2021-03-01 19:02:18 -0800196 bool onSessionStarted(uid_t uid);
Chong Zhangebd86d32021-03-29 11:30:56 -0700197 void onSessionCompleted(uid_t uid, std::chrono::microseconds runningTime);
Chong Zhang2a3c9672021-03-31 15:36:32 -0700198 void onSessionCancelled(uid_t uid);
Chong Zhang87d199c2021-03-01 19:02:18 -0800199
200private:
201 // Threshold of time between finish/start below which a back-to-back start is counted.
202 int32_t mBurstThresholdMs;
203 // Maximum allowed back-to-back start count.
204 int32_t mBurstCountQuota;
205 // Maximum allowed back-to-back running time.
206 int32_t mBurstTimeQuotaSec;
207
208 struct UidHistoryEntry {
Chong Zhangebd86d32021-03-29 11:30:56 -0700209 bool sessionActive = false;
Chong Zhang87d199c2021-03-01 19:02:18 -0800210 int32_t burstCount = 0;
211 std::chrono::steady_clock::duration burstDuration{0};
Chong Zhangebd86d32021-03-29 11:30:56 -0700212 std::chrono::steady_clock::time_point lastCompletedTime;
Chong Zhang87d199c2021-03-01 19:02:18 -0800213 };
214 std::map<uid_t, UidHistoryEntry> mUidHistoryMap;
215};
216
Chong Zhangebd86d32021-03-29 11:30:56 -0700217bool TranscodingSessionController::Pacer::onSessionStarted(uid_t uid) {
218 // If uid doesn't exist, only insert the entry and mark session active. Skip quota checking.
Chong Zhang87d199c2021-03-01 19:02:18 -0800219 if (mUidHistoryMap.find(uid) == mUidHistoryMap.end()) {
220 mUidHistoryMap.emplace(uid, UidHistoryEntry{});
Chong Zhangebd86d32021-03-29 11:30:56 -0700221 mUidHistoryMap[uid].sessionActive = true;
222 ALOGV("Pacer::onSessionStarted: uid %d: new", uid);
Chong Zhang87d199c2021-03-01 19:02:18 -0800223 return true;
224 }
225
226 // TODO: if Thermal throttling or resoure lost happened to occurr between this start
227 // and the previous completion, we should deduct the paused time from the elapsed time.
228 // (Individual session's pause time, on the other hand, doesn't need to be deducted
229 // because it doesn't affect the gap between last completion and the start.
230 auto timeSinceLastComplete =
231 std::chrono::steady_clock::now() - mUidHistoryMap[uid].lastCompletedTime;
232 if (mUidHistoryMap[uid].burstCount >= mBurstCountQuota &&
233 mUidHistoryMap[uid].burstDuration >= std::chrono::seconds(mBurstTimeQuotaSec)) {
Chong Zhangebd86d32021-03-29 11:30:56 -0700234 ALOGW("Pacer::onSessionStarted: uid %d: over quota, burst count %d, time %lldms", uid,
235 mUidHistoryMap[uid].burstCount,
236 (long long)mUidHistoryMap[uid].burstDuration.count() / 1000000);
Chong Zhang87d199c2021-03-01 19:02:18 -0800237 return false;
238 }
239
240 // If not over quota, allow the session, and reset as long as this is not too close
241 // to previous completion.
242 if (timeSinceLastComplete > std::chrono::milliseconds(mBurstThresholdMs)) {
Chong Zhangebd86d32021-03-29 11:30:56 -0700243 ALOGV("Pacer::onSessionStarted: uid %d: reset quota", uid);
Chong Zhang87d199c2021-03-01 19:02:18 -0800244 mUidHistoryMap[uid].burstCount = 0;
245 mUidHistoryMap[uid].burstDuration = std::chrono::milliseconds(0);
246 } else {
Chong Zhangebd86d32021-03-29 11:30:56 -0700247 ALOGV("Pacer::onSessionStarted: uid %d: burst count %d, time %lldms", uid,
248 mUidHistoryMap[uid].burstCount,
249 (long long)mUidHistoryMap[uid].burstDuration.count() / 1000000);
Chong Zhang87d199c2021-03-01 19:02:18 -0800250 }
251
Chong Zhangebd86d32021-03-29 11:30:56 -0700252 mUidHistoryMap[uid].sessionActive = true;
Chong Zhang87d199c2021-03-01 19:02:18 -0800253 return true;
254}
255
Chong Zhangebd86d32021-03-29 11:30:56 -0700256void TranscodingSessionController::Pacer::onSessionCompleted(
257 uid_t uid, std::chrono::microseconds runningTime) {
258 // Skip quota update if this uid missed the start. (Could happen if the uid is added via
259 // addClientUid() after the session start.)
260 if (mUidHistoryMap.find(uid) == mUidHistoryMap.end() || !mUidHistoryMap[uid].sessionActive) {
261 ALOGV("Pacer::onSessionCompleted: uid %d: not started", uid);
262 return;
263 }
264 ALOGV("Pacer::onSessionCompleted: uid %d: runningTime %lld", uid, runningTime.count() / 1000);
265 mUidHistoryMap[uid].sessionActive = false;
266 mUidHistoryMap[uid].burstCount++;
267 mUidHistoryMap[uid].burstDuration += runningTime;
268 mUidHistoryMap[uid].lastCompletedTime = std::chrono::steady_clock::now();
269}
270
Chong Zhang2a3c9672021-03-31 15:36:32 -0700271void TranscodingSessionController::Pacer::onSessionCancelled(uid_t uid) {
272 if (mUidHistoryMap.find(uid) == mUidHistoryMap.end()) {
273 ALOGV("Pacer::onSessionCancelled: uid %d: not present", uid);
274 return;
275 }
276 // This is only called if a uid is removed from a session (due to it being killed
277 // or the original submitting client was gone but session was kept for offline use).
278 // Since the uid is going to miss the onSessionCompleted(), we can't track this
279 // session, and have to check back at next onSessionStarted().
280 mUidHistoryMap[uid].sessionActive = false;
281}
282
Chong Zhang87d199c2021-03-01 19:02:18 -0800283///////////////////////////////////////////////////////////////////////////////
Chong Zhang457c6892021-02-01 15:34:20 -0800284
Chong Zhangbc062482020-10-14 16:43:53 -0700285TranscodingSessionController::TranscodingSessionController(
Chong Zhang457c6892021-02-01 15:34:20 -0800286 const TranscoderFactoryType& transcoderFactory,
Chong Zhangbc062482020-10-14 16:43:53 -0700287 const std::shared_ptr<UidPolicyInterface>& uidPolicy,
Chong Zhang8677f1f2021-01-21 20:37:35 +0000288 const std::shared_ptr<ResourcePolicyInterface>& resourcePolicy,
Chong Zhang87d199c2021-03-01 19:02:18 -0800289 const std::shared_ptr<ThermalPolicyInterface>& thermalPolicy,
290 const ControllerConfig* config)
Chong Zhang457c6892021-02-01 15:34:20 -0800291 : mTranscoderFactory(transcoderFactory),
Chong Zhangbc062482020-10-14 16:43:53 -0700292 mUidPolicy(uidPolicy),
293 mResourcePolicy(resourcePolicy),
Chong Zhang8677f1f2021-01-21 20:37:35 +0000294 mThermalPolicy(thermalPolicy),
Chong Zhangbc062482020-10-14 16:43:53 -0700295 mCurrentSession(nullptr),
296 mResourceLost(false) {
297 // Only push empty offline queue initially. Realtime queues are added when requests come in.
298 mUidSortedList.push_back(OFFLINE_UID);
299 mOfflineUidIterator = mUidSortedList.begin();
300 mSessionQueues.emplace(OFFLINE_UID, SessionQueueType());
Chong Zhangcf3f8ee2020-10-29 18:38:37 -0700301 mUidPackageNames[OFFLINE_UID] = "(offline)";
Chong Zhang8677f1f2021-01-21 20:37:35 +0000302 mThermalThrottling = thermalPolicy->getThrottlingStatus();
Chong Zhang87d199c2021-03-01 19:02:18 -0800303 if (config != nullptr) {
304 mConfig = *config;
305 }
306 mPacer.reset(new Pacer(mConfig));
307 ALOGD("@@@ watchdog %lld, burst count %d, burst time %d, burst threshold %d",
308 (long long)mConfig.watchdogTimeoutUs, mConfig.pacerBurstCountQuota,
309 mConfig.pacerBurstTimeQuotaSeconds, mConfig.pacerBurstThresholdMs);
Chong Zhangbc062482020-10-14 16:43:53 -0700310}
311
312TranscodingSessionController::~TranscodingSessionController() {}
313
Chong Zhanga1320c52020-12-15 14:30:12 -0800314void TranscodingSessionController::dumpSession_l(const Session& session, String8& result,
315 bool closedSession) {
316 const size_t SIZE = 256;
317 char buffer[SIZE];
318 const TranscodingRequestParcel& request = session.request;
319 snprintf(buffer, SIZE, " Session: %s, %s, %d%%\n", sessionToString(session.key).c_str(),
320 sessionStateToString(session.getState()), session.lastProgress);
321 result.append(buffer);
322 snprintf(buffer, SIZE, " pkg: %s\n", request.clientPackageName.c_str());
323 result.append(buffer);
324 snprintf(buffer, SIZE, " src: %s\n", request.sourceFilePath.c_str());
325 result.append(buffer);
326 snprintf(buffer, SIZE, " dst: %s\n", request.destinationFilePath.c_str());
327 result.append(buffer);
328
329 if (closedSession) {
330 snprintf(buffer, SIZE,
331 " waiting: %.1fs, running: %.1fs, paused: %.1fs, paused count: %d\n",
332 session.waitingTime.count() / 1000000.0f, session.runningTime.count() / 1000000.0f,
333 session.pausedTime.count() / 1000000.0f, session.pauseCount);
334 result.append(buffer);
335 }
336}
337
Chong Zhangbc062482020-10-14 16:43:53 -0700338void TranscodingSessionController::dumpAllSessions(int fd, const Vector<String16>& args __unused) {
339 String8 result;
340
341 const size_t SIZE = 256;
342 char buffer[SIZE];
343 std::scoped_lock lock{mLock};
344
Chong Zhanga1320c52020-12-15 14:30:12 -0800345 snprintf(buffer, SIZE, "\n========== Dumping live sessions queues =========\n");
Chong Zhangbc062482020-10-14 16:43:53 -0700346 result.append(buffer);
347 snprintf(buffer, SIZE, " Total num of Sessions: %zu\n", mSessionMap.size());
348 result.append(buffer);
349
350 std::vector<int32_t> uids(mUidSortedList.begin(), mUidSortedList.end());
Chong Zhangbc062482020-10-14 16:43:53 -0700351
352 for (int32_t i = 0; i < uids.size(); i++) {
353 const uid_t uid = uids[i];
354
355 if (mSessionQueues[uid].empty()) {
356 continue;
357 }
Chong Zhanga1320c52020-12-15 14:30:12 -0800358 snprintf(buffer, SIZE, " uid: %d, pkg: %s\n", uid,
Chong Zhangcf3f8ee2020-10-29 18:38:37 -0700359 mUidPackageNames.count(uid) > 0 ? mUidPackageNames[uid].c_str() : "(unknown)");
Chong Zhangbc062482020-10-14 16:43:53 -0700360 result.append(buffer);
361 snprintf(buffer, SIZE, " Num of sessions: %zu\n", mSessionQueues[uid].size());
362 result.append(buffer);
363 for (auto& sessionKey : mSessionQueues[uid]) {
364 auto sessionIt = mSessionMap.find(sessionKey);
365 if (sessionIt == mSessionMap.end()) {
366 snprintf(buffer, SIZE, "Failed to look up Session %s \n",
367 sessionToString(sessionKey).c_str());
368 result.append(buffer);
369 continue;
370 }
Chong Zhanga1320c52020-12-15 14:30:12 -0800371 dumpSession_l(sessionIt->second, result);
Chong Zhangbc062482020-10-14 16:43:53 -0700372 }
373 }
374
Chong Zhanga1320c52020-12-15 14:30:12 -0800375 snprintf(buffer, SIZE, "\n========== Dumping past sessions =========\n");
376 result.append(buffer);
Chong Zhang8677f1f2021-01-21 20:37:35 +0000377 for (auto& session : mSessionHistory) {
Chong Zhanga1320c52020-12-15 14:30:12 -0800378 dumpSession_l(session, result, true /*closedSession*/);
379 }
380
Chong Zhangbc062482020-10-14 16:43:53 -0700381 write(fd, result.string(), result.size());
382}
383
Chong Zhang87d199c2021-03-01 19:02:18 -0800384/*
385 * Returns nullptr if there is no session, or we're paused globally (due to resource lost,
386 * thermal throttling, etc.). Otherwise, return the session that should be run next.
387 */
Chong Zhangbc062482020-10-14 16:43:53 -0700388TranscodingSessionController::Session* TranscodingSessionController::getTopSession_l() {
389 if (mSessionMap.empty()) {
390 return nullptr;
391 }
Chong Zhang87d199c2021-03-01 19:02:18 -0800392
393 // Return nullptr if we're paused globally due to resource lost or thermal throttling.
394 if (((mResourcePolicy != nullptr && mResourceLost) ||
395 (mThermalPolicy != nullptr && mThermalThrottling))) {
396 return nullptr;
397 }
398
Chong Zhangbc062482020-10-14 16:43:53 -0700399 uid_t topUid = *mUidSortedList.begin();
Chong Zhangebd86d32021-03-29 11:30:56 -0700400 // If the current session is running, and it's in the topUid's queue, let it continue
401 // to run even if it's not the earliest in that uid's queue.
402 // For example, uid(B) is added to a session while it's pending in uid(A)'s queue, then
403 // B is brought to front which caused the session to run, then user switches back to A.
404 if (mCurrentSession != nullptr && mCurrentSession->getState() == Session::RUNNING &&
405 mCurrentSession->allClientUids.count(topUid) > 0) {
406 return mCurrentSession;
407 }
Chong Zhangbc062482020-10-14 16:43:53 -0700408 SessionKeyType topSessionKey = *mSessionQueues[topUid].begin();
409 return &mSessionMap[topSessionKey];
410}
411
Chong Zhang457c6892021-02-01 15:34:20 -0800412void TranscodingSessionController::setSessionState_l(Session* session, Session::State state) {
413 bool wasRunning = (session->getState() == Session::RUNNING);
414 session->setState(state);
415 bool isRunning = (session->getState() == Session::RUNNING);
416
417 if (wasRunning == isRunning) {
418 return;
419 }
420
421 // Currently we only have 1 running session, and we always put the previous
422 // session in non-running state before we run the new session, so it's okay
423 // to start/stop the watchdog here. If this assumption changes, we need to
424 // track the number of running sessions and start/stop watchdog based on that.
425 if (isRunning) {
426 mWatchdog->start(session->key);
427 } else {
428 mWatchdog->stop();
429 }
430}
431
Chong Zhanga1320c52020-12-15 14:30:12 -0800432void TranscodingSessionController::Session::setState(Session::State newState) {
433 if (state == newState) {
434 return;
435 }
Chong Zhang87d199c2021-03-01 19:02:18 -0800436 auto nowTime = std::chrono::steady_clock::now();
Chong Zhanga1320c52020-12-15 14:30:12 -0800437 if (state != INVALID) {
Chong Zhang87d199c2021-03-01 19:02:18 -0800438 std::chrono::microseconds elapsedTime =
439 std::chrono::duration_cast<std::chrono::microseconds>(nowTime - stateEnterTime);
Chong Zhanga1320c52020-12-15 14:30:12 -0800440 switch (state) {
441 case PAUSED:
442 pausedTime = pausedTime + elapsedTime;
443 break;
444 case RUNNING:
445 runningTime = runningTime + elapsedTime;
446 break;
447 case NOT_STARTED:
448 waitingTime = waitingTime + elapsedTime;
449 break;
450 default:
451 break;
452 }
453 }
454 if (newState == PAUSED) {
455 pauseCount++;
456 }
457 stateEnterTime = nowTime;
458 state = newState;
459}
460
Chong Zhangbc062482020-10-14 16:43:53 -0700461void TranscodingSessionController::updateCurrentSession_l() {
Chong Zhangbc062482020-10-14 16:43:53 -0700462 Session* curSession = mCurrentSession;
Chong Zhangebd86d32021-03-29 11:30:56 -0700463 Session* topSession = nullptr;
Chong Zhangbc062482020-10-14 16:43:53 -0700464
Chong Zhang87d199c2021-03-01 19:02:18 -0800465 // Delayed init of transcoder and watchdog.
466 if (mTranscoder == nullptr) {
467 mTranscoder = mTranscoderFactory(shared_from_this());
468 mWatchdog = std::make_shared<Watchdog>(this, mConfig.watchdogTimeoutUs);
Chong Zhang8677f1f2021-01-21 20:37:35 +0000469 }
470
Chong Zhang87d199c2021-03-01 19:02:18 -0800471 // If we found a different top session, or the top session's running state is not
472 // correct. Take some actions to ensure it's correct.
473 while ((topSession = getTopSession_l()) != curSession ||
474 (topSession != nullptr && !topSession->isRunning())) {
475 ALOGV("updateCurrentSession_l: topSession is %s, curSession is %s",
476 topSession == nullptr ? "null" : sessionToString(topSession->key).c_str(),
477 curSession == nullptr ? "null" : sessionToString(curSession->key).c_str());
Chong Zhang457c6892021-02-01 15:34:20 -0800478
Chong Zhang87d199c2021-03-01 19:02:18 -0800479 // If current session is running, pause it first. Note this is needed for either
480 // cases: 1) Top session is changing to another session, or 2) Top session is
481 // changing to null (which means we should be globally paused).
Chong Zhanga1320c52020-12-15 14:30:12 -0800482 if (curSession != nullptr && curSession->getState() == Session::RUNNING) {
Chong Zhangbc062482020-10-14 16:43:53 -0700483 mTranscoder->pause(curSession->key.first, curSession->key.second);
Chong Zhang457c6892021-02-01 15:34:20 -0800484 setSessionState_l(curSession, Session::PAUSED);
Chong Zhangbc062482020-10-14 16:43:53 -0700485 }
Chong Zhang87d199c2021-03-01 19:02:18 -0800486
487 if (topSession == nullptr) {
488 // Nothing more to run (either no session or globally paused).
489 break;
490 }
491
492 // Otherwise, ensure topSession is running.
493 if (topSession->getState() == Session::NOT_STARTED) {
Chong Zhangebd86d32021-03-29 11:30:56 -0700494 // Check if at least one client has quota to start the session.
495 bool keepForClient = false;
496 for (uid_t uid : topSession->allClientUids) {
497 if (mPacer->onSessionStarted(uid)) {
498 keepForClient = true;
499 // DO NOT break here, because book-keeping still needs to happen
500 // for the other uids.
501 }
502 }
503 if (!keepForClient) {
504 // Unfortunately all uids requesting this session are out of quota.
505 // Drop this session and try the next one.
Chong Zhang87d199c2021-03-01 19:02:18 -0800506 {
507 auto clientCallback = mSessionMap[topSession->key].callback.lock();
508 if (clientCallback != nullptr) {
509 clientCallback->onTranscodingFailed(
510 topSession->key.second, TranscodingErrorCode::kDroppedByService);
511 }
512 }
513 removeSession_l(topSession->key, Session::DROPPED_BY_PACER);
514 continue;
Chong Zhangbc062482020-10-14 16:43:53 -0700515 }
Chong Zhang87d199c2021-03-01 19:02:18 -0800516 mTranscoder->start(topSession->key.first, topSession->key.second, topSession->request,
517 topSession->callingUid, topSession->callback.lock());
518 setSessionState_l(topSession, Session::RUNNING);
519 } else if (topSession->getState() == Session::PAUSED) {
520 mTranscoder->resume(topSession->key.first, topSession->key.second, topSession->request,
521 topSession->callingUid, topSession->callback.lock());
Chong Zhang457c6892021-02-01 15:34:20 -0800522 setSessionState_l(topSession, Session::RUNNING);
Chong Zhangbc062482020-10-14 16:43:53 -0700523 }
Chong Zhang87d199c2021-03-01 19:02:18 -0800524 break;
Chong Zhangbc062482020-10-14 16:43:53 -0700525 }
526 mCurrentSession = topSession;
527}
528
Chong Zhangebd86d32021-03-29 11:30:56 -0700529void TranscodingSessionController::addUidToSession_l(uid_t clientUid,
530 const SessionKeyType& sessionKey) {
531 // If it's an offline session, the queue was already added in constructor.
532 // If it's a real-time sessions, check if a queue is already present for the uid,
533 // and add a new queue if needed.
534 if (clientUid != OFFLINE_UID) {
535 if (mSessionQueues.count(clientUid) == 0) {
536 mUidPolicy->registerMonitorUid(clientUid);
537 if (mUidPolicy->isUidOnTop(clientUid)) {
538 mUidSortedList.push_front(clientUid);
539 } else {
540 // Shouldn't be submitting real-time requests from non-top app,
541 // put it in front of the offline queue.
542 mUidSortedList.insert(mOfflineUidIterator, clientUid);
543 }
544 } else if (clientUid != *mUidSortedList.begin()) {
545 if (mUidPolicy->isUidOnTop(clientUid)) {
546 mUidSortedList.remove(clientUid);
547 mUidSortedList.push_front(clientUid);
548 }
549 }
550 }
551 // Append this session to the uid's queue.
552 mSessionQueues[clientUid].push_back(sessionKey);
553}
554
Chong Zhang2a3c9672021-03-31 15:36:32 -0700555void TranscodingSessionController::removeSession_l(
556 const SessionKeyType& sessionKey, Session::State finalState,
557 const std::shared_ptr<std::function<bool(uid_t uid)>>& keepUid) {
Chong Zhangbc062482020-10-14 16:43:53 -0700558 ALOGV("%s: session %s", __FUNCTION__, sessionToString(sessionKey).c_str());
559
560 if (mSessionMap.count(sessionKey) == 0) {
561 ALOGE("session %s doesn't exist", sessionToString(sessionKey).c_str());
562 return;
563 }
564
565 // Remove session from uid's queue.
Chong Zhangebd86d32021-03-29 11:30:56 -0700566 bool uidQueueRemoved = false;
Chong Zhang2a3c9672021-03-31 15:36:32 -0700567 std::unordered_set<uid_t> remainingUids;
Chong Zhangebd86d32021-03-29 11:30:56 -0700568 for (uid_t uid : mSessionMap[sessionKey].allClientUids) {
Chong Zhang2a3c9672021-03-31 15:36:32 -0700569 if (keepUid != nullptr) {
570 if ((*keepUid)(uid)) {
571 remainingUids.insert(uid);
572 continue;
573 }
574 // If we have uids to keep, the session is not going to any final
575 // state we can't use onSessionCompleted as the running time will
576 // not be valid. Only notify pacer to stop tracking this session.
577 mPacer->onSessionCancelled(uid);
Chong Zhangebd86d32021-03-29 11:30:56 -0700578 }
579 SessionQueueType& sessionQueue = mSessionQueues[uid];
580 auto it = std::find(sessionQueue.begin(), sessionQueue.end(), sessionKey);
581 if (it == sessionQueue.end()) {
582 ALOGW("couldn't find session %s in queue for uid %d",
583 sessionToString(sessionKey).c_str(), uid);
584 continue;
585 }
586 sessionQueue.erase(it);
587
588 // If this is the last session in a real-time queue, remove this uid's queue.
589 if (uid != OFFLINE_UID && sessionQueue.empty()) {
590 mUidSortedList.remove(uid);
591 mSessionQueues.erase(uid);
592 mUidPolicy->unregisterMonitorUid(uid);
593
594 uidQueueRemoved = true;
595 }
Chong Zhangbc062482020-10-14 16:43:53 -0700596 }
Chong Zhangbc062482020-10-14 16:43:53 -0700597
Chong Zhangebd86d32021-03-29 11:30:56 -0700598 if (uidQueueRemoved) {
Chong Zhangbc062482020-10-14 16:43:53 -0700599 std::unordered_set<uid_t> topUids = mUidPolicy->getTopUids();
600 moveUidsToTop_l(topUids, false /*preserveTopUid*/);
601 }
602
Chong Zhang2a3c9672021-03-31 15:36:32 -0700603 if (keepUid != nullptr) {
604 mSessionMap[sessionKey].allClientUids = remainingUids;
Chong Zhangebd86d32021-03-29 11:30:56 -0700605 return;
606 }
607
Chong Zhangbc062482020-10-14 16:43:53 -0700608 // Clear current session.
609 if (mCurrentSession == &mSessionMap[sessionKey]) {
610 mCurrentSession = nullptr;
611 }
612
Chong Zhang457c6892021-02-01 15:34:20 -0800613 setSessionState_l(&mSessionMap[sessionKey], finalState);
Chong Zhang87d199c2021-03-01 19:02:18 -0800614
Chong Zhang2a3c9672021-03-31 15:36:32 -0700615 // We can use onSessionCompleted() even for CANCELLED, because runningTime is
616 // now updated by setSessionState_l().
617 for (uid_t uid : mSessionMap[sessionKey].allClientUids) {
618 mPacer->onSessionCompleted(uid, mSessionMap[sessionKey].runningTime);
Chong Zhang87d199c2021-03-01 19:02:18 -0800619 }
620
Chong Zhanga1320c52020-12-15 14:30:12 -0800621 mSessionHistory.push_back(mSessionMap[sessionKey]);
622 if (mSessionHistory.size() > kSessionHistoryMax) {
623 mSessionHistory.erase(mSessionHistory.begin());
624 }
625
Chong Zhangbc062482020-10-14 16:43:53 -0700626 // Remove session from session map.
627 mSessionMap.erase(sessionKey);
628}
629
630/**
631 * Moves the set of uids to the front of mUidSortedList (which is used to pick
632 * the next session to run).
633 *
634 * This is called when 1) we received a onTopUidsChanged() callback from UidPolicy,
635 * or 2) we removed the session queue for a uid because it becomes empty.
636 *
637 * In case of 1), if there are multiple uids in the set, and the current front
638 * uid in mUidSortedList is still in the set, we try to keep that uid at front
639 * so that current session run is not interrupted. (This is not a concern for case 2)
640 * because the queue for a uid was just removed entirely.)
641 */
642void TranscodingSessionController::moveUidsToTop_l(const std::unordered_set<uid_t>& uids,
643 bool preserveTopUid) {
644 // If uid set is empty, nothing to do. Do not change the queue status.
645 if (uids.empty()) {
646 return;
647 }
648
649 // Save the current top uid.
650 uid_t curTopUid = *mUidSortedList.begin();
651 bool pushCurTopToFront = false;
652 int32_t numUidsMoved = 0;
653
654 // Go through the sorted uid list once, and move the ones in top set to front.
655 for (auto it = mUidSortedList.begin(); it != mUidSortedList.end();) {
656 uid_t uid = *it;
657
658 if (uid != OFFLINE_UID && uids.count(uid) > 0) {
659 it = mUidSortedList.erase(it);
660
661 // If this is the top we're preserving, don't push it here, push
662 // it after the for-loop.
663 if (uid == curTopUid && preserveTopUid) {
664 pushCurTopToFront = true;
665 } else {
666 mUidSortedList.push_front(uid);
667 }
668
669 // If we found all uids in the set, break out.
670 if (++numUidsMoved == uids.size()) {
671 break;
672 }
673 } else {
674 ++it;
675 }
676 }
677
678 if (pushCurTopToFront) {
679 mUidSortedList.push_front(curTopUid);
680 }
681}
682
683bool TranscodingSessionController::submit(
Linus Nilssona99f4042021-02-25 15:49:43 -0800684 ClientIdType clientId, SessionIdType sessionId, uid_t callingUid, uid_t clientUid,
Chong Zhangbc062482020-10-14 16:43:53 -0700685 const TranscodingRequestParcel& request,
686 const std::weak_ptr<ITranscodingClientCallback>& callback) {
687 SessionKeyType sessionKey = std::make_pair(clientId, sessionId);
688
689 ALOGV("%s: session %s, uid %d, prioirty %d", __FUNCTION__, sessionToString(sessionKey).c_str(),
Linus Nilssona99f4042021-02-25 15:49:43 -0800690 clientUid, (int32_t)request.priority);
Chong Zhangbc062482020-10-14 16:43:53 -0700691
692 std::scoped_lock lock{mLock};
693
694 if (mSessionMap.count(sessionKey) > 0) {
695 ALOGE("session %s already exists", sessionToString(sessionKey).c_str());
696 return false;
697 }
698
Chong Zhangcf3f8ee2020-10-29 18:38:37 -0700699 // Add the uid package name to the store of package names we already know.
Linus Nilssona99f4042021-02-25 15:49:43 -0800700 if (mUidPackageNames.count(clientUid) == 0) {
701 mUidPackageNames.emplace(clientUid, request.clientPackageName);
Chong Zhangcf3f8ee2020-10-29 18:38:37 -0700702 }
703
Chong Zhangbc062482020-10-14 16:43:53 -0700704 // TODO(chz): only support offline vs real-time for now. All kUnspecified sessions
705 // go to offline queue.
706 if (request.priority == TranscodingSessionPriority::kUnspecified) {
Linus Nilssona99f4042021-02-25 15:49:43 -0800707 clientUid = OFFLINE_UID;
Chong Zhangbc062482020-10-14 16:43:53 -0700708 }
709
710 // Add session to session map.
711 mSessionMap[sessionKey].key = sessionKey;
Linus Nilssona99f4042021-02-25 15:49:43 -0800712 mSessionMap[sessionKey].callingUid = callingUid;
Chong Zhangebd86d32021-03-29 11:30:56 -0700713 mSessionMap[sessionKey].allClientUids.insert(clientUid);
Chong Zhangbc062482020-10-14 16:43:53 -0700714 mSessionMap[sessionKey].request = request;
715 mSessionMap[sessionKey].callback = callback;
Chong Zhang457c6892021-02-01 15:34:20 -0800716 setSessionState_l(&mSessionMap[sessionKey], Session::NOT_STARTED);
Chong Zhangbc062482020-10-14 16:43:53 -0700717
Chong Zhangebd86d32021-03-29 11:30:56 -0700718 addUidToSession_l(clientUid, sessionKey);
Chong Zhangbc062482020-10-14 16:43:53 -0700719
720 updateCurrentSession_l();
721
722 validateState_l();
723 return true;
724}
725
726bool TranscodingSessionController::cancel(ClientIdType clientId, SessionIdType sessionId) {
727 SessionKeyType sessionKey = std::make_pair(clientId, sessionId);
728
729 ALOGV("%s: session %s", __FUNCTION__, sessionToString(sessionKey).c_str());
730
Chong Zhangebd86d32021-03-29 11:30:56 -0700731 std::list<SessionKeyType> sessionsToRemove, sessionsForOffline;
Chong Zhangbc062482020-10-14 16:43:53 -0700732
733 std::scoped_lock lock{mLock};
734
735 if (sessionId < 0) {
736 for (auto it = mSessionMap.begin(); it != mSessionMap.end(); ++it) {
Chong Zhangebd86d32021-03-29 11:30:56 -0700737 if (it->first.first == clientId) {
738 // If there is offline request, only keep the offline client;
739 // otherwise remove the session.
740 if (it->second.allClientUids.count(OFFLINE_UID) > 0) {
741 sessionsForOffline.push_back(it->first);
742 } else {
743 sessionsToRemove.push_back(it->first);
744 }
Chong Zhangbc062482020-10-14 16:43:53 -0700745 }
746 }
747 } else {
748 if (mSessionMap.count(sessionKey) == 0) {
749 ALOGE("session %s doesn't exist", sessionToString(sessionKey).c_str());
750 return false;
751 }
752 sessionsToRemove.push_back(sessionKey);
753 }
754
755 for (auto it = sessionsToRemove.begin(); it != sessionsToRemove.end(); ++it) {
756 // If the session has ever been started, stop it now.
757 // Note that stop() is needed even if the session is currently paused. This instructs
758 // the transcoder to discard any states for the session, otherwise the states may
759 // never be discarded.
Chong Zhanga1320c52020-12-15 14:30:12 -0800760 if (mSessionMap[*it].getState() != Session::NOT_STARTED) {
Chong Zhangbc062482020-10-14 16:43:53 -0700761 mTranscoder->stop(it->first, it->second);
762 }
763
764 // Remove the session.
Chong Zhanga1320c52020-12-15 14:30:12 -0800765 removeSession_l(*it, Session::CANCELED);
Chong Zhangbc062482020-10-14 16:43:53 -0700766 }
767
Chong Zhang2a3c9672021-03-31 15:36:32 -0700768 auto keepUid = std::make_shared<std::function<bool(uid_t)>>(
769 [](uid_t uid) { return uid == OFFLINE_UID; });
Chong Zhangebd86d32021-03-29 11:30:56 -0700770 for (auto it = sessionsForOffline.begin(); it != sessionsForOffline.end(); ++it) {
Chong Zhang2a3c9672021-03-31 15:36:32 -0700771 removeSession_l(*it, Session::CANCELED, keepUid);
Chong Zhangebd86d32021-03-29 11:30:56 -0700772 }
773
Chong Zhangbc062482020-10-14 16:43:53 -0700774 // Start next session.
775 updateCurrentSession_l();
776
777 validateState_l();
778 return true;
779}
780
Chong Zhangebd86d32021-03-29 11:30:56 -0700781bool TranscodingSessionController::addClientUid(ClientIdType clientId, SessionIdType sessionId,
782 uid_t clientUid) {
783 SessionKeyType sessionKey = std::make_pair(clientId, sessionId);
784
785 std::scoped_lock lock{mLock};
786
787 if (mSessionMap.count(sessionKey) == 0) {
788 ALOGE("session %s doesn't exist", sessionToString(sessionKey).c_str());
789 return false;
790 }
791
792 if (mSessionMap[sessionKey].allClientUids.count(clientUid) > 0) {
793 ALOGE("session %s already has uid %d", sessionToString(sessionKey).c_str(), clientUid);
794 return false;
795 }
796
797 mSessionMap[sessionKey].allClientUids.insert(clientUid);
798 addUidToSession_l(clientUid, sessionKey);
799
800 updateCurrentSession_l();
801
802 validateState_l();
803 return true;
804}
805
806bool TranscodingSessionController::getClientUids(ClientIdType clientId, SessionIdType sessionId,
807 std::vector<int32_t>* out_clientUids) {
808 SessionKeyType sessionKey = std::make_pair(clientId, sessionId);
809
810 std::scoped_lock lock{mLock};
811
812 if (mSessionMap.count(sessionKey) == 0) {
813 ALOGE("session %s doesn't exist", sessionToString(sessionKey).c_str());
814 return false;
815 }
816
817 out_clientUids->clear();
818 for (uid_t uid : mSessionMap[sessionKey].allClientUids) {
819 if (uid != OFFLINE_UID) {
820 out_clientUids->push_back(uid);
821 }
822 }
823 return true;
824}
825
Chong Zhangbc062482020-10-14 16:43:53 -0700826bool TranscodingSessionController::getSession(ClientIdType clientId, SessionIdType sessionId,
827 TranscodingRequestParcel* request) {
828 SessionKeyType sessionKey = std::make_pair(clientId, sessionId);
829
830 std::scoped_lock lock{mLock};
831
832 if (mSessionMap.count(sessionKey) == 0) {
833 ALOGE("session %s doesn't exist", sessionToString(sessionKey).c_str());
834 return false;
835 }
836
837 *(TranscodingRequest*)request = mSessionMap[sessionKey].request;
838 return true;
839}
840
841void TranscodingSessionController::notifyClient(ClientIdType clientId, SessionIdType sessionId,
842 const char* reason,
843 std::function<void(const SessionKeyType&)> func) {
844 SessionKeyType sessionKey = std::make_pair(clientId, sessionId);
845
846 std::scoped_lock lock{mLock};
847
848 if (mSessionMap.count(sessionKey) == 0) {
849 ALOGW("%s: ignoring %s for session %s that doesn't exist", __FUNCTION__, reason,
850 sessionToString(sessionKey).c_str());
851 return;
852 }
853
854 // Only ignore if session was never started. In particular, propagate the status
855 // to client if the session is paused. Transcoder could have posted finish when
856 // we're pausing it, and the finish arrived after we changed current session.
Chong Zhanga1320c52020-12-15 14:30:12 -0800857 if (mSessionMap[sessionKey].getState() == Session::NOT_STARTED) {
Chong Zhangbc062482020-10-14 16:43:53 -0700858 ALOGW("%s: ignoring %s for session %s that was never started", __FUNCTION__, reason,
859 sessionToString(sessionKey).c_str());
860 return;
861 }
862
863 ALOGV("%s: session %s %s", __FUNCTION__, sessionToString(sessionKey).c_str(), reason);
864 func(sessionKey);
865}
866
867void TranscodingSessionController::onStarted(ClientIdType clientId, SessionIdType sessionId) {
868 notifyClient(clientId, sessionId, "started", [=](const SessionKeyType& sessionKey) {
869 auto callback = mSessionMap[sessionKey].callback.lock();
870 if (callback != nullptr) {
871 callback->onTranscodingStarted(sessionId);
872 }
873 });
874}
875
876void TranscodingSessionController::onPaused(ClientIdType clientId, SessionIdType sessionId) {
877 notifyClient(clientId, sessionId, "paused", [=](const SessionKeyType& sessionKey) {
878 auto callback = mSessionMap[sessionKey].callback.lock();
879 if (callback != nullptr) {
880 callback->onTranscodingPaused(sessionId);
881 }
882 });
883}
884
885void TranscodingSessionController::onResumed(ClientIdType clientId, SessionIdType sessionId) {
886 notifyClient(clientId, sessionId, "resumed", [=](const SessionKeyType& sessionKey) {
887 auto callback = mSessionMap[sessionKey].callback.lock();
888 if (callback != nullptr) {
889 callback->onTranscodingResumed(sessionId);
890 }
891 });
892}
893
894void TranscodingSessionController::onFinish(ClientIdType clientId, SessionIdType sessionId) {
895 notifyClient(clientId, sessionId, "finish", [=](const SessionKeyType& sessionKey) {
896 {
897 auto clientCallback = mSessionMap[sessionKey].callback.lock();
898 if (clientCallback != nullptr) {
899 clientCallback->onTranscodingFinished(
900 sessionId, TranscodingResultParcel({sessionId, -1 /*actualBitrateBps*/,
901 std::nullopt /*sessionStats*/}));
902 }
903 }
904
905 // Remove the session.
Chong Zhanga1320c52020-12-15 14:30:12 -0800906 removeSession_l(sessionKey, Session::FINISHED);
Chong Zhangbc062482020-10-14 16:43:53 -0700907
908 // Start next session.
909 updateCurrentSession_l();
910
911 validateState_l();
912 });
913}
914
915void TranscodingSessionController::onError(ClientIdType clientId, SessionIdType sessionId,
916 TranscodingErrorCode err) {
917 notifyClient(clientId, sessionId, "error", [=](const SessionKeyType& sessionKey) {
Chong Zhang457c6892021-02-01 15:34:20 -0800918 if (err == TranscodingErrorCode::kWatchdogTimeout) {
919 // Abandon the transcoder, as its handler thread might be stuck in some call to
920 // MediaTranscoder altogether, and may not be able to handle any new tasks.
921 mTranscoder->stop(clientId, sessionId, true /*abandon*/);
922 // Clear the last ref count before we create new transcoder.
923 mTranscoder = nullptr;
Chong Zhang87d199c2021-03-01 19:02:18 -0800924 mTranscoder = mTranscoderFactory(shared_from_this());
Chong Zhang457c6892021-02-01 15:34:20 -0800925 }
926
Chong Zhangbc062482020-10-14 16:43:53 -0700927 {
928 auto clientCallback = mSessionMap[sessionKey].callback.lock();
929 if (clientCallback != nullptr) {
930 clientCallback->onTranscodingFailed(sessionId, err);
931 }
932 }
933
934 // Remove the session.
Chong Zhanga1320c52020-12-15 14:30:12 -0800935 removeSession_l(sessionKey, Session::ERROR);
Chong Zhangbc062482020-10-14 16:43:53 -0700936
937 // Start next session.
938 updateCurrentSession_l();
939
940 validateState_l();
941 });
942}
943
944void TranscodingSessionController::onProgressUpdate(ClientIdType clientId, SessionIdType sessionId,
945 int32_t progress) {
946 notifyClient(clientId, sessionId, "progress", [=](const SessionKeyType& sessionKey) {
947 auto callback = mSessionMap[sessionKey].callback.lock();
948 if (callback != nullptr) {
949 callback->onProgressUpdate(sessionId, progress);
950 }
951 mSessionMap[sessionKey].lastProgress = progress;
952 });
953}
954
Chong Zhang457c6892021-02-01 15:34:20 -0800955void TranscodingSessionController::onHeartBeat(ClientIdType clientId, SessionIdType sessionId) {
956 notifyClient(clientId, sessionId, "heart-beat",
957 [=](const SessionKeyType& /*sessionKey*/) { mWatchdog->keepAlive(); });
958}
959
Chong Zhangeffd8962020-12-02 14:29:09 -0800960void TranscodingSessionController::onResourceLost(ClientIdType clientId, SessionIdType sessionId) {
Chong Zhangbc062482020-10-14 16:43:53 -0700961 ALOGI("%s", __FUNCTION__);
962
Chong Zhangeffd8962020-12-02 14:29:09 -0800963 notifyClient(clientId, sessionId, "resource_lost", [=](const SessionKeyType& sessionKey) {
964 if (mResourceLost) {
965 return;
Chong Zhangbc062482020-10-14 16:43:53 -0700966 }
Chong Zhangbc062482020-10-14 16:43:53 -0700967
Chong Zhangeffd8962020-12-02 14:29:09 -0800968 Session* resourceLostSession = &mSessionMap[sessionKey];
Chong Zhanga1320c52020-12-15 14:30:12 -0800969 if (resourceLostSession->getState() != Session::RUNNING) {
Chong Zhangeffd8962020-12-02 14:29:09 -0800970 ALOGW("session %s lost resource but is no longer running",
Chong Zhanga1320c52020-12-15 14:30:12 -0800971 sessionToString(sessionKey).c_str());
Chong Zhangeffd8962020-12-02 14:29:09 -0800972 return;
973 }
974 // If we receive a resource loss event, the transcoder already paused the transcoding,
975 // so we don't need to call onPaused() to pause it. However, we still need to notify
976 // the client and update the session state here.
Chong Zhang457c6892021-02-01 15:34:20 -0800977 setSessionState_l(resourceLostSession, Session::PAUSED);
Chong Zhangeffd8962020-12-02 14:29:09 -0800978 // Notify the client as a paused event.
979 auto clientCallback = resourceLostSession->callback.lock();
980 if (clientCallback != nullptr) {
981 clientCallback->onTranscodingPaused(sessionKey.second);
982 }
Chong Zhang8677f1f2021-01-21 20:37:35 +0000983 if (mResourcePolicy != nullptr) {
984 mResourcePolicy->setPidResourceLost(resourceLostSession->request.clientPid);
985 }
Chong Zhangeffd8962020-12-02 14:29:09 -0800986 mResourceLost = true;
987
988 validateState_l();
989 });
Chong Zhangbc062482020-10-14 16:43:53 -0700990}
991
992void TranscodingSessionController::onTopUidsChanged(const std::unordered_set<uid_t>& uids) {
993 if (uids.empty()) {
994 ALOGW("%s: ignoring empty uids", __FUNCTION__);
995 return;
996 }
997
998 std::string uidStr;
999 for (auto it = uids.begin(); it != uids.end(); it++) {
1000 if (!uidStr.empty()) {
1001 uidStr += ", ";
1002 }
1003 uidStr += std::to_string(*it);
1004 }
1005
1006 ALOGD("%s: topUids: size %zu, uids: %s", __FUNCTION__, uids.size(), uidStr.c_str());
1007
1008 std::scoped_lock lock{mLock};
1009
1010 moveUidsToTop_l(uids, true /*preserveTopUid*/);
1011
1012 updateCurrentSession_l();
1013
1014 validateState_l();
1015}
1016
Chong Zhang2a3c9672021-03-31 15:36:32 -07001017void TranscodingSessionController::onUidGone(uid_t goneUid) {
1018 ALOGD("%s: gone uid %u", __FUNCTION__, goneUid);
1019
1020 std::list<SessionKeyType> sessionsToRemove, sessionsForOtherUids;
1021
1022 std::scoped_lock lock{mLock};
1023
1024 for (auto it = mSessionMap.begin(); it != mSessionMap.end(); ++it) {
1025 if (it->second.allClientUids.count(goneUid) > 0) {
1026 // If goneUid is the only uid, remove the session; otherwise, only
1027 // remove the uid from the session.
1028 if (it->second.allClientUids.size() > 1) {
1029 sessionsForOtherUids.push_back(it->first);
1030 } else {
1031 sessionsToRemove.push_back(it->first);
1032 }
1033 }
1034 }
1035
1036 for (auto it = sessionsToRemove.begin(); it != sessionsToRemove.end(); ++it) {
1037 // If the session has ever been started, stop it now.
1038 // Note that stop() is needed even if the session is currently paused. This instructs
1039 // the transcoder to discard any states for the session, otherwise the states may
1040 // never be discarded.
1041 if (mSessionMap[*it].getState() != Session::NOT_STARTED) {
1042 mTranscoder->stop(it->first, it->second);
1043 }
1044
1045 {
1046 auto clientCallback = mSessionMap[*it].callback.lock();
1047 if (clientCallback != nullptr) {
1048 clientCallback->onTranscodingFailed(it->second,
1049 TranscodingErrorCode::kUidGoneCancelled);
1050 }
1051 }
1052
1053 // Remove the session.
1054 removeSession_l(*it, Session::CANCELED);
1055 }
1056
1057 auto keepUid = std::make_shared<std::function<bool(uid_t)>>(
1058 [goneUid](uid_t uid) { return uid != goneUid; });
1059 for (auto it = sessionsForOtherUids.begin(); it != sessionsForOtherUids.end(); ++it) {
1060 removeSession_l(*it, Session::CANCELED, keepUid);
1061 }
1062
1063 // Start next session.
1064 updateCurrentSession_l();
1065
1066 validateState_l();
1067}
1068
Chong Zhangbc062482020-10-14 16:43:53 -07001069void TranscodingSessionController::onResourceAvailable() {
1070 std::scoped_lock lock{mLock};
1071
1072 if (!mResourceLost) {
1073 return;
1074 }
1075
1076 ALOGI("%s", __FUNCTION__);
1077
1078 mResourceLost = false;
1079 updateCurrentSession_l();
1080
1081 validateState_l();
1082}
1083
Chong Zhang8677f1f2021-01-21 20:37:35 +00001084void TranscodingSessionController::onThrottlingStarted() {
1085 std::scoped_lock lock{mLock};
1086
1087 if (mThermalThrottling) {
1088 return;
1089 }
1090
1091 ALOGI("%s", __FUNCTION__);
1092
1093 mThermalThrottling = true;
1094 updateCurrentSession_l();
1095
1096 validateState_l();
1097}
1098
1099void TranscodingSessionController::onThrottlingStopped() {
1100 std::scoped_lock lock{mLock};
1101
1102 if (!mThermalThrottling) {
1103 return;
1104 }
1105
1106 ALOGI("%s", __FUNCTION__);
1107
1108 mThermalThrottling = false;
1109 updateCurrentSession_l();
1110
1111 validateState_l();
1112}
1113
Chong Zhangbc062482020-10-14 16:43:53 -07001114void TranscodingSessionController::validateState_l() {
1115#ifdef VALIDATE_STATE
1116 LOG_ALWAYS_FATAL_IF(mSessionQueues.count(OFFLINE_UID) != 1,
1117 "mSessionQueues offline queue number is not 1");
1118 LOG_ALWAYS_FATAL_IF(*mOfflineUidIterator != OFFLINE_UID,
1119 "mOfflineUidIterator not pointing to offline uid");
1120 LOG_ALWAYS_FATAL_IF(mUidSortedList.size() != mSessionQueues.size(),
Chong Zhangebd86d32021-03-29 11:30:56 -07001121 "mUidSortedList and mSessionQueues size mismatch, %zu vs %zu",
1122 mUidSortedList.size(), mSessionQueues.size());
Chong Zhangbc062482020-10-14 16:43:53 -07001123
1124 int32_t totalSessions = 0;
1125 for (auto uid : mUidSortedList) {
1126 LOG_ALWAYS_FATAL_IF(mSessionQueues.count(uid) != 1,
1127 "mSessionQueues count for uid %d is not 1", uid);
1128 for (auto& sessionKey : mSessionQueues[uid]) {
1129 LOG_ALWAYS_FATAL_IF(mSessionMap.count(sessionKey) != 1,
1130 "mSessions count for session %s is not 1",
1131 sessionToString(sessionKey).c_str());
1132 }
1133
1134 totalSessions += mSessionQueues[uid].size();
1135 }
Chong Zhangebd86d32021-03-29 11:30:56 -07001136 int32_t totalSessionsAlternative = 0;
1137 for (auto const& s : mSessionMap) {
1138 totalSessionsAlternative += s.second.allClientUids.size();
1139 }
1140 LOG_ALWAYS_FATAL_IF(totalSessions != totalSessionsAlternative,
1141 "session count (including dup) from mSessionQueues doesn't match that from "
1142 "mSessionMap, %d vs %d",
1143 totalSessions, totalSessionsAlternative);
Chong Zhangbc062482020-10-14 16:43:53 -07001144#endif // VALIDATE_STATE
1145}
1146
1147} // namespace android