blob: 502bd591d1330c83172c4effac5ded1838d9874d [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>
Chong Zhang45a31632021-04-22 16:15:19 -070025#include <utils/AndroidThreads.h>
Chong Zhangbc062482020-10-14 16:43:53 -070026#include <utils/Log.h>
27
Chong Zhang457c6892021-02-01 15:34:20 -080028#include <thread>
Chong Zhangbc062482020-10-14 16:43:53 -070029#include <utility>
30
31namespace android {
32
33static_assert((SessionIdType)-1 < 0, "SessionIdType should be signed");
34
35constexpr static uid_t OFFLINE_UID = -1;
Chong Zhanga1320c52020-12-15 14:30:12 -080036constexpr static size_t kSessionHistoryMax = 100;
Chong Zhangbc062482020-10-14 16:43:53 -070037
38//static
39String8 TranscodingSessionController::sessionToString(const SessionKeyType& sessionKey) {
40 return String8::format("{client:%lld, session:%d}", (long long)sessionKey.first,
41 sessionKey.second);
42}
43
44//static
45const char* TranscodingSessionController::sessionStateToString(const Session::State sessionState) {
46 switch (sessionState) {
47 case Session::State::NOT_STARTED:
48 return "NOT_STARTED";
49 case Session::State::RUNNING:
50 return "RUNNING";
51 case Session::State::PAUSED:
52 return "PAUSED";
Chong Zhanga1320c52020-12-15 14:30:12 -080053 case Session::State::FINISHED:
54 return "FINISHED";
55 case Session::State::CANCELED:
56 return "CANCELED";
57 case Session::State::ERROR:
58 return "ERROR";
Chong Zhangbc062482020-10-14 16:43:53 -070059 default:
60 break;
61 }
62 return "(unknown)";
63}
64
Chong Zhang457c6892021-02-01 15:34:20 -080065///////////////////////////////////////////////////////////////////////////////
66struct TranscodingSessionController::Watchdog {
67 Watchdog(TranscodingSessionController* owner, int64_t timeoutUs);
68 ~Watchdog();
69
70 // Starts monitoring the session.
71 void start(const SessionKeyType& key);
72 // Stops monitoring the session.
73 void stop();
74 // Signals that the session is still alive. Must be sent at least every mTimeoutUs.
75 // (Timeout will happen if no ping in mTimeoutUs since the last ping.)
76 void keepAlive();
77
78private:
79 void threadLoop();
80 void updateTimer_l();
81
82 TranscodingSessionController* mOwner;
83 const int64_t mTimeoutUs;
84 mutable std::mutex mLock;
85 std::condition_variable mCondition GUARDED_BY(mLock);
86 // Whether watchdog is monitoring a session for timeout.
87 bool mActive GUARDED_BY(mLock);
88 // Whether watchdog is aborted and the monitoring thread should exit.
89 bool mAbort GUARDED_BY(mLock);
90 // When watchdog is active, the next timeout time point.
Chong Zhang87d199c2021-03-01 19:02:18 -080091 std::chrono::steady_clock::time_point mNextTimeoutTime GUARDED_BY(mLock);
Chong Zhang457c6892021-02-01 15:34:20 -080092 // When watchdog is active, the session being watched.
93 SessionKeyType mSessionToWatch GUARDED_BY(mLock);
94 std::thread mThread;
95};
96
Chong Zhang457c6892021-02-01 15:34:20 -080097TranscodingSessionController::Watchdog::Watchdog(TranscodingSessionController* owner,
98 int64_t timeoutUs)
99 : mOwner(owner),
100 mTimeoutUs(timeoutUs),
101 mActive(false),
102 mAbort(false),
103 mThread(&Watchdog::threadLoop, this) {
104 ALOGV("Watchdog CTOR: %p", this);
105}
106
107TranscodingSessionController::Watchdog::~Watchdog() {
108 ALOGV("Watchdog DTOR: %p", this);
109
110 {
111 // Exit the looper thread.
112 std::scoped_lock lock{mLock};
113
114 mAbort = true;
115 mCondition.notify_one();
116 }
117
118 mThread.join();
119 ALOGV("Watchdog DTOR: %p, done.", this);
120}
121
122void TranscodingSessionController::Watchdog::start(const SessionKeyType& key) {
123 std::scoped_lock lock{mLock};
124
125 if (!mActive) {
126 ALOGI("Watchdog start: %s", sessionToString(key).c_str());
127
128 mActive = true;
129 mSessionToWatch = key;
130 updateTimer_l();
131 mCondition.notify_one();
132 }
133}
134
135void TranscodingSessionController::Watchdog::stop() {
136 std::scoped_lock lock{mLock};
137
138 if (mActive) {
139 ALOGI("Watchdog stop: %s", sessionToString(mSessionToWatch).c_str());
140
141 mActive = false;
142 mCondition.notify_one();
143 }
144}
145
146void TranscodingSessionController::Watchdog::keepAlive() {
147 std::scoped_lock lock{mLock};
148
149 if (mActive) {
150 ALOGI("Watchdog keepAlive: %s", sessionToString(mSessionToWatch).c_str());
151
152 updateTimer_l();
153 mCondition.notify_one();
154 }
155}
156
157// updateTimer_l() is only called with lock held.
158void TranscodingSessionController::Watchdog::updateTimer_l() NO_THREAD_SAFETY_ANALYSIS {
159 std::chrono::microseconds timeout(mTimeoutUs);
Chong Zhang87d199c2021-03-01 19:02:18 -0800160 mNextTimeoutTime = std::chrono::steady_clock::now() + timeout;
Chong Zhang457c6892021-02-01 15:34:20 -0800161}
162
163// Unfortunately std::unique_lock is incompatible with -Wthread-safety.
164void TranscodingSessionController::Watchdog::threadLoop() NO_THREAD_SAFETY_ANALYSIS {
Chong Zhang45a31632021-04-22 16:15:19 -0700165 androidSetThreadPriority(0 /*tid (0 = current) */, ANDROID_PRIORITY_BACKGROUND);
Chong Zhang457c6892021-02-01 15:34:20 -0800166 std::unique_lock<std::mutex> lock{mLock};
167
168 while (!mAbort) {
169 if (!mActive) {
170 mCondition.wait(lock);
171 continue;
172 }
173 // Watchdog active, wait till next timeout time.
174 if (mCondition.wait_until(lock, mNextTimeoutTime) == std::cv_status::timeout) {
175 // If timeout happens, report timeout and deactivate watchdog.
176 mActive = false;
177 // Make a copy of session key, as once we unlock, it could be unprotected.
178 SessionKeyType sessionKey = mSessionToWatch;
179
180 ALOGE("Watchdog timeout: %s", sessionToString(sessionKey).c_str());
181
182 lock.unlock();
183 mOwner->onError(sessionKey.first, sessionKey.second,
184 TranscodingErrorCode::kWatchdogTimeout);
185 lock.lock();
186 }
187 }
188}
189///////////////////////////////////////////////////////////////////////////////
Chong Zhang87d199c2021-03-01 19:02:18 -0800190struct TranscodingSessionController::Pacer {
191 Pacer(const ControllerConfig& config)
192 : mBurstThresholdMs(config.pacerBurstThresholdMs),
193 mBurstCountQuota(config.pacerBurstCountQuota),
194 mBurstTimeQuotaSec(config.pacerBurstTimeQuotaSeconds) {}
195
196 ~Pacer() = default;
197
Chong Zhang87d199c2021-03-01 19:02:18 -0800198 bool onSessionStarted(uid_t uid);
Chong Zhangebd86d32021-03-29 11:30:56 -0700199 void onSessionCompleted(uid_t uid, std::chrono::microseconds runningTime);
Chong Zhang2a3c9672021-03-31 15:36:32 -0700200 void onSessionCancelled(uid_t uid);
Chong Zhang87d199c2021-03-01 19:02:18 -0800201
202private:
203 // Threshold of time between finish/start below which a back-to-back start is counted.
204 int32_t mBurstThresholdMs;
205 // Maximum allowed back-to-back start count.
206 int32_t mBurstCountQuota;
207 // Maximum allowed back-to-back running time.
208 int32_t mBurstTimeQuotaSec;
209
210 struct UidHistoryEntry {
Chong Zhangebd86d32021-03-29 11:30:56 -0700211 bool sessionActive = false;
Chong Zhang87d199c2021-03-01 19:02:18 -0800212 int32_t burstCount = 0;
213 std::chrono::steady_clock::duration burstDuration{0};
Chong Zhangebd86d32021-03-29 11:30:56 -0700214 std::chrono::steady_clock::time_point lastCompletedTime;
Chong Zhang87d199c2021-03-01 19:02:18 -0800215 };
216 std::map<uid_t, UidHistoryEntry> mUidHistoryMap;
217};
218
Chong Zhangebd86d32021-03-29 11:30:56 -0700219bool TranscodingSessionController::Pacer::onSessionStarted(uid_t uid) {
220 // If uid doesn't exist, only insert the entry and mark session active. Skip quota checking.
Chong Zhang87d199c2021-03-01 19:02:18 -0800221 if (mUidHistoryMap.find(uid) == mUidHistoryMap.end()) {
222 mUidHistoryMap.emplace(uid, UidHistoryEntry{});
Chong Zhangebd86d32021-03-29 11:30:56 -0700223 mUidHistoryMap[uid].sessionActive = true;
224 ALOGV("Pacer::onSessionStarted: uid %d: new", uid);
Chong Zhang87d199c2021-03-01 19:02:18 -0800225 return true;
226 }
227
228 // TODO: if Thermal throttling or resoure lost happened to occurr between this start
229 // and the previous completion, we should deduct the paused time from the elapsed time.
230 // (Individual session's pause time, on the other hand, doesn't need to be deducted
231 // because it doesn't affect the gap between last completion and the start.
232 auto timeSinceLastComplete =
233 std::chrono::steady_clock::now() - mUidHistoryMap[uid].lastCompletedTime;
234 if (mUidHistoryMap[uid].burstCount >= mBurstCountQuota &&
235 mUidHistoryMap[uid].burstDuration >= std::chrono::seconds(mBurstTimeQuotaSec)) {
Chong Zhangebd86d32021-03-29 11:30:56 -0700236 ALOGW("Pacer::onSessionStarted: uid %d: over quota, burst count %d, time %lldms", uid,
237 mUidHistoryMap[uid].burstCount,
238 (long long)mUidHistoryMap[uid].burstDuration.count() / 1000000);
Chong Zhang87d199c2021-03-01 19:02:18 -0800239 return false;
240 }
241
242 // If not over quota, allow the session, and reset as long as this is not too close
243 // to previous completion.
244 if (timeSinceLastComplete > std::chrono::milliseconds(mBurstThresholdMs)) {
Chong Zhangebd86d32021-03-29 11:30:56 -0700245 ALOGV("Pacer::onSessionStarted: uid %d: reset quota", uid);
Chong Zhang87d199c2021-03-01 19:02:18 -0800246 mUidHistoryMap[uid].burstCount = 0;
247 mUidHistoryMap[uid].burstDuration = std::chrono::milliseconds(0);
248 } else {
Chong Zhangebd86d32021-03-29 11:30:56 -0700249 ALOGV("Pacer::onSessionStarted: uid %d: burst count %d, time %lldms", uid,
250 mUidHistoryMap[uid].burstCount,
251 (long long)mUidHistoryMap[uid].burstDuration.count() / 1000000);
Chong Zhang87d199c2021-03-01 19:02:18 -0800252 }
253
Chong Zhangebd86d32021-03-29 11:30:56 -0700254 mUidHistoryMap[uid].sessionActive = true;
Chong Zhang87d199c2021-03-01 19:02:18 -0800255 return true;
256}
257
Chong Zhangebd86d32021-03-29 11:30:56 -0700258void TranscodingSessionController::Pacer::onSessionCompleted(
259 uid_t uid, std::chrono::microseconds runningTime) {
260 // Skip quota update if this uid missed the start. (Could happen if the uid is added via
261 // addClientUid() after the session start.)
262 if (mUidHistoryMap.find(uid) == mUidHistoryMap.end() || !mUidHistoryMap[uid].sessionActive) {
263 ALOGV("Pacer::onSessionCompleted: uid %d: not started", uid);
264 return;
265 }
266 ALOGV("Pacer::onSessionCompleted: uid %d: runningTime %lld", uid, runningTime.count() / 1000);
267 mUidHistoryMap[uid].sessionActive = false;
268 mUidHistoryMap[uid].burstCount++;
269 mUidHistoryMap[uid].burstDuration += runningTime;
270 mUidHistoryMap[uid].lastCompletedTime = std::chrono::steady_clock::now();
271}
272
Chong Zhang2a3c9672021-03-31 15:36:32 -0700273void TranscodingSessionController::Pacer::onSessionCancelled(uid_t uid) {
274 if (mUidHistoryMap.find(uid) == mUidHistoryMap.end()) {
275 ALOGV("Pacer::onSessionCancelled: uid %d: not present", uid);
276 return;
277 }
278 // This is only called if a uid is removed from a session (due to it being killed
279 // or the original submitting client was gone but session was kept for offline use).
280 // Since the uid is going to miss the onSessionCompleted(), we can't track this
281 // session, and have to check back at next onSessionStarted().
282 mUidHistoryMap[uid].sessionActive = false;
283}
284
Chong Zhang87d199c2021-03-01 19:02:18 -0800285///////////////////////////////////////////////////////////////////////////////
Chong Zhang457c6892021-02-01 15:34:20 -0800286
Chong Zhangbc062482020-10-14 16:43:53 -0700287TranscodingSessionController::TranscodingSessionController(
Chong Zhang457c6892021-02-01 15:34:20 -0800288 const TranscoderFactoryType& transcoderFactory,
Chong Zhangbc062482020-10-14 16:43:53 -0700289 const std::shared_ptr<UidPolicyInterface>& uidPolicy,
Chong Zhang8677f1f2021-01-21 20:37:35 +0000290 const std::shared_ptr<ResourcePolicyInterface>& resourcePolicy,
Chong Zhang87d199c2021-03-01 19:02:18 -0800291 const std::shared_ptr<ThermalPolicyInterface>& thermalPolicy,
292 const ControllerConfig* config)
Chong Zhang457c6892021-02-01 15:34:20 -0800293 : mTranscoderFactory(transcoderFactory),
Chong Zhangbc062482020-10-14 16:43:53 -0700294 mUidPolicy(uidPolicy),
295 mResourcePolicy(resourcePolicy),
Chong Zhang8677f1f2021-01-21 20:37:35 +0000296 mThermalPolicy(thermalPolicy),
Chong Zhangbc062482020-10-14 16:43:53 -0700297 mCurrentSession(nullptr),
298 mResourceLost(false) {
299 // Only push empty offline queue initially. Realtime queues are added when requests come in.
300 mUidSortedList.push_back(OFFLINE_UID);
301 mOfflineUidIterator = mUidSortedList.begin();
302 mSessionQueues.emplace(OFFLINE_UID, SessionQueueType());
Chong Zhangcf3f8ee2020-10-29 18:38:37 -0700303 mUidPackageNames[OFFLINE_UID] = "(offline)";
Chong Zhang8677f1f2021-01-21 20:37:35 +0000304 mThermalThrottling = thermalPolicy->getThrottlingStatus();
Chong Zhang87d199c2021-03-01 19:02:18 -0800305 if (config != nullptr) {
306 mConfig = *config;
307 }
308 mPacer.reset(new Pacer(mConfig));
309 ALOGD("@@@ watchdog %lld, burst count %d, burst time %d, burst threshold %d",
310 (long long)mConfig.watchdogTimeoutUs, mConfig.pacerBurstCountQuota,
311 mConfig.pacerBurstTimeQuotaSeconds, mConfig.pacerBurstThresholdMs);
Chong Zhangbc062482020-10-14 16:43:53 -0700312}
313
314TranscodingSessionController::~TranscodingSessionController() {}
315
Chong Zhanga1320c52020-12-15 14:30:12 -0800316void TranscodingSessionController::dumpSession_l(const Session& session, String8& result,
317 bool closedSession) {
318 const size_t SIZE = 256;
319 char buffer[SIZE];
320 const TranscodingRequestParcel& request = session.request;
321 snprintf(buffer, SIZE, " Session: %s, %s, %d%%\n", sessionToString(session.key).c_str(),
322 sessionStateToString(session.getState()), session.lastProgress);
323 result.append(buffer);
324 snprintf(buffer, SIZE, " pkg: %s\n", request.clientPackageName.c_str());
325 result.append(buffer);
326 snprintf(buffer, SIZE, " src: %s\n", request.sourceFilePath.c_str());
327 result.append(buffer);
328 snprintf(buffer, SIZE, " dst: %s\n", request.destinationFilePath.c_str());
329 result.append(buffer);
330
331 if (closedSession) {
332 snprintf(buffer, SIZE,
333 " waiting: %.1fs, running: %.1fs, paused: %.1fs, paused count: %d\n",
334 session.waitingTime.count() / 1000000.0f, session.runningTime.count() / 1000000.0f,
335 session.pausedTime.count() / 1000000.0f, session.pauseCount);
336 result.append(buffer);
337 }
338}
339
Chong Zhangbc062482020-10-14 16:43:53 -0700340void TranscodingSessionController::dumpAllSessions(int fd, const Vector<String16>& args __unused) {
341 String8 result;
342
343 const size_t SIZE = 256;
344 char buffer[SIZE];
345 std::scoped_lock lock{mLock};
346
Chong Zhanga1320c52020-12-15 14:30:12 -0800347 snprintf(buffer, SIZE, "\n========== Dumping live sessions queues =========\n");
Chong Zhangbc062482020-10-14 16:43:53 -0700348 result.append(buffer);
349 snprintf(buffer, SIZE, " Total num of Sessions: %zu\n", mSessionMap.size());
350 result.append(buffer);
351
352 std::vector<int32_t> uids(mUidSortedList.begin(), mUidSortedList.end());
Chong Zhangbc062482020-10-14 16:43:53 -0700353
354 for (int32_t i = 0; i < uids.size(); i++) {
355 const uid_t uid = uids[i];
356
357 if (mSessionQueues[uid].empty()) {
358 continue;
359 }
Chong Zhanga1320c52020-12-15 14:30:12 -0800360 snprintf(buffer, SIZE, " uid: %d, pkg: %s\n", uid,
Chong Zhangcf3f8ee2020-10-29 18:38:37 -0700361 mUidPackageNames.count(uid) > 0 ? mUidPackageNames[uid].c_str() : "(unknown)");
Chong Zhangbc062482020-10-14 16:43:53 -0700362 result.append(buffer);
363 snprintf(buffer, SIZE, " Num of sessions: %zu\n", mSessionQueues[uid].size());
364 result.append(buffer);
365 for (auto& sessionKey : mSessionQueues[uid]) {
366 auto sessionIt = mSessionMap.find(sessionKey);
367 if (sessionIt == mSessionMap.end()) {
368 snprintf(buffer, SIZE, "Failed to look up Session %s \n",
369 sessionToString(sessionKey).c_str());
370 result.append(buffer);
371 continue;
372 }
Chong Zhanga1320c52020-12-15 14:30:12 -0800373 dumpSession_l(sessionIt->second, result);
Chong Zhangbc062482020-10-14 16:43:53 -0700374 }
375 }
376
Chong Zhanga1320c52020-12-15 14:30:12 -0800377 snprintf(buffer, SIZE, "\n========== Dumping past sessions =========\n");
378 result.append(buffer);
Chong Zhang8677f1f2021-01-21 20:37:35 +0000379 for (auto& session : mSessionHistory) {
Chong Zhanga1320c52020-12-15 14:30:12 -0800380 dumpSession_l(session, result, true /*closedSession*/);
381 }
382
Chong Zhangbc062482020-10-14 16:43:53 -0700383 write(fd, result.string(), result.size());
384}
385
Chong Zhang87d199c2021-03-01 19:02:18 -0800386/*
387 * Returns nullptr if there is no session, or we're paused globally (due to resource lost,
388 * thermal throttling, etc.). Otherwise, return the session that should be run next.
389 */
Chong Zhangbc062482020-10-14 16:43:53 -0700390TranscodingSessionController::Session* TranscodingSessionController::getTopSession_l() {
391 if (mSessionMap.empty()) {
392 return nullptr;
393 }
Chong Zhang87d199c2021-03-01 19:02:18 -0800394
395 // Return nullptr if we're paused globally due to resource lost or thermal throttling.
396 if (((mResourcePolicy != nullptr && mResourceLost) ||
397 (mThermalPolicy != nullptr && mThermalThrottling))) {
398 return nullptr;
399 }
400
Chong Zhangbc062482020-10-14 16:43:53 -0700401 uid_t topUid = *mUidSortedList.begin();
Chong Zhangebd86d32021-03-29 11:30:56 -0700402 // If the current session is running, and it's in the topUid's queue, let it continue
403 // to run even if it's not the earliest in that uid's queue.
404 // For example, uid(B) is added to a session while it's pending in uid(A)'s queue, then
405 // B is brought to front which caused the session to run, then user switches back to A.
406 if (mCurrentSession != nullptr && mCurrentSession->getState() == Session::RUNNING &&
407 mCurrentSession->allClientUids.count(topUid) > 0) {
408 return mCurrentSession;
409 }
Chong Zhangbc062482020-10-14 16:43:53 -0700410 SessionKeyType topSessionKey = *mSessionQueues[topUid].begin();
411 return &mSessionMap[topSessionKey];
412}
413
Chong Zhang457c6892021-02-01 15:34:20 -0800414void TranscodingSessionController::setSessionState_l(Session* session, Session::State state) {
415 bool wasRunning = (session->getState() == Session::RUNNING);
416 session->setState(state);
417 bool isRunning = (session->getState() == Session::RUNNING);
418
419 if (wasRunning == isRunning) {
420 return;
421 }
422
423 // Currently we only have 1 running session, and we always put the previous
424 // session in non-running state before we run the new session, so it's okay
425 // to start/stop the watchdog here. If this assumption changes, we need to
426 // track the number of running sessions and start/stop watchdog based on that.
427 if (isRunning) {
428 mWatchdog->start(session->key);
429 } else {
430 mWatchdog->stop();
431 }
432}
433
Chong Zhanga1320c52020-12-15 14:30:12 -0800434void TranscodingSessionController::Session::setState(Session::State newState) {
435 if (state == newState) {
436 return;
437 }
Chong Zhang87d199c2021-03-01 19:02:18 -0800438 auto nowTime = std::chrono::steady_clock::now();
Chong Zhanga1320c52020-12-15 14:30:12 -0800439 if (state != INVALID) {
Chong Zhang87d199c2021-03-01 19:02:18 -0800440 std::chrono::microseconds elapsedTime =
441 std::chrono::duration_cast<std::chrono::microseconds>(nowTime - stateEnterTime);
Chong Zhanga1320c52020-12-15 14:30:12 -0800442 switch (state) {
443 case PAUSED:
444 pausedTime = pausedTime + elapsedTime;
445 break;
446 case RUNNING:
447 runningTime = runningTime + elapsedTime;
448 break;
449 case NOT_STARTED:
450 waitingTime = waitingTime + elapsedTime;
451 break;
452 default:
453 break;
454 }
455 }
456 if (newState == PAUSED) {
457 pauseCount++;
458 }
459 stateEnterTime = nowTime;
460 state = newState;
461}
462
Chong Zhangbc062482020-10-14 16:43:53 -0700463void TranscodingSessionController::updateCurrentSession_l() {
Chong Zhangbc062482020-10-14 16:43:53 -0700464 Session* curSession = mCurrentSession;
Chong Zhangebd86d32021-03-29 11:30:56 -0700465 Session* topSession = nullptr;
Chong Zhangbc062482020-10-14 16:43:53 -0700466
Chong Zhang87d199c2021-03-01 19:02:18 -0800467 // Delayed init of transcoder and watchdog.
468 if (mTranscoder == nullptr) {
469 mTranscoder = mTranscoderFactory(shared_from_this());
470 mWatchdog = std::make_shared<Watchdog>(this, mConfig.watchdogTimeoutUs);
Chong Zhang8677f1f2021-01-21 20:37:35 +0000471 }
472
Chong Zhang87d199c2021-03-01 19:02:18 -0800473 // If we found a different top session, or the top session's running state is not
474 // correct. Take some actions to ensure it's correct.
475 while ((topSession = getTopSession_l()) != curSession ||
476 (topSession != nullptr && !topSession->isRunning())) {
477 ALOGV("updateCurrentSession_l: topSession is %s, curSession is %s",
478 topSession == nullptr ? "null" : sessionToString(topSession->key).c_str(),
479 curSession == nullptr ? "null" : sessionToString(curSession->key).c_str());
Chong Zhang457c6892021-02-01 15:34:20 -0800480
Chong Zhang87d199c2021-03-01 19:02:18 -0800481 // If current session is running, pause it first. Note this is needed for either
482 // cases: 1) Top session is changing to another session, or 2) Top session is
483 // changing to null (which means we should be globally paused).
Chong Zhanga1320c52020-12-15 14:30:12 -0800484 if (curSession != nullptr && curSession->getState() == Session::RUNNING) {
Chong Zhangbc062482020-10-14 16:43:53 -0700485 mTranscoder->pause(curSession->key.first, curSession->key.second);
Chong Zhang457c6892021-02-01 15:34:20 -0800486 setSessionState_l(curSession, Session::PAUSED);
Chong Zhangbc062482020-10-14 16:43:53 -0700487 }
Chong Zhang87d199c2021-03-01 19:02:18 -0800488
489 if (topSession == nullptr) {
490 // Nothing more to run (either no session or globally paused).
491 break;
492 }
493
494 // Otherwise, ensure topSession is running.
495 if (topSession->getState() == Session::NOT_STARTED) {
Chong Zhangebd86d32021-03-29 11:30:56 -0700496 // Check if at least one client has quota to start the session.
497 bool keepForClient = false;
498 for (uid_t uid : topSession->allClientUids) {
499 if (mPacer->onSessionStarted(uid)) {
500 keepForClient = true;
501 // DO NOT break here, because book-keeping still needs to happen
502 // for the other uids.
503 }
504 }
505 if (!keepForClient) {
506 // Unfortunately all uids requesting this session are out of quota.
507 // Drop this session and try the next one.
Chong Zhang87d199c2021-03-01 19:02:18 -0800508 {
509 auto clientCallback = mSessionMap[topSession->key].callback.lock();
510 if (clientCallback != nullptr) {
511 clientCallback->onTranscodingFailed(
512 topSession->key.second, TranscodingErrorCode::kDroppedByService);
513 }
514 }
515 removeSession_l(topSession->key, Session::DROPPED_BY_PACER);
516 continue;
Chong Zhangbc062482020-10-14 16:43:53 -0700517 }
Chong Zhang87d199c2021-03-01 19:02:18 -0800518 mTranscoder->start(topSession->key.first, topSession->key.second, topSession->request,
519 topSession->callingUid, topSession->callback.lock());
520 setSessionState_l(topSession, Session::RUNNING);
521 } else if (topSession->getState() == Session::PAUSED) {
522 mTranscoder->resume(topSession->key.first, topSession->key.second, topSession->request,
523 topSession->callingUid, topSession->callback.lock());
Chong Zhang457c6892021-02-01 15:34:20 -0800524 setSessionState_l(topSession, Session::RUNNING);
Chong Zhangbc062482020-10-14 16:43:53 -0700525 }
Chong Zhang87d199c2021-03-01 19:02:18 -0800526 break;
Chong Zhangbc062482020-10-14 16:43:53 -0700527 }
528 mCurrentSession = topSession;
529}
530
Chong Zhangebd86d32021-03-29 11:30:56 -0700531void TranscodingSessionController::addUidToSession_l(uid_t clientUid,
532 const SessionKeyType& sessionKey) {
533 // If it's an offline session, the queue was already added in constructor.
534 // If it's a real-time sessions, check if a queue is already present for the uid,
535 // and add a new queue if needed.
536 if (clientUid != OFFLINE_UID) {
537 if (mSessionQueues.count(clientUid) == 0) {
538 mUidPolicy->registerMonitorUid(clientUid);
539 if (mUidPolicy->isUidOnTop(clientUid)) {
540 mUidSortedList.push_front(clientUid);
541 } else {
542 // Shouldn't be submitting real-time requests from non-top app,
543 // put it in front of the offline queue.
544 mUidSortedList.insert(mOfflineUidIterator, clientUid);
545 }
546 } else if (clientUid != *mUidSortedList.begin()) {
547 if (mUidPolicy->isUidOnTop(clientUid)) {
548 mUidSortedList.remove(clientUid);
549 mUidSortedList.push_front(clientUid);
550 }
551 }
552 }
553 // Append this session to the uid's queue.
554 mSessionQueues[clientUid].push_back(sessionKey);
555}
556
Chong Zhang2a3c9672021-03-31 15:36:32 -0700557void TranscodingSessionController::removeSession_l(
558 const SessionKeyType& sessionKey, Session::State finalState,
559 const std::shared_ptr<std::function<bool(uid_t uid)>>& keepUid) {
Chong Zhangbc062482020-10-14 16:43:53 -0700560 ALOGV("%s: session %s", __FUNCTION__, sessionToString(sessionKey).c_str());
561
562 if (mSessionMap.count(sessionKey) == 0) {
563 ALOGE("session %s doesn't exist", sessionToString(sessionKey).c_str());
564 return;
565 }
566
567 // Remove session from uid's queue.
Chong Zhangebd86d32021-03-29 11:30:56 -0700568 bool uidQueueRemoved = false;
Chong Zhang2a3c9672021-03-31 15:36:32 -0700569 std::unordered_set<uid_t> remainingUids;
Chong Zhangebd86d32021-03-29 11:30:56 -0700570 for (uid_t uid : mSessionMap[sessionKey].allClientUids) {
Chong Zhang2a3c9672021-03-31 15:36:32 -0700571 if (keepUid != nullptr) {
572 if ((*keepUid)(uid)) {
573 remainingUids.insert(uid);
574 continue;
575 }
576 // If we have uids to keep, the session is not going to any final
577 // state we can't use onSessionCompleted as the running time will
578 // not be valid. Only notify pacer to stop tracking this session.
579 mPacer->onSessionCancelled(uid);
Chong Zhangebd86d32021-03-29 11:30:56 -0700580 }
581 SessionQueueType& sessionQueue = mSessionQueues[uid];
582 auto it = std::find(sessionQueue.begin(), sessionQueue.end(), sessionKey);
583 if (it == sessionQueue.end()) {
584 ALOGW("couldn't find session %s in queue for uid %d",
585 sessionToString(sessionKey).c_str(), uid);
586 continue;
587 }
588 sessionQueue.erase(it);
589
590 // If this is the last session in a real-time queue, remove this uid's queue.
591 if (uid != OFFLINE_UID && sessionQueue.empty()) {
592 mUidSortedList.remove(uid);
593 mSessionQueues.erase(uid);
594 mUidPolicy->unregisterMonitorUid(uid);
595
596 uidQueueRemoved = true;
597 }
Chong Zhangbc062482020-10-14 16:43:53 -0700598 }
Chong Zhangbc062482020-10-14 16:43:53 -0700599
Chong Zhangebd86d32021-03-29 11:30:56 -0700600 if (uidQueueRemoved) {
Chong Zhangbc062482020-10-14 16:43:53 -0700601 std::unordered_set<uid_t> topUids = mUidPolicy->getTopUids();
602 moveUidsToTop_l(topUids, false /*preserveTopUid*/);
603 }
604
Chong Zhang2a3c9672021-03-31 15:36:32 -0700605 if (keepUid != nullptr) {
606 mSessionMap[sessionKey].allClientUids = remainingUids;
Chong Zhangebd86d32021-03-29 11:30:56 -0700607 return;
608 }
609
Chong Zhangbc062482020-10-14 16:43:53 -0700610 // Clear current session.
611 if (mCurrentSession == &mSessionMap[sessionKey]) {
612 mCurrentSession = nullptr;
613 }
614
Chong Zhang457c6892021-02-01 15:34:20 -0800615 setSessionState_l(&mSessionMap[sessionKey], finalState);
Chong Zhang87d199c2021-03-01 19:02:18 -0800616
Chong Zhang2a3c9672021-03-31 15:36:32 -0700617 // We can use onSessionCompleted() even for CANCELLED, because runningTime is
618 // now updated by setSessionState_l().
619 for (uid_t uid : mSessionMap[sessionKey].allClientUids) {
620 mPacer->onSessionCompleted(uid, mSessionMap[sessionKey].runningTime);
Chong Zhang87d199c2021-03-01 19:02:18 -0800621 }
622
Chong Zhanga1320c52020-12-15 14:30:12 -0800623 mSessionHistory.push_back(mSessionMap[sessionKey]);
624 if (mSessionHistory.size() > kSessionHistoryMax) {
625 mSessionHistory.erase(mSessionHistory.begin());
626 }
627
Chong Zhangbc062482020-10-14 16:43:53 -0700628 // Remove session from session map.
629 mSessionMap.erase(sessionKey);
630}
631
632/**
633 * Moves the set of uids to the front of mUidSortedList (which is used to pick
634 * the next session to run).
635 *
636 * This is called when 1) we received a onTopUidsChanged() callback from UidPolicy,
637 * or 2) we removed the session queue for a uid because it becomes empty.
638 *
639 * In case of 1), if there are multiple uids in the set, and the current front
640 * uid in mUidSortedList is still in the set, we try to keep that uid at front
641 * so that current session run is not interrupted. (This is not a concern for case 2)
642 * because the queue for a uid was just removed entirely.)
643 */
644void TranscodingSessionController::moveUidsToTop_l(const std::unordered_set<uid_t>& uids,
645 bool preserveTopUid) {
646 // If uid set is empty, nothing to do. Do not change the queue status.
647 if (uids.empty()) {
648 return;
649 }
650
651 // Save the current top uid.
652 uid_t curTopUid = *mUidSortedList.begin();
653 bool pushCurTopToFront = false;
654 int32_t numUidsMoved = 0;
655
656 // Go through the sorted uid list once, and move the ones in top set to front.
657 for (auto it = mUidSortedList.begin(); it != mUidSortedList.end();) {
658 uid_t uid = *it;
659
660 if (uid != OFFLINE_UID && uids.count(uid) > 0) {
661 it = mUidSortedList.erase(it);
662
663 // If this is the top we're preserving, don't push it here, push
664 // it after the for-loop.
665 if (uid == curTopUid && preserveTopUid) {
666 pushCurTopToFront = true;
667 } else {
668 mUidSortedList.push_front(uid);
669 }
670
671 // If we found all uids in the set, break out.
672 if (++numUidsMoved == uids.size()) {
673 break;
674 }
675 } else {
676 ++it;
677 }
678 }
679
680 if (pushCurTopToFront) {
681 mUidSortedList.push_front(curTopUid);
682 }
683}
684
685bool TranscodingSessionController::submit(
Linus Nilssona99f4042021-02-25 15:49:43 -0800686 ClientIdType clientId, SessionIdType sessionId, uid_t callingUid, uid_t clientUid,
Chong Zhangbc062482020-10-14 16:43:53 -0700687 const TranscodingRequestParcel& request,
688 const std::weak_ptr<ITranscodingClientCallback>& callback) {
689 SessionKeyType sessionKey = std::make_pair(clientId, sessionId);
690
691 ALOGV("%s: session %s, uid %d, prioirty %d", __FUNCTION__, sessionToString(sessionKey).c_str(),
Linus Nilssona99f4042021-02-25 15:49:43 -0800692 clientUid, (int32_t)request.priority);
Chong Zhangbc062482020-10-14 16:43:53 -0700693
694 std::scoped_lock lock{mLock};
695
696 if (mSessionMap.count(sessionKey) > 0) {
697 ALOGE("session %s already exists", sessionToString(sessionKey).c_str());
698 return false;
699 }
700
Chong Zhangcf3f8ee2020-10-29 18:38:37 -0700701 // Add the uid package name to the store of package names we already know.
Linus Nilssona99f4042021-02-25 15:49:43 -0800702 if (mUidPackageNames.count(clientUid) == 0) {
703 mUidPackageNames.emplace(clientUid, request.clientPackageName);
Chong Zhangcf3f8ee2020-10-29 18:38:37 -0700704 }
705
Chong Zhangbc062482020-10-14 16:43:53 -0700706 // TODO(chz): only support offline vs real-time for now. All kUnspecified sessions
707 // go to offline queue.
708 if (request.priority == TranscodingSessionPriority::kUnspecified) {
Linus Nilssona99f4042021-02-25 15:49:43 -0800709 clientUid = OFFLINE_UID;
Chong Zhangbc062482020-10-14 16:43:53 -0700710 }
711
712 // Add session to session map.
713 mSessionMap[sessionKey].key = sessionKey;
Linus Nilssona99f4042021-02-25 15:49:43 -0800714 mSessionMap[sessionKey].callingUid = callingUid;
Chong Zhangebd86d32021-03-29 11:30:56 -0700715 mSessionMap[sessionKey].allClientUids.insert(clientUid);
Chong Zhangbc062482020-10-14 16:43:53 -0700716 mSessionMap[sessionKey].request = request;
717 mSessionMap[sessionKey].callback = callback;
Chong Zhang457c6892021-02-01 15:34:20 -0800718 setSessionState_l(&mSessionMap[sessionKey], Session::NOT_STARTED);
Chong Zhangbc062482020-10-14 16:43:53 -0700719
Chong Zhangebd86d32021-03-29 11:30:56 -0700720 addUidToSession_l(clientUid, sessionKey);
Chong Zhangbc062482020-10-14 16:43:53 -0700721
722 updateCurrentSession_l();
723
724 validateState_l();
725 return true;
726}
727
728bool TranscodingSessionController::cancel(ClientIdType clientId, SessionIdType sessionId) {
729 SessionKeyType sessionKey = std::make_pair(clientId, sessionId);
730
731 ALOGV("%s: session %s", __FUNCTION__, sessionToString(sessionKey).c_str());
732
Chong Zhangebd86d32021-03-29 11:30:56 -0700733 std::list<SessionKeyType> sessionsToRemove, sessionsForOffline;
Chong Zhangbc062482020-10-14 16:43:53 -0700734
735 std::scoped_lock lock{mLock};
736
737 if (sessionId < 0) {
738 for (auto it = mSessionMap.begin(); it != mSessionMap.end(); ++it) {
Chong Zhangebd86d32021-03-29 11:30:56 -0700739 if (it->first.first == clientId) {
740 // If there is offline request, only keep the offline client;
741 // otherwise remove the session.
742 if (it->second.allClientUids.count(OFFLINE_UID) > 0) {
743 sessionsForOffline.push_back(it->first);
744 } else {
745 sessionsToRemove.push_back(it->first);
746 }
Chong Zhangbc062482020-10-14 16:43:53 -0700747 }
748 }
749 } else {
750 if (mSessionMap.count(sessionKey) == 0) {
751 ALOGE("session %s doesn't exist", sessionToString(sessionKey).c_str());
752 return false;
753 }
754 sessionsToRemove.push_back(sessionKey);
755 }
756
757 for (auto it = sessionsToRemove.begin(); it != sessionsToRemove.end(); ++it) {
758 // If the session has ever been started, stop it now.
759 // Note that stop() is needed even if the session is currently paused. This instructs
760 // the transcoder to discard any states for the session, otherwise the states may
761 // never be discarded.
Chong Zhanga1320c52020-12-15 14:30:12 -0800762 if (mSessionMap[*it].getState() != Session::NOT_STARTED) {
Chong Zhangbc062482020-10-14 16:43:53 -0700763 mTranscoder->stop(it->first, it->second);
764 }
765
766 // Remove the session.
Chong Zhanga1320c52020-12-15 14:30:12 -0800767 removeSession_l(*it, Session::CANCELED);
Chong Zhangbc062482020-10-14 16:43:53 -0700768 }
769
Chong Zhang2a3c9672021-03-31 15:36:32 -0700770 auto keepUid = std::make_shared<std::function<bool(uid_t)>>(
771 [](uid_t uid) { return uid == OFFLINE_UID; });
Chong Zhangebd86d32021-03-29 11:30:56 -0700772 for (auto it = sessionsForOffline.begin(); it != sessionsForOffline.end(); ++it) {
Chong Zhang2a3c9672021-03-31 15:36:32 -0700773 removeSession_l(*it, Session::CANCELED, keepUid);
Chong Zhangebd86d32021-03-29 11:30:56 -0700774 }
775
Chong Zhangbc062482020-10-14 16:43:53 -0700776 // Start next session.
777 updateCurrentSession_l();
778
779 validateState_l();
780 return true;
781}
782
Chong Zhangebd86d32021-03-29 11:30:56 -0700783bool TranscodingSessionController::addClientUid(ClientIdType clientId, SessionIdType sessionId,
784 uid_t clientUid) {
785 SessionKeyType sessionKey = std::make_pair(clientId, sessionId);
786
787 std::scoped_lock lock{mLock};
788
789 if (mSessionMap.count(sessionKey) == 0) {
790 ALOGE("session %s doesn't exist", sessionToString(sessionKey).c_str());
791 return false;
792 }
793
794 if (mSessionMap[sessionKey].allClientUids.count(clientUid) > 0) {
795 ALOGE("session %s already has uid %d", sessionToString(sessionKey).c_str(), clientUid);
796 return false;
797 }
798
799 mSessionMap[sessionKey].allClientUids.insert(clientUid);
800 addUidToSession_l(clientUid, sessionKey);
801
802 updateCurrentSession_l();
803
804 validateState_l();
805 return true;
806}
807
808bool TranscodingSessionController::getClientUids(ClientIdType clientId, SessionIdType sessionId,
809 std::vector<int32_t>* out_clientUids) {
810 SessionKeyType sessionKey = std::make_pair(clientId, sessionId);
811
812 std::scoped_lock lock{mLock};
813
814 if (mSessionMap.count(sessionKey) == 0) {
815 ALOGE("session %s doesn't exist", sessionToString(sessionKey).c_str());
816 return false;
817 }
818
819 out_clientUids->clear();
820 for (uid_t uid : mSessionMap[sessionKey].allClientUids) {
821 if (uid != OFFLINE_UID) {
822 out_clientUids->push_back(uid);
823 }
824 }
825 return true;
826}
827
Chong Zhangbc062482020-10-14 16:43:53 -0700828bool TranscodingSessionController::getSession(ClientIdType clientId, SessionIdType sessionId,
829 TranscodingRequestParcel* request) {
830 SessionKeyType sessionKey = std::make_pair(clientId, sessionId);
831
832 std::scoped_lock lock{mLock};
833
834 if (mSessionMap.count(sessionKey) == 0) {
835 ALOGE("session %s doesn't exist", sessionToString(sessionKey).c_str());
836 return false;
837 }
838
839 *(TranscodingRequest*)request = mSessionMap[sessionKey].request;
840 return true;
841}
842
843void TranscodingSessionController::notifyClient(ClientIdType clientId, SessionIdType sessionId,
844 const char* reason,
845 std::function<void(const SessionKeyType&)> func) {
846 SessionKeyType sessionKey = std::make_pair(clientId, sessionId);
847
848 std::scoped_lock lock{mLock};
849
850 if (mSessionMap.count(sessionKey) == 0) {
851 ALOGW("%s: ignoring %s for session %s that doesn't exist", __FUNCTION__, reason,
852 sessionToString(sessionKey).c_str());
853 return;
854 }
855
856 // Only ignore if session was never started. In particular, propagate the status
857 // to client if the session is paused. Transcoder could have posted finish when
858 // we're pausing it, and the finish arrived after we changed current session.
Chong Zhanga1320c52020-12-15 14:30:12 -0800859 if (mSessionMap[sessionKey].getState() == Session::NOT_STARTED) {
Chong Zhangbc062482020-10-14 16:43:53 -0700860 ALOGW("%s: ignoring %s for session %s that was never started", __FUNCTION__, reason,
861 sessionToString(sessionKey).c_str());
862 return;
863 }
864
865 ALOGV("%s: session %s %s", __FUNCTION__, sessionToString(sessionKey).c_str(), reason);
866 func(sessionKey);
867}
868
869void TranscodingSessionController::onStarted(ClientIdType clientId, SessionIdType sessionId) {
870 notifyClient(clientId, sessionId, "started", [=](const SessionKeyType& sessionKey) {
871 auto callback = mSessionMap[sessionKey].callback.lock();
872 if (callback != nullptr) {
873 callback->onTranscodingStarted(sessionId);
874 }
875 });
876}
877
878void TranscodingSessionController::onPaused(ClientIdType clientId, SessionIdType sessionId) {
879 notifyClient(clientId, sessionId, "paused", [=](const SessionKeyType& sessionKey) {
880 auto callback = mSessionMap[sessionKey].callback.lock();
881 if (callback != nullptr) {
882 callback->onTranscodingPaused(sessionId);
883 }
884 });
885}
886
887void TranscodingSessionController::onResumed(ClientIdType clientId, SessionIdType sessionId) {
888 notifyClient(clientId, sessionId, "resumed", [=](const SessionKeyType& sessionKey) {
889 auto callback = mSessionMap[sessionKey].callback.lock();
890 if (callback != nullptr) {
891 callback->onTranscodingResumed(sessionId);
892 }
893 });
894}
895
896void TranscodingSessionController::onFinish(ClientIdType clientId, SessionIdType sessionId) {
897 notifyClient(clientId, sessionId, "finish", [=](const SessionKeyType& sessionKey) {
898 {
899 auto clientCallback = mSessionMap[sessionKey].callback.lock();
900 if (clientCallback != nullptr) {
901 clientCallback->onTranscodingFinished(
902 sessionId, TranscodingResultParcel({sessionId, -1 /*actualBitrateBps*/,
903 std::nullopt /*sessionStats*/}));
904 }
905 }
906
907 // Remove the session.
Chong Zhanga1320c52020-12-15 14:30:12 -0800908 removeSession_l(sessionKey, Session::FINISHED);
Chong Zhangbc062482020-10-14 16:43:53 -0700909
910 // Start next session.
911 updateCurrentSession_l();
912
913 validateState_l();
914 });
915}
916
917void TranscodingSessionController::onError(ClientIdType clientId, SessionIdType sessionId,
918 TranscodingErrorCode err) {
919 notifyClient(clientId, sessionId, "error", [=](const SessionKeyType& sessionKey) {
Chong Zhang457c6892021-02-01 15:34:20 -0800920 if (err == TranscodingErrorCode::kWatchdogTimeout) {
921 // Abandon the transcoder, as its handler thread might be stuck in some call to
922 // MediaTranscoder altogether, and may not be able to handle any new tasks.
923 mTranscoder->stop(clientId, sessionId, true /*abandon*/);
924 // Clear the last ref count before we create new transcoder.
925 mTranscoder = nullptr;
Chong Zhang87d199c2021-03-01 19:02:18 -0800926 mTranscoder = mTranscoderFactory(shared_from_this());
Chong Zhang457c6892021-02-01 15:34:20 -0800927 }
928
Chong Zhangbc062482020-10-14 16:43:53 -0700929 {
930 auto clientCallback = mSessionMap[sessionKey].callback.lock();
931 if (clientCallback != nullptr) {
932 clientCallback->onTranscodingFailed(sessionId, err);
933 }
934 }
935
936 // Remove the session.
Chong Zhanga1320c52020-12-15 14:30:12 -0800937 removeSession_l(sessionKey, Session::ERROR);
Chong Zhangbc062482020-10-14 16:43:53 -0700938
939 // Start next session.
940 updateCurrentSession_l();
941
942 validateState_l();
943 });
944}
945
946void TranscodingSessionController::onProgressUpdate(ClientIdType clientId, SessionIdType sessionId,
947 int32_t progress) {
948 notifyClient(clientId, sessionId, "progress", [=](const SessionKeyType& sessionKey) {
949 auto callback = mSessionMap[sessionKey].callback.lock();
950 if (callback != nullptr) {
951 callback->onProgressUpdate(sessionId, progress);
952 }
953 mSessionMap[sessionKey].lastProgress = progress;
954 });
955}
956
Chong Zhang457c6892021-02-01 15:34:20 -0800957void TranscodingSessionController::onHeartBeat(ClientIdType clientId, SessionIdType sessionId) {
958 notifyClient(clientId, sessionId, "heart-beat",
959 [=](const SessionKeyType& /*sessionKey*/) { mWatchdog->keepAlive(); });
960}
961
Chong Zhangeffd8962020-12-02 14:29:09 -0800962void TranscodingSessionController::onResourceLost(ClientIdType clientId, SessionIdType sessionId) {
Chong Zhangbc062482020-10-14 16:43:53 -0700963 ALOGI("%s", __FUNCTION__);
964
Chong Zhangeffd8962020-12-02 14:29:09 -0800965 notifyClient(clientId, sessionId, "resource_lost", [=](const SessionKeyType& sessionKey) {
966 if (mResourceLost) {
967 return;
Chong Zhangbc062482020-10-14 16:43:53 -0700968 }
Chong Zhangbc062482020-10-14 16:43:53 -0700969
Chong Zhangeffd8962020-12-02 14:29:09 -0800970 Session* resourceLostSession = &mSessionMap[sessionKey];
Chong Zhanga1320c52020-12-15 14:30:12 -0800971 if (resourceLostSession->getState() != Session::RUNNING) {
Chong Zhangeffd8962020-12-02 14:29:09 -0800972 ALOGW("session %s lost resource but is no longer running",
Chong Zhanga1320c52020-12-15 14:30:12 -0800973 sessionToString(sessionKey).c_str());
Chong Zhangeffd8962020-12-02 14:29:09 -0800974 return;
975 }
976 // If we receive a resource loss event, the transcoder already paused the transcoding,
977 // so we don't need to call onPaused() to pause it. However, we still need to notify
978 // the client and update the session state here.
Chong Zhang457c6892021-02-01 15:34:20 -0800979 setSessionState_l(resourceLostSession, Session::PAUSED);
Chong Zhangeffd8962020-12-02 14:29:09 -0800980 // Notify the client as a paused event.
981 auto clientCallback = resourceLostSession->callback.lock();
982 if (clientCallback != nullptr) {
983 clientCallback->onTranscodingPaused(sessionKey.second);
984 }
Chong Zhang8677f1f2021-01-21 20:37:35 +0000985 if (mResourcePolicy != nullptr) {
986 mResourcePolicy->setPidResourceLost(resourceLostSession->request.clientPid);
987 }
Chong Zhangeffd8962020-12-02 14:29:09 -0800988 mResourceLost = true;
989
990 validateState_l();
991 });
Chong Zhangbc062482020-10-14 16:43:53 -0700992}
993
994void TranscodingSessionController::onTopUidsChanged(const std::unordered_set<uid_t>& uids) {
995 if (uids.empty()) {
996 ALOGW("%s: ignoring empty uids", __FUNCTION__);
997 return;
998 }
999
1000 std::string uidStr;
1001 for (auto it = uids.begin(); it != uids.end(); it++) {
1002 if (!uidStr.empty()) {
1003 uidStr += ", ";
1004 }
1005 uidStr += std::to_string(*it);
1006 }
1007
1008 ALOGD("%s: topUids: size %zu, uids: %s", __FUNCTION__, uids.size(), uidStr.c_str());
1009
1010 std::scoped_lock lock{mLock};
1011
1012 moveUidsToTop_l(uids, true /*preserveTopUid*/);
1013
1014 updateCurrentSession_l();
1015
1016 validateState_l();
1017}
1018
Chong Zhang2a3c9672021-03-31 15:36:32 -07001019void TranscodingSessionController::onUidGone(uid_t goneUid) {
1020 ALOGD("%s: gone uid %u", __FUNCTION__, goneUid);
1021
1022 std::list<SessionKeyType> sessionsToRemove, sessionsForOtherUids;
1023
1024 std::scoped_lock lock{mLock};
1025
1026 for (auto it = mSessionMap.begin(); it != mSessionMap.end(); ++it) {
1027 if (it->second.allClientUids.count(goneUid) > 0) {
1028 // If goneUid is the only uid, remove the session; otherwise, only
1029 // remove the uid from the session.
1030 if (it->second.allClientUids.size() > 1) {
1031 sessionsForOtherUids.push_back(it->first);
1032 } else {
1033 sessionsToRemove.push_back(it->first);
1034 }
1035 }
1036 }
1037
1038 for (auto it = sessionsToRemove.begin(); it != sessionsToRemove.end(); ++it) {
1039 // If the session has ever been started, stop it now.
1040 // Note that stop() is needed even if the session is currently paused. This instructs
1041 // the transcoder to discard any states for the session, otherwise the states may
1042 // never be discarded.
1043 if (mSessionMap[*it].getState() != Session::NOT_STARTED) {
1044 mTranscoder->stop(it->first, it->second);
1045 }
1046
1047 {
1048 auto clientCallback = mSessionMap[*it].callback.lock();
1049 if (clientCallback != nullptr) {
1050 clientCallback->onTranscodingFailed(it->second,
1051 TranscodingErrorCode::kUidGoneCancelled);
1052 }
1053 }
1054
1055 // Remove the session.
1056 removeSession_l(*it, Session::CANCELED);
1057 }
1058
1059 auto keepUid = std::make_shared<std::function<bool(uid_t)>>(
1060 [goneUid](uid_t uid) { return uid != goneUid; });
1061 for (auto it = sessionsForOtherUids.begin(); it != sessionsForOtherUids.end(); ++it) {
1062 removeSession_l(*it, Session::CANCELED, keepUid);
1063 }
1064
1065 // Start next session.
1066 updateCurrentSession_l();
1067
1068 validateState_l();
1069}
1070
Chong Zhangbc062482020-10-14 16:43:53 -07001071void TranscodingSessionController::onResourceAvailable() {
1072 std::scoped_lock lock{mLock};
1073
1074 if (!mResourceLost) {
1075 return;
1076 }
1077
1078 ALOGI("%s", __FUNCTION__);
1079
1080 mResourceLost = false;
1081 updateCurrentSession_l();
1082
1083 validateState_l();
1084}
1085
Chong Zhang8677f1f2021-01-21 20:37:35 +00001086void TranscodingSessionController::onThrottlingStarted() {
1087 std::scoped_lock lock{mLock};
1088
1089 if (mThermalThrottling) {
1090 return;
1091 }
1092
1093 ALOGI("%s", __FUNCTION__);
1094
1095 mThermalThrottling = true;
1096 updateCurrentSession_l();
1097
1098 validateState_l();
1099}
1100
1101void TranscodingSessionController::onThrottlingStopped() {
1102 std::scoped_lock lock{mLock};
1103
1104 if (!mThermalThrottling) {
1105 return;
1106 }
1107
1108 ALOGI("%s", __FUNCTION__);
1109
1110 mThermalThrottling = false;
1111 updateCurrentSession_l();
1112
1113 validateState_l();
1114}
1115
Chong Zhangbc062482020-10-14 16:43:53 -07001116void TranscodingSessionController::validateState_l() {
1117#ifdef VALIDATE_STATE
1118 LOG_ALWAYS_FATAL_IF(mSessionQueues.count(OFFLINE_UID) != 1,
1119 "mSessionQueues offline queue number is not 1");
1120 LOG_ALWAYS_FATAL_IF(*mOfflineUidIterator != OFFLINE_UID,
1121 "mOfflineUidIterator not pointing to offline uid");
1122 LOG_ALWAYS_FATAL_IF(mUidSortedList.size() != mSessionQueues.size(),
Chong Zhangebd86d32021-03-29 11:30:56 -07001123 "mUidSortedList and mSessionQueues size mismatch, %zu vs %zu",
1124 mUidSortedList.size(), mSessionQueues.size());
Chong Zhangbc062482020-10-14 16:43:53 -07001125
1126 int32_t totalSessions = 0;
1127 for (auto uid : mUidSortedList) {
1128 LOG_ALWAYS_FATAL_IF(mSessionQueues.count(uid) != 1,
1129 "mSessionQueues count for uid %d is not 1", uid);
1130 for (auto& sessionKey : mSessionQueues[uid]) {
1131 LOG_ALWAYS_FATAL_IF(mSessionMap.count(sessionKey) != 1,
1132 "mSessions count for session %s is not 1",
1133 sessionToString(sessionKey).c_str());
1134 }
1135
1136 totalSessions += mSessionQueues[uid].size();
1137 }
Chong Zhangebd86d32021-03-29 11:30:56 -07001138 int32_t totalSessionsAlternative = 0;
1139 for (auto const& s : mSessionMap) {
1140 totalSessionsAlternative += s.second.allClientUids.size();
1141 }
1142 LOG_ALWAYS_FATAL_IF(totalSessions != totalSessionsAlternative,
1143 "session count (including dup) from mSessionQueues doesn't match that from "
1144 "mSessionMap, %d vs %d",
1145 totalSessions, totalSessionsAlternative);
Chong Zhangbc062482020-10-14 16:43:53 -07001146#endif // VALIDATE_STATE
1147}
1148
1149} // namespace android