blob: 9705f3ce6b3dbdc4f53fd0cb3e0ae19bd90e8e93 [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
Chong Zhangb28d6ca2021-04-15 18:02:21 -070022#include <android/permission_manager.h>
Chong Zhangbc062482020-10-14 16:43:53 -070023#include <inttypes.h>
24#include <media/TranscodingSessionController.h>
25#include <media/TranscodingUidPolicy.h>
26#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 {
165 std::unique_lock<std::mutex> lock{mLock};
166
167 while (!mAbort) {
168 if (!mActive) {
169 mCondition.wait(lock);
170 continue;
171 }
172 // Watchdog active, wait till next timeout time.
173 if (mCondition.wait_until(lock, mNextTimeoutTime) == std::cv_status::timeout) {
174 // If timeout happens, report timeout and deactivate watchdog.
175 mActive = false;
176 // Make a copy of session key, as once we unlock, it could be unprotected.
177 SessionKeyType sessionKey = mSessionToWatch;
178
179 ALOGE("Watchdog timeout: %s", sessionToString(sessionKey).c_str());
180
181 lock.unlock();
182 mOwner->onError(sessionKey.first, sessionKey.second,
183 TranscodingErrorCode::kWatchdogTimeout);
184 lock.lock();
185 }
186 }
187}
188///////////////////////////////////////////////////////////////////////////////
Chong Zhang87d199c2021-03-01 19:02:18 -0800189struct TranscodingSessionController::Pacer {
190 Pacer(const ControllerConfig& config)
191 : mBurstThresholdMs(config.pacerBurstThresholdMs),
192 mBurstCountQuota(config.pacerBurstCountQuota),
193 mBurstTimeQuotaSec(config.pacerBurstTimeQuotaSeconds) {}
194
195 ~Pacer() = default;
196
Chong Zhangb28d6ca2021-04-15 18:02:21 -0700197 bool onSessionStarted(uid_t uid, uid_t callingUid);
Chong Zhangebd86d32021-03-29 11:30:56 -0700198 void onSessionCompleted(uid_t uid, std::chrono::microseconds runningTime);
Chong Zhang2a3c9672021-03-31 15:36:32 -0700199 void onSessionCancelled(uid_t uid);
Chong Zhang87d199c2021-03-01 19:02:18 -0800200
201private:
202 // Threshold of time between finish/start below which a back-to-back start is counted.
203 int32_t mBurstThresholdMs;
204 // Maximum allowed back-to-back start count.
205 int32_t mBurstCountQuota;
206 // Maximum allowed back-to-back running time.
207 int32_t mBurstTimeQuotaSec;
208
209 struct UidHistoryEntry {
Chong Zhangebd86d32021-03-29 11:30:56 -0700210 bool sessionActive = false;
Chong Zhang87d199c2021-03-01 19:02:18 -0800211 int32_t burstCount = 0;
212 std::chrono::steady_clock::duration burstDuration{0};
Chong Zhangebd86d32021-03-29 11:30:56 -0700213 std::chrono::steady_clock::time_point lastCompletedTime;
Chong Zhang87d199c2021-03-01 19:02:18 -0800214 };
215 std::map<uid_t, UidHistoryEntry> mUidHistoryMap;
Chong Zhangb28d6ca2021-04-15 18:02:21 -0700216 std::unordered_set<uid_t> mMtpUids;
217 std::unordered_set<uid_t> mNonMtpUids;
218
219 bool isSubjectToQuota(uid_t uid, uid_t callingUid);
Chong Zhang87d199c2021-03-01 19:02:18 -0800220};
221
Chong Zhangb28d6ca2021-04-15 18:02:21 -0700222bool TranscodingSessionController::Pacer::isSubjectToQuota(uid_t uid, uid_t callingUid) {
223 // Submitting with self uid is not limited (which can only happen if it's used as an
224 // app-facing API). MediaProvider usage always submit on behalf of other uids.
225 if (uid == callingUid) {
226 return false;
227 }
228
229 if (mMtpUids.find(uid) != mMtpUids.end()) {
230 return false;
231 }
232
233 if (mNonMtpUids.find(uid) != mNonMtpUids.end()) {
234 return true;
235 }
236
237 // We don't have MTP permission info about this uid yet, check permission and save the result.
238 int32_t result;
239 if (__builtin_available(android __TRANSCODING_MIN_API__, *)) {
240 if (APermissionManager_checkPermission("android.permission.ACCESS_MTP", -1 /*pid*/, uid,
241 &result) == PERMISSION_MANAGER_STATUS_OK &&
242 result == PERMISSION_MANAGER_PERMISSION_GRANTED) {
243 mMtpUids.insert(uid);
244 return false;
245 }
246 }
247
248 mNonMtpUids.insert(uid);
249 return true;
250}
251
252bool TranscodingSessionController::Pacer::onSessionStarted(uid_t uid, uid_t callingUid) {
253 if (!isSubjectToQuota(uid, callingUid)) {
254 ALOGI("Pacer::onSessionStarted: uid %d (caling uid: %d): not subject to quota", uid,
255 callingUid);
256 return true;
257 }
258
Chong Zhangebd86d32021-03-29 11:30:56 -0700259 // If uid doesn't exist, only insert the entry and mark session active. Skip quota checking.
Chong Zhang87d199c2021-03-01 19:02:18 -0800260 if (mUidHistoryMap.find(uid) == mUidHistoryMap.end()) {
261 mUidHistoryMap.emplace(uid, UidHistoryEntry{});
Chong Zhangebd86d32021-03-29 11:30:56 -0700262 mUidHistoryMap[uid].sessionActive = true;
263 ALOGV("Pacer::onSessionStarted: uid %d: new", uid);
Chong Zhang87d199c2021-03-01 19:02:18 -0800264 return true;
265 }
266
267 // TODO: if Thermal throttling or resoure lost happened to occurr between this start
268 // and the previous completion, we should deduct the paused time from the elapsed time.
269 // (Individual session's pause time, on the other hand, doesn't need to be deducted
270 // because it doesn't affect the gap between last completion and the start.
271 auto timeSinceLastComplete =
272 std::chrono::steady_clock::now() - mUidHistoryMap[uid].lastCompletedTime;
273 if (mUidHistoryMap[uid].burstCount >= mBurstCountQuota &&
274 mUidHistoryMap[uid].burstDuration >= std::chrono::seconds(mBurstTimeQuotaSec)) {
Chong Zhangebd86d32021-03-29 11:30:56 -0700275 ALOGW("Pacer::onSessionStarted: uid %d: over quota, burst count %d, time %lldms", uid,
276 mUidHistoryMap[uid].burstCount,
277 (long long)mUidHistoryMap[uid].burstDuration.count() / 1000000);
Chong Zhang87d199c2021-03-01 19:02:18 -0800278 return false;
279 }
280
281 // If not over quota, allow the session, and reset as long as this is not too close
282 // to previous completion.
283 if (timeSinceLastComplete > std::chrono::milliseconds(mBurstThresholdMs)) {
Chong Zhangebd86d32021-03-29 11:30:56 -0700284 ALOGV("Pacer::onSessionStarted: uid %d: reset quota", uid);
Chong Zhang87d199c2021-03-01 19:02:18 -0800285 mUidHistoryMap[uid].burstCount = 0;
286 mUidHistoryMap[uid].burstDuration = std::chrono::milliseconds(0);
287 } else {
Chong Zhangebd86d32021-03-29 11:30:56 -0700288 ALOGV("Pacer::onSessionStarted: uid %d: burst count %d, time %lldms", uid,
289 mUidHistoryMap[uid].burstCount,
290 (long long)mUidHistoryMap[uid].burstDuration.count() / 1000000);
Chong Zhang87d199c2021-03-01 19:02:18 -0800291 }
292
Chong Zhangebd86d32021-03-29 11:30:56 -0700293 mUidHistoryMap[uid].sessionActive = true;
Chong Zhang87d199c2021-03-01 19:02:18 -0800294 return true;
295}
296
Chong Zhangebd86d32021-03-29 11:30:56 -0700297void TranscodingSessionController::Pacer::onSessionCompleted(
298 uid_t uid, std::chrono::microseconds runningTime) {
299 // Skip quota update if this uid missed the start. (Could happen if the uid is added via
300 // addClientUid() after the session start.)
301 if (mUidHistoryMap.find(uid) == mUidHistoryMap.end() || !mUidHistoryMap[uid].sessionActive) {
302 ALOGV("Pacer::onSessionCompleted: uid %d: not started", uid);
303 return;
304 }
305 ALOGV("Pacer::onSessionCompleted: uid %d: runningTime %lld", uid, runningTime.count() / 1000);
306 mUidHistoryMap[uid].sessionActive = false;
307 mUidHistoryMap[uid].burstCount++;
308 mUidHistoryMap[uid].burstDuration += runningTime;
309 mUidHistoryMap[uid].lastCompletedTime = std::chrono::steady_clock::now();
310}
311
Chong Zhang2a3c9672021-03-31 15:36:32 -0700312void TranscodingSessionController::Pacer::onSessionCancelled(uid_t uid) {
313 if (mUidHistoryMap.find(uid) == mUidHistoryMap.end()) {
314 ALOGV("Pacer::onSessionCancelled: uid %d: not present", uid);
315 return;
316 }
317 // This is only called if a uid is removed from a session (due to it being killed
318 // or the original submitting client was gone but session was kept for offline use).
319 // Since the uid is going to miss the onSessionCompleted(), we can't track this
320 // session, and have to check back at next onSessionStarted().
321 mUidHistoryMap[uid].sessionActive = false;
322}
323
Chong Zhang87d199c2021-03-01 19:02:18 -0800324///////////////////////////////////////////////////////////////////////////////
Chong Zhang457c6892021-02-01 15:34:20 -0800325
Chong Zhangbc062482020-10-14 16:43:53 -0700326TranscodingSessionController::TranscodingSessionController(
Chong Zhang457c6892021-02-01 15:34:20 -0800327 const TranscoderFactoryType& transcoderFactory,
Chong Zhangbc062482020-10-14 16:43:53 -0700328 const std::shared_ptr<UidPolicyInterface>& uidPolicy,
Chong Zhang8677f1f2021-01-21 20:37:35 +0000329 const std::shared_ptr<ResourcePolicyInterface>& resourcePolicy,
Chong Zhang87d199c2021-03-01 19:02:18 -0800330 const std::shared_ptr<ThermalPolicyInterface>& thermalPolicy,
331 const ControllerConfig* config)
Chong Zhang457c6892021-02-01 15:34:20 -0800332 : mTranscoderFactory(transcoderFactory),
Chong Zhangbc062482020-10-14 16:43:53 -0700333 mUidPolicy(uidPolicy),
334 mResourcePolicy(resourcePolicy),
Chong Zhang8677f1f2021-01-21 20:37:35 +0000335 mThermalPolicy(thermalPolicy),
Chong Zhangbc062482020-10-14 16:43:53 -0700336 mCurrentSession(nullptr),
337 mResourceLost(false) {
338 // Only push empty offline queue initially. Realtime queues are added when requests come in.
339 mUidSortedList.push_back(OFFLINE_UID);
340 mOfflineUidIterator = mUidSortedList.begin();
341 mSessionQueues.emplace(OFFLINE_UID, SessionQueueType());
Chong Zhangcf3f8ee2020-10-29 18:38:37 -0700342 mUidPackageNames[OFFLINE_UID] = "(offline)";
Chong Zhang8677f1f2021-01-21 20:37:35 +0000343 mThermalThrottling = thermalPolicy->getThrottlingStatus();
Chong Zhang87d199c2021-03-01 19:02:18 -0800344 if (config != nullptr) {
345 mConfig = *config;
346 }
347 mPacer.reset(new Pacer(mConfig));
348 ALOGD("@@@ watchdog %lld, burst count %d, burst time %d, burst threshold %d",
349 (long long)mConfig.watchdogTimeoutUs, mConfig.pacerBurstCountQuota,
350 mConfig.pacerBurstTimeQuotaSeconds, mConfig.pacerBurstThresholdMs);
Chong Zhangbc062482020-10-14 16:43:53 -0700351}
352
353TranscodingSessionController::~TranscodingSessionController() {}
354
Chong Zhanga1320c52020-12-15 14:30:12 -0800355void TranscodingSessionController::dumpSession_l(const Session& session, String8& result,
356 bool closedSession) {
357 const size_t SIZE = 256;
358 char buffer[SIZE];
359 const TranscodingRequestParcel& request = session.request;
360 snprintf(buffer, SIZE, " Session: %s, %s, %d%%\n", sessionToString(session.key).c_str(),
361 sessionStateToString(session.getState()), session.lastProgress);
362 result.append(buffer);
363 snprintf(buffer, SIZE, " pkg: %s\n", request.clientPackageName.c_str());
364 result.append(buffer);
365 snprintf(buffer, SIZE, " src: %s\n", request.sourceFilePath.c_str());
366 result.append(buffer);
367 snprintf(buffer, SIZE, " dst: %s\n", request.destinationFilePath.c_str());
368 result.append(buffer);
369
370 if (closedSession) {
371 snprintf(buffer, SIZE,
372 " waiting: %.1fs, running: %.1fs, paused: %.1fs, paused count: %d\n",
373 session.waitingTime.count() / 1000000.0f, session.runningTime.count() / 1000000.0f,
374 session.pausedTime.count() / 1000000.0f, session.pauseCount);
375 result.append(buffer);
376 }
377}
378
Chong Zhangbc062482020-10-14 16:43:53 -0700379void TranscodingSessionController::dumpAllSessions(int fd, const Vector<String16>& args __unused) {
380 String8 result;
381
382 const size_t SIZE = 256;
383 char buffer[SIZE];
384 std::scoped_lock lock{mLock};
385
Chong Zhanga1320c52020-12-15 14:30:12 -0800386 snprintf(buffer, SIZE, "\n========== Dumping live sessions queues =========\n");
Chong Zhangbc062482020-10-14 16:43:53 -0700387 result.append(buffer);
388 snprintf(buffer, SIZE, " Total num of Sessions: %zu\n", mSessionMap.size());
389 result.append(buffer);
390
391 std::vector<int32_t> uids(mUidSortedList.begin(), mUidSortedList.end());
Chong Zhangbc062482020-10-14 16:43:53 -0700392
393 for (int32_t i = 0; i < uids.size(); i++) {
394 const uid_t uid = uids[i];
395
396 if (mSessionQueues[uid].empty()) {
397 continue;
398 }
Chong Zhanga1320c52020-12-15 14:30:12 -0800399 snprintf(buffer, SIZE, " uid: %d, pkg: %s\n", uid,
Chong Zhangcf3f8ee2020-10-29 18:38:37 -0700400 mUidPackageNames.count(uid) > 0 ? mUidPackageNames[uid].c_str() : "(unknown)");
Chong Zhangbc062482020-10-14 16:43:53 -0700401 result.append(buffer);
402 snprintf(buffer, SIZE, " Num of sessions: %zu\n", mSessionQueues[uid].size());
403 result.append(buffer);
404 for (auto& sessionKey : mSessionQueues[uid]) {
405 auto sessionIt = mSessionMap.find(sessionKey);
406 if (sessionIt == mSessionMap.end()) {
407 snprintf(buffer, SIZE, "Failed to look up Session %s \n",
408 sessionToString(sessionKey).c_str());
409 result.append(buffer);
410 continue;
411 }
Chong Zhanga1320c52020-12-15 14:30:12 -0800412 dumpSession_l(sessionIt->second, result);
Chong Zhangbc062482020-10-14 16:43:53 -0700413 }
414 }
415
Chong Zhanga1320c52020-12-15 14:30:12 -0800416 snprintf(buffer, SIZE, "\n========== Dumping past sessions =========\n");
417 result.append(buffer);
Chong Zhang8677f1f2021-01-21 20:37:35 +0000418 for (auto& session : mSessionHistory) {
Chong Zhanga1320c52020-12-15 14:30:12 -0800419 dumpSession_l(session, result, true /*closedSession*/);
420 }
421
Chong Zhangbc062482020-10-14 16:43:53 -0700422 write(fd, result.string(), result.size());
423}
424
Chong Zhang87d199c2021-03-01 19:02:18 -0800425/*
426 * Returns nullptr if there is no session, or we're paused globally (due to resource lost,
427 * thermal throttling, etc.). Otherwise, return the session that should be run next.
428 */
Chong Zhangbc062482020-10-14 16:43:53 -0700429TranscodingSessionController::Session* TranscodingSessionController::getTopSession_l() {
430 if (mSessionMap.empty()) {
431 return nullptr;
432 }
Chong Zhang87d199c2021-03-01 19:02:18 -0800433
434 // Return nullptr if we're paused globally due to resource lost or thermal throttling.
435 if (((mResourcePolicy != nullptr && mResourceLost) ||
436 (mThermalPolicy != nullptr && mThermalThrottling))) {
437 return nullptr;
438 }
439
Chong Zhangbc062482020-10-14 16:43:53 -0700440 uid_t topUid = *mUidSortedList.begin();
Chong Zhangebd86d32021-03-29 11:30:56 -0700441 // If the current session is running, and it's in the topUid's queue, let it continue
442 // to run even if it's not the earliest in that uid's queue.
443 // For example, uid(B) is added to a session while it's pending in uid(A)'s queue, then
444 // B is brought to front which caused the session to run, then user switches back to A.
445 if (mCurrentSession != nullptr && mCurrentSession->getState() == Session::RUNNING &&
446 mCurrentSession->allClientUids.count(topUid) > 0) {
447 return mCurrentSession;
448 }
Chong Zhangbc062482020-10-14 16:43:53 -0700449 SessionKeyType topSessionKey = *mSessionQueues[topUid].begin();
450 return &mSessionMap[topSessionKey];
451}
452
Chong Zhang457c6892021-02-01 15:34:20 -0800453void TranscodingSessionController::setSessionState_l(Session* session, Session::State state) {
454 bool wasRunning = (session->getState() == Session::RUNNING);
455 session->setState(state);
456 bool isRunning = (session->getState() == Session::RUNNING);
457
458 if (wasRunning == isRunning) {
459 return;
460 }
461
462 // Currently we only have 1 running session, and we always put the previous
463 // session in non-running state before we run the new session, so it's okay
464 // to start/stop the watchdog here. If this assumption changes, we need to
465 // track the number of running sessions and start/stop watchdog based on that.
466 if (isRunning) {
467 mWatchdog->start(session->key);
468 } else {
469 mWatchdog->stop();
470 }
471}
472
Chong Zhanga1320c52020-12-15 14:30:12 -0800473void TranscodingSessionController::Session::setState(Session::State newState) {
474 if (state == newState) {
475 return;
476 }
Chong Zhang87d199c2021-03-01 19:02:18 -0800477 auto nowTime = std::chrono::steady_clock::now();
Chong Zhanga1320c52020-12-15 14:30:12 -0800478 if (state != INVALID) {
Chong Zhang87d199c2021-03-01 19:02:18 -0800479 std::chrono::microseconds elapsedTime =
480 std::chrono::duration_cast<std::chrono::microseconds>(nowTime - stateEnterTime);
Chong Zhanga1320c52020-12-15 14:30:12 -0800481 switch (state) {
482 case PAUSED:
483 pausedTime = pausedTime + elapsedTime;
484 break;
485 case RUNNING:
486 runningTime = runningTime + elapsedTime;
487 break;
488 case NOT_STARTED:
489 waitingTime = waitingTime + elapsedTime;
490 break;
491 default:
492 break;
493 }
494 }
495 if (newState == PAUSED) {
496 pauseCount++;
497 }
498 stateEnterTime = nowTime;
499 state = newState;
500}
501
Chong Zhangbc062482020-10-14 16:43:53 -0700502void TranscodingSessionController::updateCurrentSession_l() {
Chong Zhangbc062482020-10-14 16:43:53 -0700503 Session* curSession = mCurrentSession;
Chong Zhangebd86d32021-03-29 11:30:56 -0700504 Session* topSession = nullptr;
Chong Zhangbc062482020-10-14 16:43:53 -0700505
Chong Zhang87d199c2021-03-01 19:02:18 -0800506 // Delayed init of transcoder and watchdog.
507 if (mTranscoder == nullptr) {
508 mTranscoder = mTranscoderFactory(shared_from_this());
509 mWatchdog = std::make_shared<Watchdog>(this, mConfig.watchdogTimeoutUs);
Chong Zhang8677f1f2021-01-21 20:37:35 +0000510 }
511
Chong Zhang87d199c2021-03-01 19:02:18 -0800512 // If we found a different top session, or the top session's running state is not
513 // correct. Take some actions to ensure it's correct.
514 while ((topSession = getTopSession_l()) != curSession ||
515 (topSession != nullptr && !topSession->isRunning())) {
516 ALOGV("updateCurrentSession_l: topSession is %s, curSession is %s",
517 topSession == nullptr ? "null" : sessionToString(topSession->key).c_str(),
518 curSession == nullptr ? "null" : sessionToString(curSession->key).c_str());
Chong Zhang457c6892021-02-01 15:34:20 -0800519
Chong Zhang87d199c2021-03-01 19:02:18 -0800520 // If current session is running, pause it first. Note this is needed for either
521 // cases: 1) Top session is changing to another session, or 2) Top session is
522 // changing to null (which means we should be globally paused).
Chong Zhanga1320c52020-12-15 14:30:12 -0800523 if (curSession != nullptr && curSession->getState() == Session::RUNNING) {
Chong Zhangbc062482020-10-14 16:43:53 -0700524 mTranscoder->pause(curSession->key.first, curSession->key.second);
Chong Zhang457c6892021-02-01 15:34:20 -0800525 setSessionState_l(curSession, Session::PAUSED);
Chong Zhangbc062482020-10-14 16:43:53 -0700526 }
Chong Zhang87d199c2021-03-01 19:02:18 -0800527
528 if (topSession == nullptr) {
529 // Nothing more to run (either no session or globally paused).
530 break;
531 }
532
533 // Otherwise, ensure topSession is running.
534 if (topSession->getState() == Session::NOT_STARTED) {
Chong Zhangebd86d32021-03-29 11:30:56 -0700535 // Check if at least one client has quota to start the session.
536 bool keepForClient = false;
537 for (uid_t uid : topSession->allClientUids) {
Chong Zhangb28d6ca2021-04-15 18:02:21 -0700538 if (mPacer->onSessionStarted(uid, topSession->callingUid)) {
Chong Zhangebd86d32021-03-29 11:30:56 -0700539 keepForClient = true;
540 // DO NOT break here, because book-keeping still needs to happen
541 // for the other uids.
542 }
543 }
544 if (!keepForClient) {
545 // Unfortunately all uids requesting this session are out of quota.
546 // Drop this session and try the next one.
Chong Zhang87d199c2021-03-01 19:02:18 -0800547 {
548 auto clientCallback = mSessionMap[topSession->key].callback.lock();
549 if (clientCallback != nullptr) {
550 clientCallback->onTranscodingFailed(
551 topSession->key.second, TranscodingErrorCode::kDroppedByService);
552 }
553 }
554 removeSession_l(topSession->key, Session::DROPPED_BY_PACER);
555 continue;
Chong Zhangbc062482020-10-14 16:43:53 -0700556 }
Chong Zhang87d199c2021-03-01 19:02:18 -0800557 mTranscoder->start(topSession->key.first, topSession->key.second, topSession->request,
558 topSession->callingUid, topSession->callback.lock());
559 setSessionState_l(topSession, Session::RUNNING);
560 } else if (topSession->getState() == Session::PAUSED) {
561 mTranscoder->resume(topSession->key.first, topSession->key.second, topSession->request,
562 topSession->callingUid, topSession->callback.lock());
Chong Zhang457c6892021-02-01 15:34:20 -0800563 setSessionState_l(topSession, Session::RUNNING);
Chong Zhangbc062482020-10-14 16:43:53 -0700564 }
Chong Zhang87d199c2021-03-01 19:02:18 -0800565 break;
Chong Zhangbc062482020-10-14 16:43:53 -0700566 }
567 mCurrentSession = topSession;
568}
569
Chong Zhangebd86d32021-03-29 11:30:56 -0700570void TranscodingSessionController::addUidToSession_l(uid_t clientUid,
571 const SessionKeyType& sessionKey) {
572 // If it's an offline session, the queue was already added in constructor.
573 // If it's a real-time sessions, check if a queue is already present for the uid,
574 // and add a new queue if needed.
575 if (clientUid != OFFLINE_UID) {
576 if (mSessionQueues.count(clientUid) == 0) {
577 mUidPolicy->registerMonitorUid(clientUid);
578 if (mUidPolicy->isUidOnTop(clientUid)) {
579 mUidSortedList.push_front(clientUid);
580 } else {
581 // Shouldn't be submitting real-time requests from non-top app,
582 // put it in front of the offline queue.
583 mUidSortedList.insert(mOfflineUidIterator, clientUid);
584 }
585 } else if (clientUid != *mUidSortedList.begin()) {
586 if (mUidPolicy->isUidOnTop(clientUid)) {
587 mUidSortedList.remove(clientUid);
588 mUidSortedList.push_front(clientUid);
589 }
590 }
591 }
592 // Append this session to the uid's queue.
593 mSessionQueues[clientUid].push_back(sessionKey);
594}
595
Chong Zhang2a3c9672021-03-31 15:36:32 -0700596void TranscodingSessionController::removeSession_l(
597 const SessionKeyType& sessionKey, Session::State finalState,
598 const std::shared_ptr<std::function<bool(uid_t uid)>>& keepUid) {
Chong Zhangbc062482020-10-14 16:43:53 -0700599 ALOGV("%s: session %s", __FUNCTION__, sessionToString(sessionKey).c_str());
600
601 if (mSessionMap.count(sessionKey) == 0) {
602 ALOGE("session %s doesn't exist", sessionToString(sessionKey).c_str());
603 return;
604 }
605
606 // Remove session from uid's queue.
Chong Zhangebd86d32021-03-29 11:30:56 -0700607 bool uidQueueRemoved = false;
Chong Zhang2a3c9672021-03-31 15:36:32 -0700608 std::unordered_set<uid_t> remainingUids;
Chong Zhangebd86d32021-03-29 11:30:56 -0700609 for (uid_t uid : mSessionMap[sessionKey].allClientUids) {
Chong Zhang2a3c9672021-03-31 15:36:32 -0700610 if (keepUid != nullptr) {
611 if ((*keepUid)(uid)) {
612 remainingUids.insert(uid);
613 continue;
614 }
615 // If we have uids to keep, the session is not going to any final
616 // state we can't use onSessionCompleted as the running time will
617 // not be valid. Only notify pacer to stop tracking this session.
618 mPacer->onSessionCancelled(uid);
Chong Zhangebd86d32021-03-29 11:30:56 -0700619 }
620 SessionQueueType& sessionQueue = mSessionQueues[uid];
621 auto it = std::find(sessionQueue.begin(), sessionQueue.end(), sessionKey);
622 if (it == sessionQueue.end()) {
623 ALOGW("couldn't find session %s in queue for uid %d",
624 sessionToString(sessionKey).c_str(), uid);
625 continue;
626 }
627 sessionQueue.erase(it);
628
629 // If this is the last session in a real-time queue, remove this uid's queue.
630 if (uid != OFFLINE_UID && sessionQueue.empty()) {
631 mUidSortedList.remove(uid);
632 mSessionQueues.erase(uid);
633 mUidPolicy->unregisterMonitorUid(uid);
634
635 uidQueueRemoved = true;
636 }
Chong Zhangbc062482020-10-14 16:43:53 -0700637 }
Chong Zhangbc062482020-10-14 16:43:53 -0700638
Chong Zhangebd86d32021-03-29 11:30:56 -0700639 if (uidQueueRemoved) {
Chong Zhangbc062482020-10-14 16:43:53 -0700640 std::unordered_set<uid_t> topUids = mUidPolicy->getTopUids();
641 moveUidsToTop_l(topUids, false /*preserveTopUid*/);
642 }
643
Chong Zhang2a3c9672021-03-31 15:36:32 -0700644 if (keepUid != nullptr) {
645 mSessionMap[sessionKey].allClientUids = remainingUids;
Chong Zhangebd86d32021-03-29 11:30:56 -0700646 return;
647 }
648
Chong Zhangbc062482020-10-14 16:43:53 -0700649 // Clear current session.
650 if (mCurrentSession == &mSessionMap[sessionKey]) {
651 mCurrentSession = nullptr;
652 }
653
Chong Zhang457c6892021-02-01 15:34:20 -0800654 setSessionState_l(&mSessionMap[sessionKey], finalState);
Chong Zhang87d199c2021-03-01 19:02:18 -0800655
Chong Zhang2a3c9672021-03-31 15:36:32 -0700656 // We can use onSessionCompleted() even for CANCELLED, because runningTime is
657 // now updated by setSessionState_l().
658 for (uid_t uid : mSessionMap[sessionKey].allClientUids) {
659 mPacer->onSessionCompleted(uid, mSessionMap[sessionKey].runningTime);
Chong Zhang87d199c2021-03-01 19:02:18 -0800660 }
661
Chong Zhanga1320c52020-12-15 14:30:12 -0800662 mSessionHistory.push_back(mSessionMap[sessionKey]);
663 if (mSessionHistory.size() > kSessionHistoryMax) {
664 mSessionHistory.erase(mSessionHistory.begin());
665 }
666
Chong Zhangbc062482020-10-14 16:43:53 -0700667 // Remove session from session map.
668 mSessionMap.erase(sessionKey);
669}
670
671/**
672 * Moves the set of uids to the front of mUidSortedList (which is used to pick
673 * the next session to run).
674 *
675 * This is called when 1) we received a onTopUidsChanged() callback from UidPolicy,
676 * or 2) we removed the session queue for a uid because it becomes empty.
677 *
678 * In case of 1), if there are multiple uids in the set, and the current front
679 * uid in mUidSortedList is still in the set, we try to keep that uid at front
680 * so that current session run is not interrupted. (This is not a concern for case 2)
681 * because the queue for a uid was just removed entirely.)
682 */
683void TranscodingSessionController::moveUidsToTop_l(const std::unordered_set<uid_t>& uids,
684 bool preserveTopUid) {
685 // If uid set is empty, nothing to do. Do not change the queue status.
686 if (uids.empty()) {
687 return;
688 }
689
690 // Save the current top uid.
691 uid_t curTopUid = *mUidSortedList.begin();
692 bool pushCurTopToFront = false;
693 int32_t numUidsMoved = 0;
694
695 // Go through the sorted uid list once, and move the ones in top set to front.
696 for (auto it = mUidSortedList.begin(); it != mUidSortedList.end();) {
697 uid_t uid = *it;
698
699 if (uid != OFFLINE_UID && uids.count(uid) > 0) {
700 it = mUidSortedList.erase(it);
701
702 // If this is the top we're preserving, don't push it here, push
703 // it after the for-loop.
704 if (uid == curTopUid && preserveTopUid) {
705 pushCurTopToFront = true;
706 } else {
707 mUidSortedList.push_front(uid);
708 }
709
710 // If we found all uids in the set, break out.
711 if (++numUidsMoved == uids.size()) {
712 break;
713 }
714 } else {
715 ++it;
716 }
717 }
718
719 if (pushCurTopToFront) {
720 mUidSortedList.push_front(curTopUid);
721 }
722}
723
724bool TranscodingSessionController::submit(
Linus Nilssona99f4042021-02-25 15:49:43 -0800725 ClientIdType clientId, SessionIdType sessionId, uid_t callingUid, uid_t clientUid,
Chong Zhangbc062482020-10-14 16:43:53 -0700726 const TranscodingRequestParcel& request,
727 const std::weak_ptr<ITranscodingClientCallback>& callback) {
728 SessionKeyType sessionKey = std::make_pair(clientId, sessionId);
729
730 ALOGV("%s: session %s, uid %d, prioirty %d", __FUNCTION__, sessionToString(sessionKey).c_str(),
Linus Nilssona99f4042021-02-25 15:49:43 -0800731 clientUid, (int32_t)request.priority);
Chong Zhangbc062482020-10-14 16:43:53 -0700732
733 std::scoped_lock lock{mLock};
734
735 if (mSessionMap.count(sessionKey) > 0) {
736 ALOGE("session %s already exists", sessionToString(sessionKey).c_str());
737 return false;
738 }
739
Chong Zhangcf3f8ee2020-10-29 18:38:37 -0700740 // Add the uid package name to the store of package names we already know.
Linus Nilssona99f4042021-02-25 15:49:43 -0800741 if (mUidPackageNames.count(clientUid) == 0) {
742 mUidPackageNames.emplace(clientUid, request.clientPackageName);
Chong Zhangcf3f8ee2020-10-29 18:38:37 -0700743 }
744
Chong Zhangbc062482020-10-14 16:43:53 -0700745 // TODO(chz): only support offline vs real-time for now. All kUnspecified sessions
746 // go to offline queue.
747 if (request.priority == TranscodingSessionPriority::kUnspecified) {
Linus Nilssona99f4042021-02-25 15:49:43 -0800748 clientUid = OFFLINE_UID;
Chong Zhangbc062482020-10-14 16:43:53 -0700749 }
750
751 // Add session to session map.
752 mSessionMap[sessionKey].key = sessionKey;
Linus Nilssona99f4042021-02-25 15:49:43 -0800753 mSessionMap[sessionKey].callingUid = callingUid;
Chong Zhangebd86d32021-03-29 11:30:56 -0700754 mSessionMap[sessionKey].allClientUids.insert(clientUid);
Chong Zhangbc062482020-10-14 16:43:53 -0700755 mSessionMap[sessionKey].request = request;
756 mSessionMap[sessionKey].callback = callback;
Chong Zhang457c6892021-02-01 15:34:20 -0800757 setSessionState_l(&mSessionMap[sessionKey], Session::NOT_STARTED);
Chong Zhangbc062482020-10-14 16:43:53 -0700758
Chong Zhangebd86d32021-03-29 11:30:56 -0700759 addUidToSession_l(clientUid, sessionKey);
Chong Zhangbc062482020-10-14 16:43:53 -0700760
761 updateCurrentSession_l();
762
763 validateState_l();
764 return true;
765}
766
767bool TranscodingSessionController::cancel(ClientIdType clientId, SessionIdType sessionId) {
768 SessionKeyType sessionKey = std::make_pair(clientId, sessionId);
769
770 ALOGV("%s: session %s", __FUNCTION__, sessionToString(sessionKey).c_str());
771
Chong Zhangebd86d32021-03-29 11:30:56 -0700772 std::list<SessionKeyType> sessionsToRemove, sessionsForOffline;
Chong Zhangbc062482020-10-14 16:43:53 -0700773
774 std::scoped_lock lock{mLock};
775
776 if (sessionId < 0) {
777 for (auto it = mSessionMap.begin(); it != mSessionMap.end(); ++it) {
Chong Zhangebd86d32021-03-29 11:30:56 -0700778 if (it->first.first == clientId) {
779 // If there is offline request, only keep the offline client;
780 // otherwise remove the session.
781 if (it->second.allClientUids.count(OFFLINE_UID) > 0) {
782 sessionsForOffline.push_back(it->first);
783 } else {
784 sessionsToRemove.push_back(it->first);
785 }
Chong Zhangbc062482020-10-14 16:43:53 -0700786 }
787 }
788 } else {
789 if (mSessionMap.count(sessionKey) == 0) {
790 ALOGE("session %s doesn't exist", sessionToString(sessionKey).c_str());
791 return false;
792 }
793 sessionsToRemove.push_back(sessionKey);
794 }
795
796 for (auto it = sessionsToRemove.begin(); it != sessionsToRemove.end(); ++it) {
797 // If the session has ever been started, stop it now.
798 // Note that stop() is needed even if the session is currently paused. This instructs
799 // the transcoder to discard any states for the session, otherwise the states may
800 // never be discarded.
Chong Zhanga1320c52020-12-15 14:30:12 -0800801 if (mSessionMap[*it].getState() != Session::NOT_STARTED) {
Chong Zhangbc062482020-10-14 16:43:53 -0700802 mTranscoder->stop(it->first, it->second);
803 }
804
805 // Remove the session.
Chong Zhanga1320c52020-12-15 14:30:12 -0800806 removeSession_l(*it, Session::CANCELED);
Chong Zhangbc062482020-10-14 16:43:53 -0700807 }
808
Chong Zhang2a3c9672021-03-31 15:36:32 -0700809 auto keepUid = std::make_shared<std::function<bool(uid_t)>>(
810 [](uid_t uid) { return uid == OFFLINE_UID; });
Chong Zhangebd86d32021-03-29 11:30:56 -0700811 for (auto it = sessionsForOffline.begin(); it != sessionsForOffline.end(); ++it) {
Chong Zhang2a3c9672021-03-31 15:36:32 -0700812 removeSession_l(*it, Session::CANCELED, keepUid);
Chong Zhangebd86d32021-03-29 11:30:56 -0700813 }
814
Chong Zhangbc062482020-10-14 16:43:53 -0700815 // Start next session.
816 updateCurrentSession_l();
817
818 validateState_l();
819 return true;
820}
821
Chong Zhangebd86d32021-03-29 11:30:56 -0700822bool TranscodingSessionController::addClientUid(ClientIdType clientId, SessionIdType sessionId,
823 uid_t clientUid) {
824 SessionKeyType sessionKey = std::make_pair(clientId, sessionId);
825
826 std::scoped_lock lock{mLock};
827
828 if (mSessionMap.count(sessionKey) == 0) {
829 ALOGE("session %s doesn't exist", sessionToString(sessionKey).c_str());
830 return false;
831 }
832
833 if (mSessionMap[sessionKey].allClientUids.count(clientUid) > 0) {
834 ALOGE("session %s already has uid %d", sessionToString(sessionKey).c_str(), clientUid);
835 return false;
836 }
837
838 mSessionMap[sessionKey].allClientUids.insert(clientUid);
839 addUidToSession_l(clientUid, sessionKey);
840
841 updateCurrentSession_l();
842
843 validateState_l();
844 return true;
845}
846
847bool TranscodingSessionController::getClientUids(ClientIdType clientId, SessionIdType sessionId,
848 std::vector<int32_t>* out_clientUids) {
849 SessionKeyType sessionKey = std::make_pair(clientId, sessionId);
850
851 std::scoped_lock lock{mLock};
852
853 if (mSessionMap.count(sessionKey) == 0) {
854 ALOGE("session %s doesn't exist", sessionToString(sessionKey).c_str());
855 return false;
856 }
857
858 out_clientUids->clear();
859 for (uid_t uid : mSessionMap[sessionKey].allClientUids) {
860 if (uid != OFFLINE_UID) {
861 out_clientUids->push_back(uid);
862 }
863 }
864 return true;
865}
866
Chong Zhangbc062482020-10-14 16:43:53 -0700867bool TranscodingSessionController::getSession(ClientIdType clientId, SessionIdType sessionId,
868 TranscodingRequestParcel* request) {
869 SessionKeyType sessionKey = std::make_pair(clientId, sessionId);
870
871 std::scoped_lock lock{mLock};
872
873 if (mSessionMap.count(sessionKey) == 0) {
874 ALOGE("session %s doesn't exist", sessionToString(sessionKey).c_str());
875 return false;
876 }
877
878 *(TranscodingRequest*)request = mSessionMap[sessionKey].request;
879 return true;
880}
881
882void TranscodingSessionController::notifyClient(ClientIdType clientId, SessionIdType sessionId,
883 const char* reason,
884 std::function<void(const SessionKeyType&)> func) {
885 SessionKeyType sessionKey = std::make_pair(clientId, sessionId);
886
887 std::scoped_lock lock{mLock};
888
889 if (mSessionMap.count(sessionKey) == 0) {
890 ALOGW("%s: ignoring %s for session %s that doesn't exist", __FUNCTION__, reason,
891 sessionToString(sessionKey).c_str());
892 return;
893 }
894
895 // Only ignore if session was never started. In particular, propagate the status
896 // to client if the session is paused. Transcoder could have posted finish when
897 // we're pausing it, and the finish arrived after we changed current session.
Chong Zhanga1320c52020-12-15 14:30:12 -0800898 if (mSessionMap[sessionKey].getState() == Session::NOT_STARTED) {
Chong Zhangbc062482020-10-14 16:43:53 -0700899 ALOGW("%s: ignoring %s for session %s that was never started", __FUNCTION__, reason,
900 sessionToString(sessionKey).c_str());
901 return;
902 }
903
904 ALOGV("%s: session %s %s", __FUNCTION__, sessionToString(sessionKey).c_str(), reason);
905 func(sessionKey);
906}
907
908void TranscodingSessionController::onStarted(ClientIdType clientId, SessionIdType sessionId) {
909 notifyClient(clientId, sessionId, "started", [=](const SessionKeyType& sessionKey) {
910 auto callback = mSessionMap[sessionKey].callback.lock();
911 if (callback != nullptr) {
912 callback->onTranscodingStarted(sessionId);
913 }
914 });
915}
916
917void TranscodingSessionController::onPaused(ClientIdType clientId, SessionIdType sessionId) {
918 notifyClient(clientId, sessionId, "paused", [=](const SessionKeyType& sessionKey) {
919 auto callback = mSessionMap[sessionKey].callback.lock();
920 if (callback != nullptr) {
921 callback->onTranscodingPaused(sessionId);
922 }
923 });
924}
925
926void TranscodingSessionController::onResumed(ClientIdType clientId, SessionIdType sessionId) {
927 notifyClient(clientId, sessionId, "resumed", [=](const SessionKeyType& sessionKey) {
928 auto callback = mSessionMap[sessionKey].callback.lock();
929 if (callback != nullptr) {
930 callback->onTranscodingResumed(sessionId);
931 }
932 });
933}
934
935void TranscodingSessionController::onFinish(ClientIdType clientId, SessionIdType sessionId) {
936 notifyClient(clientId, sessionId, "finish", [=](const SessionKeyType& sessionKey) {
937 {
938 auto clientCallback = mSessionMap[sessionKey].callback.lock();
939 if (clientCallback != nullptr) {
940 clientCallback->onTranscodingFinished(
941 sessionId, TranscodingResultParcel({sessionId, -1 /*actualBitrateBps*/,
942 std::nullopt /*sessionStats*/}));
943 }
944 }
945
946 // Remove the session.
Chong Zhanga1320c52020-12-15 14:30:12 -0800947 removeSession_l(sessionKey, Session::FINISHED);
Chong Zhangbc062482020-10-14 16:43:53 -0700948
949 // Start next session.
950 updateCurrentSession_l();
951
952 validateState_l();
953 });
954}
955
956void TranscodingSessionController::onError(ClientIdType clientId, SessionIdType sessionId,
957 TranscodingErrorCode err) {
958 notifyClient(clientId, sessionId, "error", [=](const SessionKeyType& sessionKey) {
Chong Zhang457c6892021-02-01 15:34:20 -0800959 if (err == TranscodingErrorCode::kWatchdogTimeout) {
960 // Abandon the transcoder, as its handler thread might be stuck in some call to
961 // MediaTranscoder altogether, and may not be able to handle any new tasks.
962 mTranscoder->stop(clientId, sessionId, true /*abandon*/);
963 // Clear the last ref count before we create new transcoder.
964 mTranscoder = nullptr;
Chong Zhang87d199c2021-03-01 19:02:18 -0800965 mTranscoder = mTranscoderFactory(shared_from_this());
Chong Zhang457c6892021-02-01 15:34:20 -0800966 }
967
Chong Zhangbc062482020-10-14 16:43:53 -0700968 {
969 auto clientCallback = mSessionMap[sessionKey].callback.lock();
970 if (clientCallback != nullptr) {
971 clientCallback->onTranscodingFailed(sessionId, err);
972 }
973 }
974
975 // Remove the session.
Chong Zhanga1320c52020-12-15 14:30:12 -0800976 removeSession_l(sessionKey, Session::ERROR);
Chong Zhangbc062482020-10-14 16:43:53 -0700977
978 // Start next session.
979 updateCurrentSession_l();
980
981 validateState_l();
982 });
983}
984
985void TranscodingSessionController::onProgressUpdate(ClientIdType clientId, SessionIdType sessionId,
986 int32_t progress) {
987 notifyClient(clientId, sessionId, "progress", [=](const SessionKeyType& sessionKey) {
988 auto callback = mSessionMap[sessionKey].callback.lock();
989 if (callback != nullptr) {
990 callback->onProgressUpdate(sessionId, progress);
991 }
992 mSessionMap[sessionKey].lastProgress = progress;
993 });
994}
995
Chong Zhang457c6892021-02-01 15:34:20 -0800996void TranscodingSessionController::onHeartBeat(ClientIdType clientId, SessionIdType sessionId) {
997 notifyClient(clientId, sessionId, "heart-beat",
998 [=](const SessionKeyType& /*sessionKey*/) { mWatchdog->keepAlive(); });
999}
1000
Chong Zhangeffd8962020-12-02 14:29:09 -08001001void TranscodingSessionController::onResourceLost(ClientIdType clientId, SessionIdType sessionId) {
Chong Zhangbc062482020-10-14 16:43:53 -07001002 ALOGI("%s", __FUNCTION__);
1003
Chong Zhangeffd8962020-12-02 14:29:09 -08001004 notifyClient(clientId, sessionId, "resource_lost", [=](const SessionKeyType& sessionKey) {
1005 if (mResourceLost) {
1006 return;
Chong Zhangbc062482020-10-14 16:43:53 -07001007 }
Chong Zhangbc062482020-10-14 16:43:53 -07001008
Chong Zhangeffd8962020-12-02 14:29:09 -08001009 Session* resourceLostSession = &mSessionMap[sessionKey];
Chong Zhanga1320c52020-12-15 14:30:12 -08001010 if (resourceLostSession->getState() != Session::RUNNING) {
Chong Zhangeffd8962020-12-02 14:29:09 -08001011 ALOGW("session %s lost resource but is no longer running",
Chong Zhanga1320c52020-12-15 14:30:12 -08001012 sessionToString(sessionKey).c_str());
Chong Zhangeffd8962020-12-02 14:29:09 -08001013 return;
1014 }
1015 // If we receive a resource loss event, the transcoder already paused the transcoding,
1016 // so we don't need to call onPaused() to pause it. However, we still need to notify
1017 // the client and update the session state here.
Chong Zhang457c6892021-02-01 15:34:20 -08001018 setSessionState_l(resourceLostSession, Session::PAUSED);
Chong Zhangeffd8962020-12-02 14:29:09 -08001019 // Notify the client as a paused event.
1020 auto clientCallback = resourceLostSession->callback.lock();
1021 if (clientCallback != nullptr) {
1022 clientCallback->onTranscodingPaused(sessionKey.second);
1023 }
Chong Zhang8677f1f2021-01-21 20:37:35 +00001024 if (mResourcePolicy != nullptr) {
1025 mResourcePolicy->setPidResourceLost(resourceLostSession->request.clientPid);
1026 }
Chong Zhangeffd8962020-12-02 14:29:09 -08001027 mResourceLost = true;
1028
1029 validateState_l();
1030 });
Chong Zhangbc062482020-10-14 16:43:53 -07001031}
1032
1033void TranscodingSessionController::onTopUidsChanged(const std::unordered_set<uid_t>& uids) {
1034 if (uids.empty()) {
1035 ALOGW("%s: ignoring empty uids", __FUNCTION__);
1036 return;
1037 }
1038
1039 std::string uidStr;
1040 for (auto it = uids.begin(); it != uids.end(); it++) {
1041 if (!uidStr.empty()) {
1042 uidStr += ", ";
1043 }
1044 uidStr += std::to_string(*it);
1045 }
1046
1047 ALOGD("%s: topUids: size %zu, uids: %s", __FUNCTION__, uids.size(), uidStr.c_str());
1048
1049 std::scoped_lock lock{mLock};
1050
1051 moveUidsToTop_l(uids, true /*preserveTopUid*/);
1052
1053 updateCurrentSession_l();
1054
1055 validateState_l();
1056}
1057
Chong Zhang2a3c9672021-03-31 15:36:32 -07001058void TranscodingSessionController::onUidGone(uid_t goneUid) {
1059 ALOGD("%s: gone uid %u", __FUNCTION__, goneUid);
1060
1061 std::list<SessionKeyType> sessionsToRemove, sessionsForOtherUids;
1062
1063 std::scoped_lock lock{mLock};
1064
1065 for (auto it = mSessionMap.begin(); it != mSessionMap.end(); ++it) {
1066 if (it->second.allClientUids.count(goneUid) > 0) {
1067 // If goneUid is the only uid, remove the session; otherwise, only
1068 // remove the uid from the session.
1069 if (it->second.allClientUids.size() > 1) {
1070 sessionsForOtherUids.push_back(it->first);
1071 } else {
1072 sessionsToRemove.push_back(it->first);
1073 }
1074 }
1075 }
1076
1077 for (auto it = sessionsToRemove.begin(); it != sessionsToRemove.end(); ++it) {
1078 // If the session has ever been started, stop it now.
1079 // Note that stop() is needed even if the session is currently paused. This instructs
1080 // the transcoder to discard any states for the session, otherwise the states may
1081 // never be discarded.
1082 if (mSessionMap[*it].getState() != Session::NOT_STARTED) {
1083 mTranscoder->stop(it->first, it->second);
1084 }
1085
1086 {
1087 auto clientCallback = mSessionMap[*it].callback.lock();
1088 if (clientCallback != nullptr) {
1089 clientCallback->onTranscodingFailed(it->second,
1090 TranscodingErrorCode::kUidGoneCancelled);
1091 }
1092 }
1093
1094 // Remove the session.
1095 removeSession_l(*it, Session::CANCELED);
1096 }
1097
1098 auto keepUid = std::make_shared<std::function<bool(uid_t)>>(
1099 [goneUid](uid_t uid) { return uid != goneUid; });
1100 for (auto it = sessionsForOtherUids.begin(); it != sessionsForOtherUids.end(); ++it) {
1101 removeSession_l(*it, Session::CANCELED, keepUid);
1102 }
1103
1104 // Start next session.
1105 updateCurrentSession_l();
1106
1107 validateState_l();
1108}
1109
Chong Zhangbc062482020-10-14 16:43:53 -07001110void TranscodingSessionController::onResourceAvailable() {
1111 std::scoped_lock lock{mLock};
1112
1113 if (!mResourceLost) {
1114 return;
1115 }
1116
1117 ALOGI("%s", __FUNCTION__);
1118
1119 mResourceLost = false;
1120 updateCurrentSession_l();
1121
1122 validateState_l();
1123}
1124
Chong Zhang8677f1f2021-01-21 20:37:35 +00001125void TranscodingSessionController::onThrottlingStarted() {
1126 std::scoped_lock lock{mLock};
1127
1128 if (mThermalThrottling) {
1129 return;
1130 }
1131
1132 ALOGI("%s", __FUNCTION__);
1133
1134 mThermalThrottling = true;
1135 updateCurrentSession_l();
1136
1137 validateState_l();
1138}
1139
1140void TranscodingSessionController::onThrottlingStopped() {
1141 std::scoped_lock lock{mLock};
1142
1143 if (!mThermalThrottling) {
1144 return;
1145 }
1146
1147 ALOGI("%s", __FUNCTION__);
1148
1149 mThermalThrottling = false;
1150 updateCurrentSession_l();
1151
1152 validateState_l();
1153}
1154
Chong Zhangbc062482020-10-14 16:43:53 -07001155void TranscodingSessionController::validateState_l() {
1156#ifdef VALIDATE_STATE
1157 LOG_ALWAYS_FATAL_IF(mSessionQueues.count(OFFLINE_UID) != 1,
1158 "mSessionQueues offline queue number is not 1");
1159 LOG_ALWAYS_FATAL_IF(*mOfflineUidIterator != OFFLINE_UID,
1160 "mOfflineUidIterator not pointing to offline uid");
1161 LOG_ALWAYS_FATAL_IF(mUidSortedList.size() != mSessionQueues.size(),
Chong Zhangebd86d32021-03-29 11:30:56 -07001162 "mUidSortedList and mSessionQueues size mismatch, %zu vs %zu",
1163 mUidSortedList.size(), mSessionQueues.size());
Chong Zhangbc062482020-10-14 16:43:53 -07001164
1165 int32_t totalSessions = 0;
1166 for (auto uid : mUidSortedList) {
1167 LOG_ALWAYS_FATAL_IF(mSessionQueues.count(uid) != 1,
1168 "mSessionQueues count for uid %d is not 1", uid);
1169 for (auto& sessionKey : mSessionQueues[uid]) {
1170 LOG_ALWAYS_FATAL_IF(mSessionMap.count(sessionKey) != 1,
1171 "mSessions count for session %s is not 1",
1172 sessionToString(sessionKey).c_str());
1173 }
1174
1175 totalSessions += mSessionQueues[uid].size();
1176 }
Chong Zhangebd86d32021-03-29 11:30:56 -07001177 int32_t totalSessionsAlternative = 0;
1178 for (auto const& s : mSessionMap) {
1179 totalSessionsAlternative += s.second.allClientUids.size();
1180 }
1181 LOG_ALWAYS_FATAL_IF(totalSessions != totalSessionsAlternative,
1182 "session count (including dup) from mSessionQueues doesn't match that from "
1183 "mSessionMap, %d vs %d",
1184 totalSessions, totalSessionsAlternative);
Chong Zhangbc062482020-10-14 16:43:53 -07001185#endif // VALIDATE_STATE
1186}
1187
1188} // namespace android