blob: d3a1836619347eb7e6b3f11a1f04ef730461a4df [file] [log] [blame]
Chong Zhang6d58e4b2020-03-31 09:41:10 -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
Chong Zhangacb33502020-04-20 11:04:48 -070017//#define LOG_NDEBUG 0
Chong Zhang6d58e4b2020-03-31 09:41:10 -070018#define LOG_TAG "TranscodingJobScheduler"
19
20#define VALIDATE_STATE 1
21
22#include <inttypes.h>
23#include <media/TranscodingJobScheduler.h>
Chong Zhang0579c6f2020-10-05 12:03:34 -070024#include <media/TranscodingUidPolicy.h>
Chong Zhang6d58e4b2020-03-31 09:41:10 -070025#include <utils/Log.h>
26
27#include <utility>
28
29namespace android {
30
Chong Zhang15c192a2020-05-05 16:24:00 -070031static_assert((JobIdType)-1 < 0, "JobIdType should be signed");
32
Chong Zhang7ae4e2f2020-04-17 15:24:34 -070033constexpr static uid_t OFFLINE_UID = -1;
Chong Zhang6d58e4b2020-03-31 09:41:10 -070034
35//static
36String8 TranscodingJobScheduler::jobToString(const JobKeyType& jobKey) {
37 return String8::format("{client:%lld, job:%d}", (long long)jobKey.first, jobKey.second);
38}
39
Chong Zhang0579c6f2020-10-05 12:03:34 -070040//static
41const char* TranscodingJobScheduler::jobStateToString(const Job::State jobState) {
42 switch (jobState) {
43 case Job::State::NOT_STARTED:
44 return "NOT_STARTED";
45 case Job::State::RUNNING:
46 return "RUNNING";
47 case Job::State::PAUSED:
48 return "PAUSED";
49 default:
50 break;
51 }
52 return "(unknown)";
53}
54
Chong Zhang6d58e4b2020-03-31 09:41:10 -070055TranscodingJobScheduler::TranscodingJobScheduler(
56 const std::shared_ptr<TranscoderInterface>& transcoder,
Chong Zhangf9077512020-09-21 21:02:06 -070057 const std::shared_ptr<UidPolicyInterface>& uidPolicy,
58 const std::shared_ptr<ResourcePolicyInterface>& resourcePolicy)
59 : mTranscoder(transcoder),
60 mUidPolicy(uidPolicy),
61 mResourcePolicy(resourcePolicy),
62 mCurrentJob(nullptr),
63 mResourceLost(false) {
Chong Zhang6d58e4b2020-03-31 09:41:10 -070064 // Only push empty offline queue initially. Realtime queues are added when requests come in.
Chong Zhang7ae4e2f2020-04-17 15:24:34 -070065 mUidSortedList.push_back(OFFLINE_UID);
66 mOfflineUidIterator = mUidSortedList.begin();
67 mJobQueues.emplace(OFFLINE_UID, JobQueueType());
Chong Zhang6d58e4b2020-03-31 09:41:10 -070068}
69
70TranscodingJobScheduler::~TranscodingJobScheduler() {}
71
hkuangff778692020-10-02 15:40:56 -070072void TranscodingJobScheduler::dumpAllJobs(int fd, const Vector<String16>& args __unused) {
73 String8 result;
74
75 const size_t SIZE = 256;
76 char buffer[SIZE];
77 std::scoped_lock lock{mLock};
78
Chong Zhang0579c6f2020-10-05 12:03:34 -070079 snprintf(buffer, SIZE, "\n========== Dumping all jobs queues =========\n");
80 result.append(buffer);
81 snprintf(buffer, SIZE, " Total num of Jobs: %zu\n", mJobMap.size());
hkuangff778692020-10-02 15:40:56 -070082 result.append(buffer);
83
Chong Zhang0579c6f2020-10-05 12:03:34 -070084 std::vector<int32_t> uids(mUidSortedList.begin(), mUidSortedList.end());
85 // Exclude last uid, which is for offline queue
86 uids.pop_back();
87 std::vector<std::string> packageNames;
88 if (TranscodingUidPolicy::getNamesForUids(uids, &packageNames)) {
89 uids.push_back(OFFLINE_UID);
90 packageNames.push_back("(offline)");
hkuangff778692020-10-02 15:40:56 -070091 }
92
Chong Zhang0579c6f2020-10-05 12:03:34 -070093 for (int32_t i = 0; i < uids.size(); i++) {
94 const uid_t uid = uids[i];
hkuangff778692020-10-02 15:40:56 -070095
Chong Zhang0579c6f2020-10-05 12:03:34 -070096 if (mJobQueues[uid].empty()) {
97 continue;
98 }
99 snprintf(buffer, SIZE, " Uid: %d, pkg: %s\n", uid,
100 packageNames.empty() ? "(unknown)" : packageNames[i].c_str());
101 result.append(buffer);
102 snprintf(buffer, SIZE, " Num of jobs: %zu\n", mJobQueues[uid].size());
103 result.append(buffer);
104 for (auto& jobKey : mJobQueues[uid]) {
105 auto jobIt = mJobMap.find(jobKey);
106 if (jobIt == mJobMap.end()) {
107 snprintf(buffer, SIZE, "Failed to look up Job %s \n", jobToString(jobKey).c_str());
108 result.append(buffer);
109 continue;
hkuangff778692020-10-02 15:40:56 -0700110 }
Chong Zhang0579c6f2020-10-05 12:03:34 -0700111 Job& job = jobIt->second;
112 TranscodingRequestParcel& request = job.request;
113 snprintf(buffer, SIZE, " Job: %s, %s, %d%%\n", jobToString(jobKey).c_str(),
114 jobStateToString(job.state), job.lastProgress);
115 result.append(buffer);
116 snprintf(buffer, SIZE, " Src: %s\n", request.sourceFilePath.c_str());
117 result.append(buffer);
118 snprintf(buffer, SIZE, " Dst: %s\n", request.destinationFilePath.c_str());
hkuangff778692020-10-02 15:40:56 -0700119 result.append(buffer);
120 }
121 }
122
123 write(fd, result.string(), result.size());
124}
125
Chong Zhang6d58e4b2020-03-31 09:41:10 -0700126TranscodingJobScheduler::Job* TranscodingJobScheduler::getTopJob_l() {
127 if (mJobMap.empty()) {
128 return nullptr;
129 }
Chong Zhang7ae4e2f2020-04-17 15:24:34 -0700130 uid_t topUid = *mUidSortedList.begin();
131 JobKeyType topJobKey = *mJobQueues[topUid].begin();
Chong Zhang6d58e4b2020-03-31 09:41:10 -0700132 return &mJobMap[topJobKey];
133}
134
135void TranscodingJobScheduler::updateCurrentJob_l() {
136 Job* topJob = getTopJob_l();
137 Job* curJob = mCurrentJob;
138 ALOGV("updateCurrentJob: topJob is %s, curJob is %s",
139 topJob == nullptr ? "null" : jobToString(topJob->key).c_str(),
140 curJob == nullptr ? "null" : jobToString(curJob->key).c_str());
141
142 // If we found a topJob that should be run, and it's not already running,
143 // take some actions to ensure it's running.
144 if (topJob != nullptr && (topJob != curJob || topJob->state != Job::RUNNING)) {
145 // If another job is currently running, pause it first.
146 if (curJob != nullptr && curJob->state == Job::RUNNING) {
147 mTranscoder->pause(curJob->key.first, curJob->key.second);
148 curJob->state = Job::PAUSED;
149 }
150 // If we are not experiencing resource loss, we can start or resume
151 // the topJob now.
152 if (!mResourceLost) {
153 if (topJob->state == Job::NOT_STARTED) {
Chong Zhang66469272020-06-04 16:51:55 -0700154 mTranscoder->start(topJob->key.first, topJob->key.second, topJob->request,
155 topJob->callback.lock());
Chong Zhang6d58e4b2020-03-31 09:41:10 -0700156 } else if (topJob->state == Job::PAUSED) {
Chong Zhangb55c5452020-06-26 14:32:12 -0700157 mTranscoder->resume(topJob->key.first, topJob->key.second, topJob->request,
158 topJob->callback.lock());
Chong Zhang6d58e4b2020-03-31 09:41:10 -0700159 }
160 topJob->state = Job::RUNNING;
161 }
162 }
163 mCurrentJob = topJob;
164}
165
166void TranscodingJobScheduler::removeJob_l(const JobKeyType& jobKey) {
167 ALOGV("%s: job %s", __FUNCTION__, jobToString(jobKey).c_str());
168
169 if (mJobMap.count(jobKey) == 0) {
170 ALOGE("job %s doesn't exist", jobToString(jobKey).c_str());
171 return;
172 }
173
Chong Zhang7ae4e2f2020-04-17 15:24:34 -0700174 // Remove job from uid's queue.
175 const uid_t uid = mJobMap[jobKey].uid;
176 JobQueueType& jobQueue = mJobQueues[uid];
Chong Zhang6d58e4b2020-03-31 09:41:10 -0700177 auto it = std::find(jobQueue.begin(), jobQueue.end(), jobKey);
178 if (it == jobQueue.end()) {
Chong Zhang7ae4e2f2020-04-17 15:24:34 -0700179 ALOGE("couldn't find job %s in queue for uid %d", jobToString(jobKey).c_str(), uid);
Chong Zhang6d58e4b2020-03-31 09:41:10 -0700180 return;
181 }
182 jobQueue.erase(it);
183
Chong Zhang7ae4e2f2020-04-17 15:24:34 -0700184 // If this is the last job in a real-time queue, remove this uid's queue.
185 if (uid != OFFLINE_UID && jobQueue.empty()) {
186 mUidSortedList.remove(uid);
187 mJobQueues.erase(uid);
Chong Zhangacb33502020-04-20 11:04:48 -0700188 mUidPolicy->unregisterMonitorUid(uid);
189
190 std::unordered_set<uid_t> topUids = mUidPolicy->getTopUids();
191 moveUidsToTop_l(topUids, false /*preserveTopUid*/);
Chong Zhang6d58e4b2020-03-31 09:41:10 -0700192 }
193
194 // Clear current job.
195 if (mCurrentJob == &mJobMap[jobKey]) {
196 mCurrentJob = nullptr;
197 }
198
199 // Remove job from job map.
200 mJobMap.erase(jobKey);
201}
202
Chong Zhangacb33502020-04-20 11:04:48 -0700203/**
204 * Moves the set of uids to the front of mUidSortedList (which is used to pick
205 * the next job to run).
206 *
207 * This is called when 1) we received a onTopUidsChanged() callbcak from UidPolicy,
208 * or 2) we removed the job queue for a uid because it becomes empty.
209 *
210 * In case of 1), if there are multiple uids in the set, and the current front
211 * uid in mUidSortedList is still in the set, we try to keep that uid at front
212 * so that current job run is not interrupted. (This is not a concern for case 2)
213 * because the queue for a uid was just removed entirely.)
214 */
215void TranscodingJobScheduler::moveUidsToTop_l(const std::unordered_set<uid_t>& uids,
216 bool preserveTopUid) {
217 // If uid set is empty, nothing to do. Do not change the queue status.
218 if (uids.empty()) {
219 return;
220 }
221
222 // Save the current top uid.
223 uid_t curTopUid = *mUidSortedList.begin();
224 bool pushCurTopToFront = false;
225 int32_t numUidsMoved = 0;
226
227 // Go through the sorted uid list once, and move the ones in top set to front.
228 for (auto it = mUidSortedList.begin(); it != mUidSortedList.end();) {
229 uid_t uid = *it;
230
231 if (uid != OFFLINE_UID && uids.count(uid) > 0) {
232 it = mUidSortedList.erase(it);
233
234 // If this is the top we're preserving, don't push it here, push
235 // it after the for-loop.
236 if (uid == curTopUid && preserveTopUid) {
237 pushCurTopToFront = true;
238 } else {
239 mUidSortedList.push_front(uid);
240 }
241
242 // If we found all uids in the set, break out.
243 if (++numUidsMoved == uids.size()) {
244 break;
245 }
246 } else {
247 ++it;
248 }
249 }
250
251 if (pushCurTopToFront) {
252 mUidSortedList.push_front(curTopUid);
253 }
254}
255
Chong Zhang3fa408f2020-04-30 11:04:28 -0700256bool TranscodingJobScheduler::submit(ClientIdType clientId, JobIdType jobId, uid_t uid,
Chong Zhang6d58e4b2020-03-31 09:41:10 -0700257 const TranscodingRequestParcel& request,
258 const std::weak_ptr<ITranscodingClientCallback>& callback) {
259 JobKeyType jobKey = std::make_pair(clientId, jobId);
260
Chong Zhang7ae4e2f2020-04-17 15:24:34 -0700261 ALOGV("%s: job %s, uid %d, prioirty %d", __FUNCTION__, jobToString(jobKey).c_str(), uid,
Chong Zhang6d58e4b2020-03-31 09:41:10 -0700262 (int32_t)request.priority);
263
264 std::scoped_lock lock{mLock};
265
266 if (mJobMap.count(jobKey) > 0) {
267 ALOGE("job %s already exists", jobToString(jobKey).c_str());
268 return false;
269 }
270
271 // TODO(chz): only support offline vs real-time for now. All kUnspecified jobs
272 // go to offline queue.
273 if (request.priority == TranscodingJobPriority::kUnspecified) {
Chong Zhang7ae4e2f2020-04-17 15:24:34 -0700274 uid = OFFLINE_UID;
Chong Zhang6d58e4b2020-03-31 09:41:10 -0700275 }
276
277 // Add job to job map.
278 mJobMap[jobKey].key = jobKey;
Chong Zhang7ae4e2f2020-04-17 15:24:34 -0700279 mJobMap[jobKey].uid = uid;
Chong Zhang6d58e4b2020-03-31 09:41:10 -0700280 mJobMap[jobKey].state = Job::NOT_STARTED;
Chong Zhang0579c6f2020-10-05 12:03:34 -0700281 mJobMap[jobKey].lastProgress = 0;
Chong Zhang6d58e4b2020-03-31 09:41:10 -0700282 mJobMap[jobKey].request = request;
283 mJobMap[jobKey].callback = callback;
284
285 // If it's an offline job, the queue was already added in constructor.
Chong Zhang7ae4e2f2020-04-17 15:24:34 -0700286 // If it's a real-time jobs, check if a queue is already present for the uid,
Chong Zhang6d58e4b2020-03-31 09:41:10 -0700287 // and add a new queue if needed.
Chong Zhang7ae4e2f2020-04-17 15:24:34 -0700288 if (uid != OFFLINE_UID) {
289 if (mJobQueues.count(uid) == 0) {
Chong Zhangacb33502020-04-20 11:04:48 -0700290 mUidPolicy->registerMonitorUid(uid);
Chong Zhang7ae4e2f2020-04-17 15:24:34 -0700291 if (mUidPolicy->isUidOnTop(uid)) {
292 mUidSortedList.push_front(uid);
Chong Zhang6d58e4b2020-03-31 09:41:10 -0700293 } else {
294 // Shouldn't be submitting real-time requests from non-top app,
295 // put it in front of the offline queue.
Chong Zhang7ae4e2f2020-04-17 15:24:34 -0700296 mUidSortedList.insert(mOfflineUidIterator, uid);
Chong Zhang6d58e4b2020-03-31 09:41:10 -0700297 }
Chong Zhang7ae4e2f2020-04-17 15:24:34 -0700298 } else if (uid != *mUidSortedList.begin()) {
299 if (mUidPolicy->isUidOnTop(uid)) {
300 mUidSortedList.remove(uid);
301 mUidSortedList.push_front(uid);
Chong Zhang6d58e4b2020-03-31 09:41:10 -0700302 }
303 }
304 }
Chong Zhang7ae4e2f2020-04-17 15:24:34 -0700305 // Append this job to the uid's queue.
306 mJobQueues[uid].push_back(jobKey);
Chong Zhang6d58e4b2020-03-31 09:41:10 -0700307
308 updateCurrentJob_l();
309
310 validateState_l();
311 return true;
312}
313
Chong Zhang3fa408f2020-04-30 11:04:28 -0700314bool TranscodingJobScheduler::cancel(ClientIdType clientId, JobIdType jobId) {
Chong Zhang6d58e4b2020-03-31 09:41:10 -0700315 JobKeyType jobKey = std::make_pair(clientId, jobId);
316
317 ALOGV("%s: job %s", __FUNCTION__, jobToString(jobKey).c_str());
318
Chong Zhang15c192a2020-05-05 16:24:00 -0700319 std::list<JobKeyType> jobsToRemove;
320
Chong Zhang6d58e4b2020-03-31 09:41:10 -0700321 std::scoped_lock lock{mLock};
322
Chong Zhang15c192a2020-05-05 16:24:00 -0700323 if (jobId < 0) {
324 for (auto it = mJobMap.begin(); it != mJobMap.end(); ++it) {
325 if (it->first.first == clientId && it->second.uid != OFFLINE_UID) {
326 jobsToRemove.push_back(it->first);
327 }
328 }
329 } else {
330 if (mJobMap.count(jobKey) == 0) {
331 ALOGE("job %s doesn't exist", jobToString(jobKey).c_str());
332 return false;
333 }
334 jobsToRemove.push_back(jobKey);
Chong Zhang6d58e4b2020-03-31 09:41:10 -0700335 }
336
Chong Zhang15c192a2020-05-05 16:24:00 -0700337 for (auto it = jobsToRemove.begin(); it != jobsToRemove.end(); ++it) {
Chong Zhang00feca22020-05-08 15:02:06 -0700338 // If the job has ever been started, stop it now.
339 // Note that stop() is needed even if the job is currently paused. This instructs
340 // the transcoder to discard any states for the job, otherwise the states may
341 // never be discarded.
342 if (mJobMap[*it].state != Job::NOT_STARTED) {
343 mTranscoder->stop(it->first, it->second);
Chong Zhang15c192a2020-05-05 16:24:00 -0700344 }
345
346 // Remove the job.
347 removeJob_l(*it);
348 }
Chong Zhang6d58e4b2020-03-31 09:41:10 -0700349
350 // Start next job.
351 updateCurrentJob_l();
352
353 validateState_l();
354 return true;
355}
356
Chong Zhang3fa408f2020-04-30 11:04:28 -0700357bool TranscodingJobScheduler::getJob(ClientIdType clientId, JobIdType jobId,
Chong Zhang6d58e4b2020-03-31 09:41:10 -0700358 TranscodingRequestParcel* request) {
359 JobKeyType jobKey = std::make_pair(clientId, jobId);
360
361 std::scoped_lock lock{mLock};
362
363 if (mJobMap.count(jobKey) == 0) {
364 ALOGE("job %s doesn't exist", jobToString(jobKey).c_str());
365 return false;
366 }
367
368 *(TranscodingRequest*)request = mJobMap[jobKey].request;
369 return true;
370}
371
Chong Zhangde60f062020-06-11 17:05:10 -0700372void TranscodingJobScheduler::notifyClient(ClientIdType clientId, JobIdType jobId,
373 const char* reason,
374 std::function<void(const JobKeyType&)> func) {
Chong Zhang6d58e4b2020-03-31 09:41:10 -0700375 JobKeyType jobKey = std::make_pair(clientId, jobId);
376
Chong Zhang6d58e4b2020-03-31 09:41:10 -0700377 std::scoped_lock lock{mLock};
378
379 if (mJobMap.count(jobKey) == 0) {
Chong Zhangde60f062020-06-11 17:05:10 -0700380 ALOGW("%s: ignoring %s for job %s that doesn't exist", __FUNCTION__, reason,
381 jobToString(jobKey).c_str());
Chong Zhang6d58e4b2020-03-31 09:41:10 -0700382 return;
383 }
384
385 // Only ignore if job was never started. In particular, propagate the status
386 // to client if the job is paused. Transcoder could have posted finish when
387 // we're pausing it, and the finish arrived after we changed current job.
388 if (mJobMap[jobKey].state == Job::NOT_STARTED) {
Chong Zhangde60f062020-06-11 17:05:10 -0700389 ALOGW("%s: ignoring %s for job %s that was never started", __FUNCTION__, reason,
390 jobToString(jobKey).c_str());
Chong Zhang6d58e4b2020-03-31 09:41:10 -0700391 return;
392 }
393
Chong Zhangde60f062020-06-11 17:05:10 -0700394 ALOGV("%s: job %s %s", __FUNCTION__, jobToString(jobKey).c_str(), reason);
395 func(jobKey);
396}
397
398void TranscodingJobScheduler::onStarted(ClientIdType clientId, JobIdType jobId) {
399 notifyClient(clientId, jobId, "started", [=](const JobKeyType& jobKey) {
400 auto callback = mJobMap[jobKey].callback.lock();
401 if (callback != nullptr) {
402 callback->onTranscodingStarted(jobId);
Chong Zhang6d58e4b2020-03-31 09:41:10 -0700403 }
Chong Zhangde60f062020-06-11 17:05:10 -0700404 });
405}
Chong Zhang6d58e4b2020-03-31 09:41:10 -0700406
Chong Zhangde60f062020-06-11 17:05:10 -0700407void TranscodingJobScheduler::onPaused(ClientIdType clientId, JobIdType jobId) {
408 notifyClient(clientId, jobId, "paused", [=](const JobKeyType& jobKey) {
409 auto callback = mJobMap[jobKey].callback.lock();
410 if (callback != nullptr) {
411 callback->onTranscodingPaused(jobId);
412 }
413 });
414}
Chong Zhang6d58e4b2020-03-31 09:41:10 -0700415
Chong Zhangde60f062020-06-11 17:05:10 -0700416void TranscodingJobScheduler::onResumed(ClientIdType clientId, JobIdType jobId) {
417 notifyClient(clientId, jobId, "resumed", [=](const JobKeyType& jobKey) {
418 auto callback = mJobMap[jobKey].callback.lock();
419 if (callback != nullptr) {
420 callback->onTranscodingResumed(jobId);
421 }
422 });
423}
Chong Zhang6d58e4b2020-03-31 09:41:10 -0700424
Chong Zhangde60f062020-06-11 17:05:10 -0700425void TranscodingJobScheduler::onFinish(ClientIdType clientId, JobIdType jobId) {
426 notifyClient(clientId, jobId, "finish", [=](const JobKeyType& jobKey) {
427 {
428 auto clientCallback = mJobMap[jobKey].callback.lock();
429 if (clientCallback != nullptr) {
Chong Zhang66469272020-06-04 16:51:55 -0700430 clientCallback->onTranscodingFinished(
hkuang0bd73742020-06-24 12:57:09 -0700431 jobId, TranscodingResultParcel({jobId, -1 /*actualBitrateBps*/,
432 std::nullopt /*jobStats*/}));
Chong Zhangde60f062020-06-11 17:05:10 -0700433 }
434 }
435
436 // Remove the job.
437 removeJob_l(jobKey);
438
439 // Start next job.
440 updateCurrentJob_l();
441
442 validateState_l();
443 });
Chong Zhang6d58e4b2020-03-31 09:41:10 -0700444}
445
Chong Zhang3fa408f2020-04-30 11:04:28 -0700446void TranscodingJobScheduler::onError(ClientIdType clientId, JobIdType jobId,
447 TranscodingErrorCode err) {
Chong Zhangde60f062020-06-11 17:05:10 -0700448 notifyClient(clientId, jobId, "error", [=](const JobKeyType& jobKey) {
449 {
450 auto clientCallback = mJobMap[jobKey].callback.lock();
451 if (clientCallback != nullptr) {
452 clientCallback->onTranscodingFailed(jobId, err);
453 }
Chong Zhang6d58e4b2020-03-31 09:41:10 -0700454 }
Chong Zhang6d58e4b2020-03-31 09:41:10 -0700455
Chong Zhangde60f062020-06-11 17:05:10 -0700456 // Remove the job.
457 removeJob_l(jobKey);
Chong Zhang6d58e4b2020-03-31 09:41:10 -0700458
Chong Zhangde60f062020-06-11 17:05:10 -0700459 // Start next job.
460 updateCurrentJob_l();
Chong Zhang6d58e4b2020-03-31 09:41:10 -0700461
Chong Zhangde60f062020-06-11 17:05:10 -0700462 validateState_l();
463 });
Chong Zhang6d58e4b2020-03-31 09:41:10 -0700464}
465
Chong Zhang3fa408f2020-04-30 11:04:28 -0700466void TranscodingJobScheduler::onProgressUpdate(ClientIdType clientId, JobIdType jobId,
467 int32_t progress) {
Chong Zhangde60f062020-06-11 17:05:10 -0700468 notifyClient(clientId, jobId, "progress", [=](const JobKeyType& jobKey) {
469 auto callback = mJobMap[jobKey].callback.lock();
470 if (callback != nullptr) {
471 callback->onProgressUpdate(jobId, progress);
Chong Zhangacb33502020-04-20 11:04:48 -0700472 }
Chong Zhang0579c6f2020-10-05 12:03:34 -0700473 mJobMap[jobKey].lastProgress = progress;
Chong Zhangde60f062020-06-11 17:05:10 -0700474 });
Chong Zhangacb33502020-04-20 11:04:48 -0700475}
476
Chong Zhang6d58e4b2020-03-31 09:41:10 -0700477void TranscodingJobScheduler::onResourceLost() {
Chong Zhangf9077512020-09-21 21:02:06 -0700478 ALOGI("%s", __FUNCTION__);
Chong Zhang6d58e4b2020-03-31 09:41:10 -0700479
480 std::scoped_lock lock{mLock};
481
Chong Zhangf9077512020-09-21 21:02:06 -0700482 if (mResourceLost) {
483 return;
484 }
485
Chong Zhang6d58e4b2020-03-31 09:41:10 -0700486 // If we receive a resource loss event, the TranscoderLibrary already paused
487 // the transcoding, so we don't need to call onPaused to notify it to pause.
488 // Only need to update the job state here.
489 if (mCurrentJob != nullptr && mCurrentJob->state == Job::RUNNING) {
490 mCurrentJob->state = Job::PAUSED;
Chong Zhangf9077512020-09-21 21:02:06 -0700491 // Notify the client as a paused event.
492 auto clientCallback = mCurrentJob->callback.lock();
493 if (clientCallback != nullptr) {
494 clientCallback->onTranscodingPaused(mCurrentJob->key.second);
495 }
Chong Zhang6d58e4b2020-03-31 09:41:10 -0700496 }
497 mResourceLost = true;
498
499 validateState_l();
500}
501
Chong Zhangacb33502020-04-20 11:04:48 -0700502void TranscodingJobScheduler::onTopUidsChanged(const std::unordered_set<uid_t>& uids) {
503 if (uids.empty()) {
504 ALOGW("%s: ignoring empty uids", __FUNCTION__);
505 return;
506 }
507
508 std::string uidStr;
509 for (auto it = uids.begin(); it != uids.end(); it++) {
510 if (!uidStr.empty()) {
511 uidStr += ", ";
512 }
513 uidStr += std::to_string(*it);
514 }
515
516 ALOGD("%s: topUids: size %zu, uids: %s", __FUNCTION__, uids.size(), uidStr.c_str());
Chong Zhang6d58e4b2020-03-31 09:41:10 -0700517
518 std::scoped_lock lock{mLock};
519
Chong Zhangacb33502020-04-20 11:04:48 -0700520 moveUidsToTop_l(uids, true /*preserveTopUid*/);
Chong Zhang6d58e4b2020-03-31 09:41:10 -0700521
522 updateCurrentJob_l();
523
524 validateState_l();
525}
526
527void TranscodingJobScheduler::onResourceAvailable() {
Chong Zhang6d58e4b2020-03-31 09:41:10 -0700528 std::scoped_lock lock{mLock};
529
Chong Zhangf9077512020-09-21 21:02:06 -0700530 if (!mResourceLost) {
531 return;
532 }
533
534 ALOGI("%s", __FUNCTION__);
535
Chong Zhang6d58e4b2020-03-31 09:41:10 -0700536 mResourceLost = false;
537 updateCurrentJob_l();
538
539 validateState_l();
540}
541
542void TranscodingJobScheduler::validateState_l() {
543#ifdef VALIDATE_STATE
Chong Zhang7ae4e2f2020-04-17 15:24:34 -0700544 LOG_ALWAYS_FATAL_IF(mJobQueues.count(OFFLINE_UID) != 1,
Chong Zhang6d58e4b2020-03-31 09:41:10 -0700545 "mJobQueues offline queue number is not 1");
Chong Zhang7ae4e2f2020-04-17 15:24:34 -0700546 LOG_ALWAYS_FATAL_IF(*mOfflineUidIterator != OFFLINE_UID,
547 "mOfflineUidIterator not pointing to offline uid");
548 LOG_ALWAYS_FATAL_IF(mUidSortedList.size() != mJobQueues.size(),
549 "mUidList and mJobQueues size mismatch");
Chong Zhang6d58e4b2020-03-31 09:41:10 -0700550
551 int32_t totalJobs = 0;
Chong Zhang0579c6f2020-10-05 12:03:34 -0700552 for (auto uid : mUidSortedList) {
553 LOG_ALWAYS_FATAL_IF(mJobQueues.count(uid) != 1, "mJobQueues count for uid %d is not 1",
554 uid);
555 for (auto& jobKey : mJobQueues[uid]) {
556 LOG_ALWAYS_FATAL_IF(mJobMap.count(jobKey) != 1, "mJobs count for job %s is not 1",
557 jobToString(jobKey).c_str());
Chong Zhang6d58e4b2020-03-31 09:41:10 -0700558 }
559
Chong Zhang0579c6f2020-10-05 12:03:34 -0700560 totalJobs += mJobQueues[uid].size();
Chong Zhang6d58e4b2020-03-31 09:41:10 -0700561 }
562 LOG_ALWAYS_FATAL_IF(mJobMap.size() != totalJobs,
Chong Zhang7ae4e2f2020-04-17 15:24:34 -0700563 "mJobs size doesn't match total jobs counted from uid queues");
Chong Zhang6d58e4b2020-03-31 09:41:10 -0700564#endif // VALIDATE_STATE
565}
566
567} // namespace android