1*ec779b8eSAndroid Build Coastguard Worker /*
2*ec779b8eSAndroid Build Coastguard Worker * Copyright (C) 2020 The Android Open Source Project
3*ec779b8eSAndroid Build Coastguard Worker *
4*ec779b8eSAndroid Build Coastguard Worker * Licensed under the Apache License, Version 2.0 (the "License");
5*ec779b8eSAndroid Build Coastguard Worker * you may not use this file except in compliance with the License.
6*ec779b8eSAndroid Build Coastguard Worker * You may obtain a copy of the License at
7*ec779b8eSAndroid Build Coastguard Worker *
8*ec779b8eSAndroid Build Coastguard Worker * http://www.apache.org/licenses/LICENSE-2.0
9*ec779b8eSAndroid Build Coastguard Worker *
10*ec779b8eSAndroid Build Coastguard Worker * Unless required by applicable law or agreed to in writing, software
11*ec779b8eSAndroid Build Coastguard Worker * distributed under the License is distributed on an "AS IS" BASIS,
12*ec779b8eSAndroid Build Coastguard Worker * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*ec779b8eSAndroid Build Coastguard Worker * See the License for the specific language governing permissions and
14*ec779b8eSAndroid Build Coastguard Worker * limitations under the License.
15*ec779b8eSAndroid Build Coastguard Worker */
16*ec779b8eSAndroid Build Coastguard Worker
17*ec779b8eSAndroid Build Coastguard Worker //#define LOG_NDEBUG 0
18*ec779b8eSAndroid Build Coastguard Worker #define LOG_TAG "TranscodingSessionController"
19*ec779b8eSAndroid Build Coastguard Worker
20*ec779b8eSAndroid Build Coastguard Worker #define VALIDATE_STATE 1
21*ec779b8eSAndroid Build Coastguard Worker
22*ec779b8eSAndroid Build Coastguard Worker #include <android/permission_manager.h>
23*ec779b8eSAndroid Build Coastguard Worker #include <inttypes.h>
24*ec779b8eSAndroid Build Coastguard Worker #include <media/TranscodingSessionController.h>
25*ec779b8eSAndroid Build Coastguard Worker #include <media/TranscodingUidPolicy.h>
26*ec779b8eSAndroid Build Coastguard Worker #include <utils/AndroidThreads.h>
27*ec779b8eSAndroid Build Coastguard Worker #include <utils/Log.h>
28*ec779b8eSAndroid Build Coastguard Worker
29*ec779b8eSAndroid Build Coastguard Worker #include <thread>
30*ec779b8eSAndroid Build Coastguard Worker #include <utility>
31*ec779b8eSAndroid Build Coastguard Worker
32*ec779b8eSAndroid Build Coastguard Worker namespace android {
33*ec779b8eSAndroid Build Coastguard Worker
34*ec779b8eSAndroid Build Coastguard Worker static_assert((SessionIdType)-1 < 0, "SessionIdType should be signed");
35*ec779b8eSAndroid Build Coastguard Worker
36*ec779b8eSAndroid Build Coastguard Worker constexpr static uid_t OFFLINE_UID = -1;
37*ec779b8eSAndroid Build Coastguard Worker constexpr static size_t kSessionHistoryMax = 100;
38*ec779b8eSAndroid Build Coastguard Worker
39*ec779b8eSAndroid Build Coastguard Worker //static
sessionToString(const SessionKeyType & sessionKey)40*ec779b8eSAndroid Build Coastguard Worker String8 TranscodingSessionController::sessionToString(const SessionKeyType& sessionKey) {
41*ec779b8eSAndroid Build Coastguard Worker return String8::format("{client:%lld, session:%d}", (long long)sessionKey.first,
42*ec779b8eSAndroid Build Coastguard Worker sessionKey.second);
43*ec779b8eSAndroid Build Coastguard Worker }
44*ec779b8eSAndroid Build Coastguard Worker
45*ec779b8eSAndroid Build Coastguard Worker //static
sessionStateToString(const Session::State sessionState)46*ec779b8eSAndroid Build Coastguard Worker const char* TranscodingSessionController::sessionStateToString(const Session::State sessionState) {
47*ec779b8eSAndroid Build Coastguard Worker switch (sessionState) {
48*ec779b8eSAndroid Build Coastguard Worker case Session::State::NOT_STARTED:
49*ec779b8eSAndroid Build Coastguard Worker return "NOT_STARTED";
50*ec779b8eSAndroid Build Coastguard Worker case Session::State::RUNNING:
51*ec779b8eSAndroid Build Coastguard Worker return "RUNNING";
52*ec779b8eSAndroid Build Coastguard Worker case Session::State::PAUSED:
53*ec779b8eSAndroid Build Coastguard Worker return "PAUSED";
54*ec779b8eSAndroid Build Coastguard Worker case Session::State::FINISHED:
55*ec779b8eSAndroid Build Coastguard Worker return "FINISHED";
56*ec779b8eSAndroid Build Coastguard Worker case Session::State::CANCELED:
57*ec779b8eSAndroid Build Coastguard Worker return "CANCELED";
58*ec779b8eSAndroid Build Coastguard Worker case Session::State::ERROR:
59*ec779b8eSAndroid Build Coastguard Worker return "ERROR";
60*ec779b8eSAndroid Build Coastguard Worker default:
61*ec779b8eSAndroid Build Coastguard Worker break;
62*ec779b8eSAndroid Build Coastguard Worker }
63*ec779b8eSAndroid Build Coastguard Worker return "(unknown)";
64*ec779b8eSAndroid Build Coastguard Worker }
65*ec779b8eSAndroid Build Coastguard Worker
66*ec779b8eSAndroid Build Coastguard Worker ///////////////////////////////////////////////////////////////////////////////
67*ec779b8eSAndroid Build Coastguard Worker struct TranscodingSessionController::Watchdog {
68*ec779b8eSAndroid Build Coastguard Worker Watchdog(TranscodingSessionController* owner, int64_t timeoutUs);
69*ec779b8eSAndroid Build Coastguard Worker ~Watchdog();
70*ec779b8eSAndroid Build Coastguard Worker
71*ec779b8eSAndroid Build Coastguard Worker // Starts monitoring the session.
72*ec779b8eSAndroid Build Coastguard Worker void start(const SessionKeyType& key);
73*ec779b8eSAndroid Build Coastguard Worker // Stops monitoring the session.
74*ec779b8eSAndroid Build Coastguard Worker void stop();
75*ec779b8eSAndroid Build Coastguard Worker // Signals that the session is still alive. Must be sent at least every mTimeoutUs.
76*ec779b8eSAndroid Build Coastguard Worker // (Timeout will happen if no ping in mTimeoutUs since the last ping.)
77*ec779b8eSAndroid Build Coastguard Worker void keepAlive();
78*ec779b8eSAndroid Build Coastguard Worker
79*ec779b8eSAndroid Build Coastguard Worker private:
80*ec779b8eSAndroid Build Coastguard Worker void threadLoop();
81*ec779b8eSAndroid Build Coastguard Worker void updateTimer_l();
82*ec779b8eSAndroid Build Coastguard Worker
83*ec779b8eSAndroid Build Coastguard Worker TranscodingSessionController* mOwner;
84*ec779b8eSAndroid Build Coastguard Worker const int64_t mTimeoutUs;
85*ec779b8eSAndroid Build Coastguard Worker mutable std::mutex mLock;
86*ec779b8eSAndroid Build Coastguard Worker std::condition_variable mCondition GUARDED_BY(mLock);
87*ec779b8eSAndroid Build Coastguard Worker // Whether watchdog is monitoring a session for timeout.
88*ec779b8eSAndroid Build Coastguard Worker bool mActive GUARDED_BY(mLock);
89*ec779b8eSAndroid Build Coastguard Worker // Whether watchdog is aborted and the monitoring thread should exit.
90*ec779b8eSAndroid Build Coastguard Worker bool mAbort GUARDED_BY(mLock);
91*ec779b8eSAndroid Build Coastguard Worker // When watchdog is active, the next timeout time point.
92*ec779b8eSAndroid Build Coastguard Worker std::chrono::steady_clock::time_point mNextTimeoutTime GUARDED_BY(mLock);
93*ec779b8eSAndroid Build Coastguard Worker // When watchdog is active, the session being watched.
94*ec779b8eSAndroid Build Coastguard Worker SessionKeyType mSessionToWatch GUARDED_BY(mLock);
95*ec779b8eSAndroid Build Coastguard Worker std::thread mThread;
96*ec779b8eSAndroid Build Coastguard Worker };
97*ec779b8eSAndroid Build Coastguard Worker
Watchdog(TranscodingSessionController * owner,int64_t timeoutUs)98*ec779b8eSAndroid Build Coastguard Worker TranscodingSessionController::Watchdog::Watchdog(TranscodingSessionController* owner,
99*ec779b8eSAndroid Build Coastguard Worker int64_t timeoutUs)
100*ec779b8eSAndroid Build Coastguard Worker : mOwner(owner),
101*ec779b8eSAndroid Build Coastguard Worker mTimeoutUs(timeoutUs),
102*ec779b8eSAndroid Build Coastguard Worker mActive(false),
103*ec779b8eSAndroid Build Coastguard Worker mAbort(false),
104*ec779b8eSAndroid Build Coastguard Worker mThread(&Watchdog::threadLoop, this) {
105*ec779b8eSAndroid Build Coastguard Worker ALOGV("Watchdog CTOR: %p", this);
106*ec779b8eSAndroid Build Coastguard Worker }
107*ec779b8eSAndroid Build Coastguard Worker
~Watchdog()108*ec779b8eSAndroid Build Coastguard Worker TranscodingSessionController::Watchdog::~Watchdog() {
109*ec779b8eSAndroid Build Coastguard Worker ALOGV("Watchdog DTOR: %p", this);
110*ec779b8eSAndroid Build Coastguard Worker
111*ec779b8eSAndroid Build Coastguard Worker {
112*ec779b8eSAndroid Build Coastguard Worker // Exit the looper thread.
113*ec779b8eSAndroid Build Coastguard Worker std::scoped_lock lock{mLock};
114*ec779b8eSAndroid Build Coastguard Worker
115*ec779b8eSAndroid Build Coastguard Worker mAbort = true;
116*ec779b8eSAndroid Build Coastguard Worker mCondition.notify_one();
117*ec779b8eSAndroid Build Coastguard Worker }
118*ec779b8eSAndroid Build Coastguard Worker
119*ec779b8eSAndroid Build Coastguard Worker mThread.join();
120*ec779b8eSAndroid Build Coastguard Worker ALOGV("Watchdog DTOR: %p, done.", this);
121*ec779b8eSAndroid Build Coastguard Worker }
122*ec779b8eSAndroid Build Coastguard Worker
start(const SessionKeyType & key)123*ec779b8eSAndroid Build Coastguard Worker void TranscodingSessionController::Watchdog::start(const SessionKeyType& key) {
124*ec779b8eSAndroid Build Coastguard Worker std::scoped_lock lock{mLock};
125*ec779b8eSAndroid Build Coastguard Worker
126*ec779b8eSAndroid Build Coastguard Worker if (!mActive) {
127*ec779b8eSAndroid Build Coastguard Worker ALOGI("Watchdog start: %s", sessionToString(key).c_str());
128*ec779b8eSAndroid Build Coastguard Worker
129*ec779b8eSAndroid Build Coastguard Worker mActive = true;
130*ec779b8eSAndroid Build Coastguard Worker mSessionToWatch = key;
131*ec779b8eSAndroid Build Coastguard Worker updateTimer_l();
132*ec779b8eSAndroid Build Coastguard Worker mCondition.notify_one();
133*ec779b8eSAndroid Build Coastguard Worker }
134*ec779b8eSAndroid Build Coastguard Worker }
135*ec779b8eSAndroid Build Coastguard Worker
stop()136*ec779b8eSAndroid Build Coastguard Worker void TranscodingSessionController::Watchdog::stop() {
137*ec779b8eSAndroid Build Coastguard Worker std::scoped_lock lock{mLock};
138*ec779b8eSAndroid Build Coastguard Worker
139*ec779b8eSAndroid Build Coastguard Worker if (mActive) {
140*ec779b8eSAndroid Build Coastguard Worker ALOGI("Watchdog stop: %s", sessionToString(mSessionToWatch).c_str());
141*ec779b8eSAndroid Build Coastguard Worker
142*ec779b8eSAndroid Build Coastguard Worker mActive = false;
143*ec779b8eSAndroid Build Coastguard Worker mCondition.notify_one();
144*ec779b8eSAndroid Build Coastguard Worker }
145*ec779b8eSAndroid Build Coastguard Worker }
146*ec779b8eSAndroid Build Coastguard Worker
keepAlive()147*ec779b8eSAndroid Build Coastguard Worker void TranscodingSessionController::Watchdog::keepAlive() {
148*ec779b8eSAndroid Build Coastguard Worker std::scoped_lock lock{mLock};
149*ec779b8eSAndroid Build Coastguard Worker
150*ec779b8eSAndroid Build Coastguard Worker if (mActive) {
151*ec779b8eSAndroid Build Coastguard Worker ALOGI("Watchdog keepAlive: %s", sessionToString(mSessionToWatch).c_str());
152*ec779b8eSAndroid Build Coastguard Worker
153*ec779b8eSAndroid Build Coastguard Worker updateTimer_l();
154*ec779b8eSAndroid Build Coastguard Worker mCondition.notify_one();
155*ec779b8eSAndroid Build Coastguard Worker }
156*ec779b8eSAndroid Build Coastguard Worker }
157*ec779b8eSAndroid Build Coastguard Worker
158*ec779b8eSAndroid Build Coastguard Worker // updateTimer_l() is only called with lock held.
updateTimer_l()159*ec779b8eSAndroid Build Coastguard Worker void TranscodingSessionController::Watchdog::updateTimer_l() NO_THREAD_SAFETY_ANALYSIS {
160*ec779b8eSAndroid Build Coastguard Worker std::chrono::microseconds timeout(mTimeoutUs);
161*ec779b8eSAndroid Build Coastguard Worker mNextTimeoutTime = std::chrono::steady_clock::now() + timeout;
162*ec779b8eSAndroid Build Coastguard Worker }
163*ec779b8eSAndroid Build Coastguard Worker
164*ec779b8eSAndroid Build Coastguard Worker // Unfortunately std::unique_lock is incompatible with -Wthread-safety.
threadLoop()165*ec779b8eSAndroid Build Coastguard Worker void TranscodingSessionController::Watchdog::threadLoop() NO_THREAD_SAFETY_ANALYSIS {
166*ec779b8eSAndroid Build Coastguard Worker androidSetThreadPriority(0 /*tid (0 = current) */, ANDROID_PRIORITY_BACKGROUND);
167*ec779b8eSAndroid Build Coastguard Worker std::unique_lock<std::mutex> lock{mLock};
168*ec779b8eSAndroid Build Coastguard Worker
169*ec779b8eSAndroid Build Coastguard Worker while (!mAbort) {
170*ec779b8eSAndroid Build Coastguard Worker if (!mActive) {
171*ec779b8eSAndroid Build Coastguard Worker mCondition.wait(lock);
172*ec779b8eSAndroid Build Coastguard Worker continue;
173*ec779b8eSAndroid Build Coastguard Worker }
174*ec779b8eSAndroid Build Coastguard Worker // Watchdog active, wait till next timeout time.
175*ec779b8eSAndroid Build Coastguard Worker if (mCondition.wait_until(lock, mNextTimeoutTime) == std::cv_status::timeout) {
176*ec779b8eSAndroid Build Coastguard Worker // If timeout happens, report timeout and deactivate watchdog.
177*ec779b8eSAndroid Build Coastguard Worker mActive = false;
178*ec779b8eSAndroid Build Coastguard Worker // Make a copy of session key, as once we unlock, it could be unprotected.
179*ec779b8eSAndroid Build Coastguard Worker SessionKeyType sessionKey = mSessionToWatch;
180*ec779b8eSAndroid Build Coastguard Worker
181*ec779b8eSAndroid Build Coastguard Worker ALOGE("Watchdog timeout: %s", sessionToString(sessionKey).c_str());
182*ec779b8eSAndroid Build Coastguard Worker
183*ec779b8eSAndroid Build Coastguard Worker lock.unlock();
184*ec779b8eSAndroid Build Coastguard Worker mOwner->onError(sessionKey.first, sessionKey.second,
185*ec779b8eSAndroid Build Coastguard Worker TranscodingErrorCode::kWatchdogTimeout);
186*ec779b8eSAndroid Build Coastguard Worker lock.lock();
187*ec779b8eSAndroid Build Coastguard Worker }
188*ec779b8eSAndroid Build Coastguard Worker }
189*ec779b8eSAndroid Build Coastguard Worker }
190*ec779b8eSAndroid Build Coastguard Worker ///////////////////////////////////////////////////////////////////////////////
191*ec779b8eSAndroid Build Coastguard Worker struct TranscodingSessionController::Pacer {
Pacerandroid::TranscodingSessionController::Pacer192*ec779b8eSAndroid Build Coastguard Worker Pacer(const ControllerConfig& config)
193*ec779b8eSAndroid Build Coastguard Worker : mBurstThresholdMs(config.pacerBurstThresholdMs),
194*ec779b8eSAndroid Build Coastguard Worker mBurstCountQuota(config.pacerBurstCountQuota),
195*ec779b8eSAndroid Build Coastguard Worker mBurstTimeQuotaSec(config.pacerBurstTimeQuotaSeconds) {}
196*ec779b8eSAndroid Build Coastguard Worker
197*ec779b8eSAndroid Build Coastguard Worker ~Pacer() = default;
198*ec779b8eSAndroid Build Coastguard Worker
199*ec779b8eSAndroid Build Coastguard Worker bool onSessionStarted(uid_t uid, uid_t callingUid);
200*ec779b8eSAndroid Build Coastguard Worker void onSessionCompleted(uid_t uid, std::chrono::microseconds runningTime);
201*ec779b8eSAndroid Build Coastguard Worker void onSessionCancelled(uid_t uid);
202*ec779b8eSAndroid Build Coastguard Worker
203*ec779b8eSAndroid Build Coastguard Worker private:
204*ec779b8eSAndroid Build Coastguard Worker // Threshold of time between finish/start below which a back-to-back start is counted.
205*ec779b8eSAndroid Build Coastguard Worker int32_t mBurstThresholdMs;
206*ec779b8eSAndroid Build Coastguard Worker // Maximum allowed back-to-back start count.
207*ec779b8eSAndroid Build Coastguard Worker int32_t mBurstCountQuota;
208*ec779b8eSAndroid Build Coastguard Worker // Maximum allowed back-to-back running time.
209*ec779b8eSAndroid Build Coastguard Worker int32_t mBurstTimeQuotaSec;
210*ec779b8eSAndroid Build Coastguard Worker
211*ec779b8eSAndroid Build Coastguard Worker struct UidHistoryEntry {
212*ec779b8eSAndroid Build Coastguard Worker bool sessionActive = false;
213*ec779b8eSAndroid Build Coastguard Worker int32_t burstCount = 0;
214*ec779b8eSAndroid Build Coastguard Worker std::chrono::steady_clock::duration burstDuration{0};
215*ec779b8eSAndroid Build Coastguard Worker std::chrono::steady_clock::time_point lastCompletedTime;
216*ec779b8eSAndroid Build Coastguard Worker };
217*ec779b8eSAndroid Build Coastguard Worker std::map<uid_t, UidHistoryEntry> mUidHistoryMap;
218*ec779b8eSAndroid Build Coastguard Worker std::unordered_set<uid_t> mMtpUids;
219*ec779b8eSAndroid Build Coastguard Worker std::unordered_set<uid_t> mNonMtpUids;
220*ec779b8eSAndroid Build Coastguard Worker
221*ec779b8eSAndroid Build Coastguard Worker bool isSubjectToQuota(uid_t uid, uid_t callingUid);
222*ec779b8eSAndroid Build Coastguard Worker };
223*ec779b8eSAndroid Build Coastguard Worker
isSubjectToQuota(uid_t uid,uid_t callingUid)224*ec779b8eSAndroid Build Coastguard Worker bool TranscodingSessionController::Pacer::isSubjectToQuota(uid_t uid, uid_t callingUid) {
225*ec779b8eSAndroid Build Coastguard Worker // Submitting with self uid is not limited (which can only happen if it's used as an
226*ec779b8eSAndroid Build Coastguard Worker // app-facing API). MediaProvider usage always submit on behalf of other uids.
227*ec779b8eSAndroid Build Coastguard Worker if (uid == callingUid) {
228*ec779b8eSAndroid Build Coastguard Worker return false;
229*ec779b8eSAndroid Build Coastguard Worker }
230*ec779b8eSAndroid Build Coastguard Worker
231*ec779b8eSAndroid Build Coastguard Worker if (mMtpUids.find(uid) != mMtpUids.end()) {
232*ec779b8eSAndroid Build Coastguard Worker return false;
233*ec779b8eSAndroid Build Coastguard Worker }
234*ec779b8eSAndroid Build Coastguard Worker
235*ec779b8eSAndroid Build Coastguard Worker if (mNonMtpUids.find(uid) != mNonMtpUids.end()) {
236*ec779b8eSAndroid Build Coastguard Worker return true;
237*ec779b8eSAndroid Build Coastguard Worker }
238*ec779b8eSAndroid Build Coastguard Worker
239*ec779b8eSAndroid Build Coastguard Worker // We don't have MTP permission info about this uid yet, check permission and save the result.
240*ec779b8eSAndroid Build Coastguard Worker int32_t result;
241*ec779b8eSAndroid Build Coastguard Worker if (__builtin_available(android __TRANSCODING_MIN_API__, *)) {
242*ec779b8eSAndroid Build Coastguard Worker if (APermissionManager_checkPermission("android.permission.ACCESS_MTP", -1 /*pid*/, uid,
243*ec779b8eSAndroid Build Coastguard Worker &result) == PERMISSION_MANAGER_STATUS_OK &&
244*ec779b8eSAndroid Build Coastguard Worker result == PERMISSION_MANAGER_PERMISSION_GRANTED) {
245*ec779b8eSAndroid Build Coastguard Worker mMtpUids.insert(uid);
246*ec779b8eSAndroid Build Coastguard Worker return false;
247*ec779b8eSAndroid Build Coastguard Worker }
248*ec779b8eSAndroid Build Coastguard Worker }
249*ec779b8eSAndroid Build Coastguard Worker
250*ec779b8eSAndroid Build Coastguard Worker mNonMtpUids.insert(uid);
251*ec779b8eSAndroid Build Coastguard Worker return true;
252*ec779b8eSAndroid Build Coastguard Worker }
253*ec779b8eSAndroid Build Coastguard Worker
onSessionStarted(uid_t uid,uid_t callingUid)254*ec779b8eSAndroid Build Coastguard Worker bool TranscodingSessionController::Pacer::onSessionStarted(uid_t uid, uid_t callingUid) {
255*ec779b8eSAndroid Build Coastguard Worker if (!isSubjectToQuota(uid, callingUid)) {
256*ec779b8eSAndroid Build Coastguard Worker ALOGI("Pacer::onSessionStarted: uid %d (caling uid: %d): not subject to quota", uid,
257*ec779b8eSAndroid Build Coastguard Worker callingUid);
258*ec779b8eSAndroid Build Coastguard Worker return true;
259*ec779b8eSAndroid Build Coastguard Worker }
260*ec779b8eSAndroid Build Coastguard Worker
261*ec779b8eSAndroid Build Coastguard Worker // If uid doesn't exist, only insert the entry and mark session active. Skip quota checking.
262*ec779b8eSAndroid Build Coastguard Worker if (mUidHistoryMap.find(uid) == mUidHistoryMap.end()) {
263*ec779b8eSAndroid Build Coastguard Worker mUidHistoryMap.emplace(uid, UidHistoryEntry{});
264*ec779b8eSAndroid Build Coastguard Worker mUidHistoryMap[uid].sessionActive = true;
265*ec779b8eSAndroid Build Coastguard Worker ALOGV("Pacer::onSessionStarted: uid %d: new", uid);
266*ec779b8eSAndroid Build Coastguard Worker return true;
267*ec779b8eSAndroid Build Coastguard Worker }
268*ec779b8eSAndroid Build Coastguard Worker
269*ec779b8eSAndroid Build Coastguard Worker // TODO: if Thermal throttling or resoure lost happened to occurr between this start
270*ec779b8eSAndroid Build Coastguard Worker // and the previous completion, we should deduct the paused time from the elapsed time.
271*ec779b8eSAndroid Build Coastguard Worker // (Individual session's pause time, on the other hand, doesn't need to be deducted
272*ec779b8eSAndroid Build Coastguard Worker // because it doesn't affect the gap between last completion and the start.
273*ec779b8eSAndroid Build Coastguard Worker auto timeSinceLastComplete =
274*ec779b8eSAndroid Build Coastguard Worker std::chrono::steady_clock::now() - mUidHistoryMap[uid].lastCompletedTime;
275*ec779b8eSAndroid Build Coastguard Worker if (mUidHistoryMap[uid].burstCount >= mBurstCountQuota &&
276*ec779b8eSAndroid Build Coastguard Worker mUidHistoryMap[uid].burstDuration >= std::chrono::seconds(mBurstTimeQuotaSec)) {
277*ec779b8eSAndroid Build Coastguard Worker ALOGW("Pacer::onSessionStarted: uid %d: over quota, burst count %d, time %lldms", uid,
278*ec779b8eSAndroid Build Coastguard Worker mUidHistoryMap[uid].burstCount,
279*ec779b8eSAndroid Build Coastguard Worker (long long)mUidHistoryMap[uid].burstDuration.count() / 1000000);
280*ec779b8eSAndroid Build Coastguard Worker return false;
281*ec779b8eSAndroid Build Coastguard Worker }
282*ec779b8eSAndroid Build Coastguard Worker
283*ec779b8eSAndroid Build Coastguard Worker // If not over quota, allow the session, and reset as long as this is not too close
284*ec779b8eSAndroid Build Coastguard Worker // to previous completion.
285*ec779b8eSAndroid Build Coastguard Worker if (timeSinceLastComplete > std::chrono::milliseconds(mBurstThresholdMs)) {
286*ec779b8eSAndroid Build Coastguard Worker ALOGV("Pacer::onSessionStarted: uid %d: reset quota", uid);
287*ec779b8eSAndroid Build Coastguard Worker mUidHistoryMap[uid].burstCount = 0;
288*ec779b8eSAndroid Build Coastguard Worker mUidHistoryMap[uid].burstDuration = std::chrono::milliseconds(0);
289*ec779b8eSAndroid Build Coastguard Worker } else {
290*ec779b8eSAndroid Build Coastguard Worker ALOGV("Pacer::onSessionStarted: uid %d: burst count %d, time %lldms", uid,
291*ec779b8eSAndroid Build Coastguard Worker mUidHistoryMap[uid].burstCount,
292*ec779b8eSAndroid Build Coastguard Worker (long long)mUidHistoryMap[uid].burstDuration.count() / 1000000);
293*ec779b8eSAndroid Build Coastguard Worker }
294*ec779b8eSAndroid Build Coastguard Worker
295*ec779b8eSAndroid Build Coastguard Worker mUidHistoryMap[uid].sessionActive = true;
296*ec779b8eSAndroid Build Coastguard Worker return true;
297*ec779b8eSAndroid Build Coastguard Worker }
298*ec779b8eSAndroid Build Coastguard Worker
onSessionCompleted(uid_t uid,std::chrono::microseconds runningTime)299*ec779b8eSAndroid Build Coastguard Worker void TranscodingSessionController::Pacer::onSessionCompleted(
300*ec779b8eSAndroid Build Coastguard Worker uid_t uid, std::chrono::microseconds runningTime) {
301*ec779b8eSAndroid Build Coastguard Worker // Skip quota update if this uid missed the start. (Could happen if the uid is added via
302*ec779b8eSAndroid Build Coastguard Worker // addClientUid() after the session start.)
303*ec779b8eSAndroid Build Coastguard Worker if (mUidHistoryMap.find(uid) == mUidHistoryMap.end() || !mUidHistoryMap[uid].sessionActive) {
304*ec779b8eSAndroid Build Coastguard Worker ALOGV("Pacer::onSessionCompleted: uid %d: not started", uid);
305*ec779b8eSAndroid Build Coastguard Worker return;
306*ec779b8eSAndroid Build Coastguard Worker }
307*ec779b8eSAndroid Build Coastguard Worker ALOGV("Pacer::onSessionCompleted: uid %d: runningTime %lld", uid, runningTime.count() / 1000);
308*ec779b8eSAndroid Build Coastguard Worker mUidHistoryMap[uid].sessionActive = false;
309*ec779b8eSAndroid Build Coastguard Worker mUidHistoryMap[uid].burstCount++;
310*ec779b8eSAndroid Build Coastguard Worker mUidHistoryMap[uid].burstDuration += runningTime;
311*ec779b8eSAndroid Build Coastguard Worker mUidHistoryMap[uid].lastCompletedTime = std::chrono::steady_clock::now();
312*ec779b8eSAndroid Build Coastguard Worker }
313*ec779b8eSAndroid Build Coastguard Worker
onSessionCancelled(uid_t uid)314*ec779b8eSAndroid Build Coastguard Worker void TranscodingSessionController::Pacer::onSessionCancelled(uid_t uid) {
315*ec779b8eSAndroid Build Coastguard Worker if (mUidHistoryMap.find(uid) == mUidHistoryMap.end()) {
316*ec779b8eSAndroid Build Coastguard Worker ALOGV("Pacer::onSessionCancelled: uid %d: not present", uid);
317*ec779b8eSAndroid Build Coastguard Worker return;
318*ec779b8eSAndroid Build Coastguard Worker }
319*ec779b8eSAndroid Build Coastguard Worker // This is only called if a uid is removed from a session (due to it being killed
320*ec779b8eSAndroid Build Coastguard Worker // or the original submitting client was gone but session was kept for offline use).
321*ec779b8eSAndroid Build Coastguard Worker // Since the uid is going to miss the onSessionCompleted(), we can't track this
322*ec779b8eSAndroid Build Coastguard Worker // session, and have to check back at next onSessionStarted().
323*ec779b8eSAndroid Build Coastguard Worker mUidHistoryMap[uid].sessionActive = false;
324*ec779b8eSAndroid Build Coastguard Worker }
325*ec779b8eSAndroid Build Coastguard Worker
326*ec779b8eSAndroid Build Coastguard Worker ///////////////////////////////////////////////////////////////////////////////
327*ec779b8eSAndroid Build Coastguard Worker
TranscodingSessionController(const TranscoderFactoryType & transcoderFactory,const std::shared_ptr<UidPolicyInterface> & uidPolicy,const std::shared_ptr<ResourcePolicyInterface> & resourcePolicy,const std::shared_ptr<ThermalPolicyInterface> & thermalPolicy,const ControllerConfig * config)328*ec779b8eSAndroid Build Coastguard Worker TranscodingSessionController::TranscodingSessionController(
329*ec779b8eSAndroid Build Coastguard Worker const TranscoderFactoryType& transcoderFactory,
330*ec779b8eSAndroid Build Coastguard Worker const std::shared_ptr<UidPolicyInterface>& uidPolicy,
331*ec779b8eSAndroid Build Coastguard Worker const std::shared_ptr<ResourcePolicyInterface>& resourcePolicy,
332*ec779b8eSAndroid Build Coastguard Worker const std::shared_ptr<ThermalPolicyInterface>& thermalPolicy,
333*ec779b8eSAndroid Build Coastguard Worker const ControllerConfig* config)
334*ec779b8eSAndroid Build Coastguard Worker : mTranscoderFactory(transcoderFactory),
335*ec779b8eSAndroid Build Coastguard Worker mUidPolicy(uidPolicy),
336*ec779b8eSAndroid Build Coastguard Worker mResourcePolicy(resourcePolicy),
337*ec779b8eSAndroid Build Coastguard Worker mThermalPolicy(thermalPolicy),
338*ec779b8eSAndroid Build Coastguard Worker mCurrentSession(nullptr),
339*ec779b8eSAndroid Build Coastguard Worker mResourceLost(false) {
340*ec779b8eSAndroid Build Coastguard Worker // Only push empty offline queue initially. Realtime queues are added when requests come in.
341*ec779b8eSAndroid Build Coastguard Worker mUidSortedList.push_back(OFFLINE_UID);
342*ec779b8eSAndroid Build Coastguard Worker mOfflineUidIterator = mUidSortedList.begin();
343*ec779b8eSAndroid Build Coastguard Worker mSessionQueues.emplace(OFFLINE_UID, SessionQueueType());
344*ec779b8eSAndroid Build Coastguard Worker mUidPackageNames[OFFLINE_UID] = "(offline)";
345*ec779b8eSAndroid Build Coastguard Worker mThermalThrottling = thermalPolicy->getThrottlingStatus();
346*ec779b8eSAndroid Build Coastguard Worker if (config != nullptr) {
347*ec779b8eSAndroid Build Coastguard Worker mConfig = *config;
348*ec779b8eSAndroid Build Coastguard Worker }
349*ec779b8eSAndroid Build Coastguard Worker mPacer.reset(new Pacer(mConfig));
350*ec779b8eSAndroid Build Coastguard Worker ALOGD("@@@ watchdog %lld, burst count %d, burst time %d, burst threshold %d",
351*ec779b8eSAndroid Build Coastguard Worker (long long)mConfig.watchdogTimeoutUs, mConfig.pacerBurstCountQuota,
352*ec779b8eSAndroid Build Coastguard Worker mConfig.pacerBurstTimeQuotaSeconds, mConfig.pacerBurstThresholdMs);
353*ec779b8eSAndroid Build Coastguard Worker }
354*ec779b8eSAndroid Build Coastguard Worker
~TranscodingSessionController()355*ec779b8eSAndroid Build Coastguard Worker TranscodingSessionController::~TranscodingSessionController() {}
356*ec779b8eSAndroid Build Coastguard Worker
dumpSession_l(const Session & session,String8 & result,bool closedSession)357*ec779b8eSAndroid Build Coastguard Worker void TranscodingSessionController::dumpSession_l(const Session& session, String8& result,
358*ec779b8eSAndroid Build Coastguard Worker bool closedSession) {
359*ec779b8eSAndroid Build Coastguard Worker const size_t SIZE = 256;
360*ec779b8eSAndroid Build Coastguard Worker char buffer[SIZE];
361*ec779b8eSAndroid Build Coastguard Worker const TranscodingRequestParcel& request = session.request;
362*ec779b8eSAndroid Build Coastguard Worker snprintf(buffer, SIZE, " Session: %s, %s, %d%%\n", sessionToString(session.key).c_str(),
363*ec779b8eSAndroid Build Coastguard Worker sessionStateToString(session.getState()), session.lastProgress);
364*ec779b8eSAndroid Build Coastguard Worker result.append(buffer);
365*ec779b8eSAndroid Build Coastguard Worker snprintf(buffer, SIZE, " pkg: %s\n", request.clientPackageName.c_str());
366*ec779b8eSAndroid Build Coastguard Worker result.append(buffer);
367*ec779b8eSAndroid Build Coastguard Worker snprintf(buffer, SIZE, " src: %s\n", request.sourceFilePath.c_str());
368*ec779b8eSAndroid Build Coastguard Worker result.append(buffer);
369*ec779b8eSAndroid Build Coastguard Worker snprintf(buffer, SIZE, " dst: %s\n", request.destinationFilePath.c_str());
370*ec779b8eSAndroid Build Coastguard Worker result.append(buffer);
371*ec779b8eSAndroid Build Coastguard Worker
372*ec779b8eSAndroid Build Coastguard Worker if (closedSession) {
373*ec779b8eSAndroid Build Coastguard Worker snprintf(buffer, SIZE,
374*ec779b8eSAndroid Build Coastguard Worker " waiting: %.1fs, running: %.1fs, paused: %.1fs, paused count: %d\n",
375*ec779b8eSAndroid Build Coastguard Worker session.waitingTime.count() / 1000000.0f, session.runningTime.count() / 1000000.0f,
376*ec779b8eSAndroid Build Coastguard Worker session.pausedTime.count() / 1000000.0f, session.pauseCount);
377*ec779b8eSAndroid Build Coastguard Worker result.append(buffer);
378*ec779b8eSAndroid Build Coastguard Worker }
379*ec779b8eSAndroid Build Coastguard Worker }
380*ec779b8eSAndroid Build Coastguard Worker
dumpAllSessions(int fd,const Vector<String16> & args __unused)381*ec779b8eSAndroid Build Coastguard Worker void TranscodingSessionController::dumpAllSessions(int fd, const Vector<String16>& args __unused) {
382*ec779b8eSAndroid Build Coastguard Worker String8 result;
383*ec779b8eSAndroid Build Coastguard Worker
384*ec779b8eSAndroid Build Coastguard Worker const size_t SIZE = 256;
385*ec779b8eSAndroid Build Coastguard Worker char buffer[SIZE];
386*ec779b8eSAndroid Build Coastguard Worker std::scoped_lock lock{mLock};
387*ec779b8eSAndroid Build Coastguard Worker
388*ec779b8eSAndroid Build Coastguard Worker snprintf(buffer, SIZE, "\n========== Dumping live sessions queues =========\n");
389*ec779b8eSAndroid Build Coastguard Worker result.append(buffer);
390*ec779b8eSAndroid Build Coastguard Worker snprintf(buffer, SIZE, " Total num of Sessions: %zu\n", mSessionMap.size());
391*ec779b8eSAndroid Build Coastguard Worker result.append(buffer);
392*ec779b8eSAndroid Build Coastguard Worker
393*ec779b8eSAndroid Build Coastguard Worker std::vector<int32_t> uids(mUidSortedList.begin(), mUidSortedList.end());
394*ec779b8eSAndroid Build Coastguard Worker
395*ec779b8eSAndroid Build Coastguard Worker for (int32_t i = 0; i < uids.size(); i++) {
396*ec779b8eSAndroid Build Coastguard Worker const uid_t uid = uids[i];
397*ec779b8eSAndroid Build Coastguard Worker
398*ec779b8eSAndroid Build Coastguard Worker if (mSessionQueues[uid].empty()) {
399*ec779b8eSAndroid Build Coastguard Worker continue;
400*ec779b8eSAndroid Build Coastguard Worker }
401*ec779b8eSAndroid Build Coastguard Worker snprintf(buffer, SIZE, " uid: %d, pkg: %s\n", uid,
402*ec779b8eSAndroid Build Coastguard Worker mUidPackageNames.count(uid) > 0 ? mUidPackageNames[uid].c_str() : "(unknown)");
403*ec779b8eSAndroid Build Coastguard Worker result.append(buffer);
404*ec779b8eSAndroid Build Coastguard Worker snprintf(buffer, SIZE, " Num of sessions: %zu\n", mSessionQueues[uid].size());
405*ec779b8eSAndroid Build Coastguard Worker result.append(buffer);
406*ec779b8eSAndroid Build Coastguard Worker for (auto& sessionKey : mSessionQueues[uid]) {
407*ec779b8eSAndroid Build Coastguard Worker auto sessionIt = mSessionMap.find(sessionKey);
408*ec779b8eSAndroid Build Coastguard Worker if (sessionIt == mSessionMap.end()) {
409*ec779b8eSAndroid Build Coastguard Worker snprintf(buffer, SIZE, "Failed to look up Session %s \n",
410*ec779b8eSAndroid Build Coastguard Worker sessionToString(sessionKey).c_str());
411*ec779b8eSAndroid Build Coastguard Worker result.append(buffer);
412*ec779b8eSAndroid Build Coastguard Worker continue;
413*ec779b8eSAndroid Build Coastguard Worker }
414*ec779b8eSAndroid Build Coastguard Worker dumpSession_l(sessionIt->second, result);
415*ec779b8eSAndroid Build Coastguard Worker }
416*ec779b8eSAndroid Build Coastguard Worker }
417*ec779b8eSAndroid Build Coastguard Worker
418*ec779b8eSAndroid Build Coastguard Worker snprintf(buffer, SIZE, "\n========== Dumping past sessions =========\n");
419*ec779b8eSAndroid Build Coastguard Worker result.append(buffer);
420*ec779b8eSAndroid Build Coastguard Worker for (auto& session : mSessionHistory) {
421*ec779b8eSAndroid Build Coastguard Worker dumpSession_l(session, result, true /*closedSession*/);
422*ec779b8eSAndroid Build Coastguard Worker }
423*ec779b8eSAndroid Build Coastguard Worker
424*ec779b8eSAndroid Build Coastguard Worker write(fd, result.c_str(), result.size());
425*ec779b8eSAndroid Build Coastguard Worker }
426*ec779b8eSAndroid Build Coastguard Worker
427*ec779b8eSAndroid Build Coastguard Worker /*
428*ec779b8eSAndroid Build Coastguard Worker * Returns nullptr if there is no session, or we're paused globally (due to resource lost,
429*ec779b8eSAndroid Build Coastguard Worker * thermal throttling, etc.). Otherwise, return the session that should be run next.
430*ec779b8eSAndroid Build Coastguard Worker */
getTopSession_l()431*ec779b8eSAndroid Build Coastguard Worker TranscodingSessionController::Session* TranscodingSessionController::getTopSession_l() {
432*ec779b8eSAndroid Build Coastguard Worker if (mSessionMap.empty()) {
433*ec779b8eSAndroid Build Coastguard Worker return nullptr;
434*ec779b8eSAndroid Build Coastguard Worker }
435*ec779b8eSAndroid Build Coastguard Worker
436*ec779b8eSAndroid Build Coastguard Worker // Return nullptr if we're paused globally due to resource lost or thermal throttling.
437*ec779b8eSAndroid Build Coastguard Worker if (((mResourcePolicy != nullptr && mResourceLost) ||
438*ec779b8eSAndroid Build Coastguard Worker (mThermalPolicy != nullptr && mThermalThrottling))) {
439*ec779b8eSAndroid Build Coastguard Worker return nullptr;
440*ec779b8eSAndroid Build Coastguard Worker }
441*ec779b8eSAndroid Build Coastguard Worker
442*ec779b8eSAndroid Build Coastguard Worker uid_t topUid = *mUidSortedList.begin();
443*ec779b8eSAndroid Build Coastguard Worker // If the current session is running, and it's in the topUid's queue, let it continue
444*ec779b8eSAndroid Build Coastguard Worker // to run even if it's not the earliest in that uid's queue.
445*ec779b8eSAndroid Build Coastguard Worker // For example, uid(B) is added to a session while it's pending in uid(A)'s queue, then
446*ec779b8eSAndroid Build Coastguard Worker // B is brought to front which caused the session to run, then user switches back to A.
447*ec779b8eSAndroid Build Coastguard Worker if (mCurrentSession != nullptr && mCurrentSession->getState() == Session::RUNNING &&
448*ec779b8eSAndroid Build Coastguard Worker mCurrentSession->allClientUids.count(topUid) > 0) {
449*ec779b8eSAndroid Build Coastguard Worker return mCurrentSession;
450*ec779b8eSAndroid Build Coastguard Worker }
451*ec779b8eSAndroid Build Coastguard Worker SessionKeyType topSessionKey = *mSessionQueues[topUid].begin();
452*ec779b8eSAndroid Build Coastguard Worker return &mSessionMap[topSessionKey];
453*ec779b8eSAndroid Build Coastguard Worker }
454*ec779b8eSAndroid Build Coastguard Worker
setSessionState_l(Session * session,Session::State state)455*ec779b8eSAndroid Build Coastguard Worker void TranscodingSessionController::setSessionState_l(Session* session, Session::State state) {
456*ec779b8eSAndroid Build Coastguard Worker bool wasRunning = (session->getState() == Session::RUNNING);
457*ec779b8eSAndroid Build Coastguard Worker session->setState(state);
458*ec779b8eSAndroid Build Coastguard Worker bool isRunning = (session->getState() == Session::RUNNING);
459*ec779b8eSAndroid Build Coastguard Worker
460*ec779b8eSAndroid Build Coastguard Worker if (wasRunning == isRunning) {
461*ec779b8eSAndroid Build Coastguard Worker return;
462*ec779b8eSAndroid Build Coastguard Worker }
463*ec779b8eSAndroid Build Coastguard Worker
464*ec779b8eSAndroid Build Coastguard Worker // Currently we only have 1 running session, and we always put the previous
465*ec779b8eSAndroid Build Coastguard Worker // session in non-running state before we run the new session, so it's okay
466*ec779b8eSAndroid Build Coastguard Worker // to start/stop the watchdog here. If this assumption changes, we need to
467*ec779b8eSAndroid Build Coastguard Worker // track the number of running sessions and start/stop watchdog based on that.
468*ec779b8eSAndroid Build Coastguard Worker if (isRunning) {
469*ec779b8eSAndroid Build Coastguard Worker mWatchdog->start(session->key);
470*ec779b8eSAndroid Build Coastguard Worker } else {
471*ec779b8eSAndroid Build Coastguard Worker mWatchdog->stop();
472*ec779b8eSAndroid Build Coastguard Worker }
473*ec779b8eSAndroid Build Coastguard Worker }
474*ec779b8eSAndroid Build Coastguard Worker
setState(Session::State newState)475*ec779b8eSAndroid Build Coastguard Worker void TranscodingSessionController::Session::setState(Session::State newState) {
476*ec779b8eSAndroid Build Coastguard Worker if (state == newState) {
477*ec779b8eSAndroid Build Coastguard Worker return;
478*ec779b8eSAndroid Build Coastguard Worker }
479*ec779b8eSAndroid Build Coastguard Worker auto nowTime = std::chrono::steady_clock::now();
480*ec779b8eSAndroid Build Coastguard Worker if (state != INVALID) {
481*ec779b8eSAndroid Build Coastguard Worker std::chrono::microseconds elapsedTime =
482*ec779b8eSAndroid Build Coastguard Worker std::chrono::duration_cast<std::chrono::microseconds>(nowTime - stateEnterTime);
483*ec779b8eSAndroid Build Coastguard Worker switch (state) {
484*ec779b8eSAndroid Build Coastguard Worker case PAUSED:
485*ec779b8eSAndroid Build Coastguard Worker pausedTime = pausedTime + elapsedTime;
486*ec779b8eSAndroid Build Coastguard Worker break;
487*ec779b8eSAndroid Build Coastguard Worker case RUNNING:
488*ec779b8eSAndroid Build Coastguard Worker runningTime = runningTime + elapsedTime;
489*ec779b8eSAndroid Build Coastguard Worker break;
490*ec779b8eSAndroid Build Coastguard Worker case NOT_STARTED:
491*ec779b8eSAndroid Build Coastguard Worker waitingTime = waitingTime + elapsedTime;
492*ec779b8eSAndroid Build Coastguard Worker break;
493*ec779b8eSAndroid Build Coastguard Worker default:
494*ec779b8eSAndroid Build Coastguard Worker break;
495*ec779b8eSAndroid Build Coastguard Worker }
496*ec779b8eSAndroid Build Coastguard Worker }
497*ec779b8eSAndroid Build Coastguard Worker if (newState == PAUSED) {
498*ec779b8eSAndroid Build Coastguard Worker pauseCount++;
499*ec779b8eSAndroid Build Coastguard Worker }
500*ec779b8eSAndroid Build Coastguard Worker stateEnterTime = nowTime;
501*ec779b8eSAndroid Build Coastguard Worker state = newState;
502*ec779b8eSAndroid Build Coastguard Worker }
503*ec779b8eSAndroid Build Coastguard Worker
updateCurrentSession_l()504*ec779b8eSAndroid Build Coastguard Worker void TranscodingSessionController::updateCurrentSession_l() {
505*ec779b8eSAndroid Build Coastguard Worker Session* curSession = mCurrentSession;
506*ec779b8eSAndroid Build Coastguard Worker Session* topSession = nullptr;
507*ec779b8eSAndroid Build Coastguard Worker
508*ec779b8eSAndroid Build Coastguard Worker // Delayed init of transcoder and watchdog.
509*ec779b8eSAndroid Build Coastguard Worker if (mTranscoder == nullptr) {
510*ec779b8eSAndroid Build Coastguard Worker mTranscoder = mTranscoderFactory(shared_from_this());
511*ec779b8eSAndroid Build Coastguard Worker mWatchdog = std::make_shared<Watchdog>(this, mConfig.watchdogTimeoutUs);
512*ec779b8eSAndroid Build Coastguard Worker }
513*ec779b8eSAndroid Build Coastguard Worker
514*ec779b8eSAndroid Build Coastguard Worker // If we found a different top session, or the top session's running state is not
515*ec779b8eSAndroid Build Coastguard Worker // correct. Take some actions to ensure it's correct.
516*ec779b8eSAndroid Build Coastguard Worker while ((topSession = getTopSession_l()) != curSession ||
517*ec779b8eSAndroid Build Coastguard Worker (topSession != nullptr && !topSession->isRunning())) {
518*ec779b8eSAndroid Build Coastguard Worker ALOGV("updateCurrentSession_l: topSession is %s, curSession is %s",
519*ec779b8eSAndroid Build Coastguard Worker topSession == nullptr ? "null" : sessionToString(topSession->key).c_str(),
520*ec779b8eSAndroid Build Coastguard Worker curSession == nullptr ? "null" : sessionToString(curSession->key).c_str());
521*ec779b8eSAndroid Build Coastguard Worker
522*ec779b8eSAndroid Build Coastguard Worker // If current session is running, pause it first. Note this is needed for either
523*ec779b8eSAndroid Build Coastguard Worker // cases: 1) Top session is changing to another session, or 2) Top session is
524*ec779b8eSAndroid Build Coastguard Worker // changing to null (which means we should be globally paused).
525*ec779b8eSAndroid Build Coastguard Worker if (curSession != nullptr && curSession->getState() == Session::RUNNING) {
526*ec779b8eSAndroid Build Coastguard Worker mTranscoder->pause(curSession->key.first, curSession->key.second);
527*ec779b8eSAndroid Build Coastguard Worker setSessionState_l(curSession, Session::PAUSED);
528*ec779b8eSAndroid Build Coastguard Worker }
529*ec779b8eSAndroid Build Coastguard Worker
530*ec779b8eSAndroid Build Coastguard Worker if (topSession == nullptr) {
531*ec779b8eSAndroid Build Coastguard Worker // Nothing more to run (either no session or globally paused).
532*ec779b8eSAndroid Build Coastguard Worker break;
533*ec779b8eSAndroid Build Coastguard Worker }
534*ec779b8eSAndroid Build Coastguard Worker
535*ec779b8eSAndroid Build Coastguard Worker // Otherwise, ensure topSession is running.
536*ec779b8eSAndroid Build Coastguard Worker if (topSession->getState() == Session::NOT_STARTED) {
537*ec779b8eSAndroid Build Coastguard Worker // Check if at least one client has quota to start the session.
538*ec779b8eSAndroid Build Coastguard Worker bool keepForClient = false;
539*ec779b8eSAndroid Build Coastguard Worker for (uid_t uid : topSession->allClientUids) {
540*ec779b8eSAndroid Build Coastguard Worker if (mPacer->onSessionStarted(uid, topSession->callingUid)) {
541*ec779b8eSAndroid Build Coastguard Worker keepForClient = true;
542*ec779b8eSAndroid Build Coastguard Worker // DO NOT break here, because book-keeping still needs to happen
543*ec779b8eSAndroid Build Coastguard Worker // for the other uids.
544*ec779b8eSAndroid Build Coastguard Worker }
545*ec779b8eSAndroid Build Coastguard Worker }
546*ec779b8eSAndroid Build Coastguard Worker if (!keepForClient) {
547*ec779b8eSAndroid Build Coastguard Worker // Unfortunately all uids requesting this session are out of quota.
548*ec779b8eSAndroid Build Coastguard Worker // Drop this session and try the next one.
549*ec779b8eSAndroid Build Coastguard Worker {
550*ec779b8eSAndroid Build Coastguard Worker auto clientCallback = mSessionMap[topSession->key].callback.lock();
551*ec779b8eSAndroid Build Coastguard Worker if (clientCallback != nullptr) {
552*ec779b8eSAndroid Build Coastguard Worker clientCallback->onTranscodingFailed(
553*ec779b8eSAndroid Build Coastguard Worker topSession->key.second, TranscodingErrorCode::kDroppedByService);
554*ec779b8eSAndroid Build Coastguard Worker }
555*ec779b8eSAndroid Build Coastguard Worker }
556*ec779b8eSAndroid Build Coastguard Worker removeSession_l(topSession->key, Session::DROPPED_BY_PACER);
557*ec779b8eSAndroid Build Coastguard Worker continue;
558*ec779b8eSAndroid Build Coastguard Worker }
559*ec779b8eSAndroid Build Coastguard Worker mTranscoder->start(topSession->key.first, topSession->key.second, topSession->request,
560*ec779b8eSAndroid Build Coastguard Worker topSession->callingUid, topSession->callback.lock());
561*ec779b8eSAndroid Build Coastguard Worker setSessionState_l(topSession, Session::RUNNING);
562*ec779b8eSAndroid Build Coastguard Worker } else if (topSession->getState() == Session::PAUSED) {
563*ec779b8eSAndroid Build Coastguard Worker mTranscoder->resume(topSession->key.first, topSession->key.second, topSession->request,
564*ec779b8eSAndroid Build Coastguard Worker topSession->callingUid, topSession->callback.lock());
565*ec779b8eSAndroid Build Coastguard Worker setSessionState_l(topSession, Session::RUNNING);
566*ec779b8eSAndroid Build Coastguard Worker }
567*ec779b8eSAndroid Build Coastguard Worker break;
568*ec779b8eSAndroid Build Coastguard Worker }
569*ec779b8eSAndroid Build Coastguard Worker mCurrentSession = topSession;
570*ec779b8eSAndroid Build Coastguard Worker }
571*ec779b8eSAndroid Build Coastguard Worker
addUidToSession_l(uid_t clientUid,const SessionKeyType & sessionKey)572*ec779b8eSAndroid Build Coastguard Worker void TranscodingSessionController::addUidToSession_l(uid_t clientUid,
573*ec779b8eSAndroid Build Coastguard Worker const SessionKeyType& sessionKey) {
574*ec779b8eSAndroid Build Coastguard Worker // If it's an offline session, the queue was already added in constructor.
575*ec779b8eSAndroid Build Coastguard Worker // If it's a real-time sessions, check if a queue is already present for the uid,
576*ec779b8eSAndroid Build Coastguard Worker // and add a new queue if needed.
577*ec779b8eSAndroid Build Coastguard Worker if (clientUid != OFFLINE_UID) {
578*ec779b8eSAndroid Build Coastguard Worker if (mSessionQueues.count(clientUid) == 0) {
579*ec779b8eSAndroid Build Coastguard Worker mUidPolicy->registerMonitorUid(clientUid);
580*ec779b8eSAndroid Build Coastguard Worker if (mUidPolicy->isUidOnTop(clientUid)) {
581*ec779b8eSAndroid Build Coastguard Worker mUidSortedList.push_front(clientUid);
582*ec779b8eSAndroid Build Coastguard Worker } else {
583*ec779b8eSAndroid Build Coastguard Worker // Shouldn't be submitting real-time requests from non-top app,
584*ec779b8eSAndroid Build Coastguard Worker // put it in front of the offline queue.
585*ec779b8eSAndroid Build Coastguard Worker mUidSortedList.insert(mOfflineUidIterator, clientUid);
586*ec779b8eSAndroid Build Coastguard Worker }
587*ec779b8eSAndroid Build Coastguard Worker } else if (clientUid != *mUidSortedList.begin()) {
588*ec779b8eSAndroid Build Coastguard Worker if (mUidPolicy->isUidOnTop(clientUid)) {
589*ec779b8eSAndroid Build Coastguard Worker mUidSortedList.remove(clientUid);
590*ec779b8eSAndroid Build Coastguard Worker mUidSortedList.push_front(clientUid);
591*ec779b8eSAndroid Build Coastguard Worker }
592*ec779b8eSAndroid Build Coastguard Worker }
593*ec779b8eSAndroid Build Coastguard Worker }
594*ec779b8eSAndroid Build Coastguard Worker // Append this session to the uid's queue.
595*ec779b8eSAndroid Build Coastguard Worker mSessionQueues[clientUid].push_back(sessionKey);
596*ec779b8eSAndroid Build Coastguard Worker }
597*ec779b8eSAndroid Build Coastguard Worker
removeSession_l(const SessionKeyType & sessionKey,Session::State finalState,const std::shared_ptr<std::function<bool (uid_t uid)>> & keepUid)598*ec779b8eSAndroid Build Coastguard Worker void TranscodingSessionController::removeSession_l(
599*ec779b8eSAndroid Build Coastguard Worker const SessionKeyType& sessionKey, Session::State finalState,
600*ec779b8eSAndroid Build Coastguard Worker const std::shared_ptr<std::function<bool(uid_t uid)>>& keepUid) {
601*ec779b8eSAndroid Build Coastguard Worker ALOGV("%s: session %s", __FUNCTION__, sessionToString(sessionKey).c_str());
602*ec779b8eSAndroid Build Coastguard Worker
603*ec779b8eSAndroid Build Coastguard Worker if (mSessionMap.count(sessionKey) == 0) {
604*ec779b8eSAndroid Build Coastguard Worker ALOGE("session %s doesn't exist", sessionToString(sessionKey).c_str());
605*ec779b8eSAndroid Build Coastguard Worker return;
606*ec779b8eSAndroid Build Coastguard Worker }
607*ec779b8eSAndroid Build Coastguard Worker
608*ec779b8eSAndroid Build Coastguard Worker // Remove session from uid's queue.
609*ec779b8eSAndroid Build Coastguard Worker bool uidQueueRemoved = false;
610*ec779b8eSAndroid Build Coastguard Worker std::unordered_set<uid_t> remainingUids;
611*ec779b8eSAndroid Build Coastguard Worker for (uid_t uid : mSessionMap[sessionKey].allClientUids) {
612*ec779b8eSAndroid Build Coastguard Worker if (keepUid != nullptr) {
613*ec779b8eSAndroid Build Coastguard Worker if ((*keepUid)(uid)) {
614*ec779b8eSAndroid Build Coastguard Worker remainingUids.insert(uid);
615*ec779b8eSAndroid Build Coastguard Worker continue;
616*ec779b8eSAndroid Build Coastguard Worker }
617*ec779b8eSAndroid Build Coastguard Worker // If we have uids to keep, the session is not going to any final
618*ec779b8eSAndroid Build Coastguard Worker // state we can't use onSessionCompleted as the running time will
619*ec779b8eSAndroid Build Coastguard Worker // not be valid. Only notify pacer to stop tracking this session.
620*ec779b8eSAndroid Build Coastguard Worker mPacer->onSessionCancelled(uid);
621*ec779b8eSAndroid Build Coastguard Worker }
622*ec779b8eSAndroid Build Coastguard Worker SessionQueueType& sessionQueue = mSessionQueues[uid];
623*ec779b8eSAndroid Build Coastguard Worker auto it = std::find(sessionQueue.begin(), sessionQueue.end(), sessionKey);
624*ec779b8eSAndroid Build Coastguard Worker if (it == sessionQueue.end()) {
625*ec779b8eSAndroid Build Coastguard Worker ALOGW("couldn't find session %s in queue for uid %d",
626*ec779b8eSAndroid Build Coastguard Worker sessionToString(sessionKey).c_str(), uid);
627*ec779b8eSAndroid Build Coastguard Worker continue;
628*ec779b8eSAndroid Build Coastguard Worker }
629*ec779b8eSAndroid Build Coastguard Worker sessionQueue.erase(it);
630*ec779b8eSAndroid Build Coastguard Worker
631*ec779b8eSAndroid Build Coastguard Worker // If this is the last session in a real-time queue, remove this uid's queue.
632*ec779b8eSAndroid Build Coastguard Worker if (uid != OFFLINE_UID && sessionQueue.empty()) {
633*ec779b8eSAndroid Build Coastguard Worker mUidSortedList.remove(uid);
634*ec779b8eSAndroid Build Coastguard Worker mSessionQueues.erase(uid);
635*ec779b8eSAndroid Build Coastguard Worker mUidPolicy->unregisterMonitorUid(uid);
636*ec779b8eSAndroid Build Coastguard Worker
637*ec779b8eSAndroid Build Coastguard Worker uidQueueRemoved = true;
638*ec779b8eSAndroid Build Coastguard Worker }
639*ec779b8eSAndroid Build Coastguard Worker }
640*ec779b8eSAndroid Build Coastguard Worker
641*ec779b8eSAndroid Build Coastguard Worker if (uidQueueRemoved) {
642*ec779b8eSAndroid Build Coastguard Worker std::unordered_set<uid_t> topUids = mUidPolicy->getTopUids();
643*ec779b8eSAndroid Build Coastguard Worker moveUidsToTop_l(topUids, false /*preserveTopUid*/);
644*ec779b8eSAndroid Build Coastguard Worker }
645*ec779b8eSAndroid Build Coastguard Worker
646*ec779b8eSAndroid Build Coastguard Worker if (keepUid != nullptr) {
647*ec779b8eSAndroid Build Coastguard Worker mSessionMap[sessionKey].allClientUids = remainingUids;
648*ec779b8eSAndroid Build Coastguard Worker return;
649*ec779b8eSAndroid Build Coastguard Worker }
650*ec779b8eSAndroid Build Coastguard Worker
651*ec779b8eSAndroid Build Coastguard Worker // Clear current session.
652*ec779b8eSAndroid Build Coastguard Worker if (mCurrentSession == &mSessionMap[sessionKey]) {
653*ec779b8eSAndroid Build Coastguard Worker mCurrentSession = nullptr;
654*ec779b8eSAndroid Build Coastguard Worker }
655*ec779b8eSAndroid Build Coastguard Worker
656*ec779b8eSAndroid Build Coastguard Worker setSessionState_l(&mSessionMap[sessionKey], finalState);
657*ec779b8eSAndroid Build Coastguard Worker
658*ec779b8eSAndroid Build Coastguard Worker // We can use onSessionCompleted() even for CANCELLED, because runningTime is
659*ec779b8eSAndroid Build Coastguard Worker // now updated by setSessionState_l().
660*ec779b8eSAndroid Build Coastguard Worker for (uid_t uid : mSessionMap[sessionKey].allClientUids) {
661*ec779b8eSAndroid Build Coastguard Worker mPacer->onSessionCompleted(uid, mSessionMap[sessionKey].runningTime);
662*ec779b8eSAndroid Build Coastguard Worker }
663*ec779b8eSAndroid Build Coastguard Worker
664*ec779b8eSAndroid Build Coastguard Worker mSessionHistory.push_back(mSessionMap[sessionKey]);
665*ec779b8eSAndroid Build Coastguard Worker if (mSessionHistory.size() > kSessionHistoryMax) {
666*ec779b8eSAndroid Build Coastguard Worker mSessionHistory.erase(mSessionHistory.begin());
667*ec779b8eSAndroid Build Coastguard Worker }
668*ec779b8eSAndroid Build Coastguard Worker
669*ec779b8eSAndroid Build Coastguard Worker // Remove session from session map.
670*ec779b8eSAndroid Build Coastguard Worker mSessionMap.erase(sessionKey);
671*ec779b8eSAndroid Build Coastguard Worker }
672*ec779b8eSAndroid Build Coastguard Worker
673*ec779b8eSAndroid Build Coastguard Worker /**
674*ec779b8eSAndroid Build Coastguard Worker * Moves the set of uids to the front of mUidSortedList (which is used to pick
675*ec779b8eSAndroid Build Coastguard Worker * the next session to run).
676*ec779b8eSAndroid Build Coastguard Worker *
677*ec779b8eSAndroid Build Coastguard Worker * This is called when 1) we received a onTopUidsChanged() callback from UidPolicy,
678*ec779b8eSAndroid Build Coastguard Worker * or 2) we removed the session queue for a uid because it becomes empty.
679*ec779b8eSAndroid Build Coastguard Worker *
680*ec779b8eSAndroid Build Coastguard Worker * In case of 1), if there are multiple uids in the set, and the current front
681*ec779b8eSAndroid Build Coastguard Worker * uid in mUidSortedList is still in the set, we try to keep that uid at front
682*ec779b8eSAndroid Build Coastguard Worker * so that current session run is not interrupted. (This is not a concern for case 2)
683*ec779b8eSAndroid Build Coastguard Worker * because the queue for a uid was just removed entirely.)
684*ec779b8eSAndroid Build Coastguard Worker */
moveUidsToTop_l(const std::unordered_set<uid_t> & uids,bool preserveTopUid)685*ec779b8eSAndroid Build Coastguard Worker void TranscodingSessionController::moveUidsToTop_l(const std::unordered_set<uid_t>& uids,
686*ec779b8eSAndroid Build Coastguard Worker bool preserveTopUid) {
687*ec779b8eSAndroid Build Coastguard Worker // If uid set is empty, nothing to do. Do not change the queue status.
688*ec779b8eSAndroid Build Coastguard Worker if (uids.empty()) {
689*ec779b8eSAndroid Build Coastguard Worker return;
690*ec779b8eSAndroid Build Coastguard Worker }
691*ec779b8eSAndroid Build Coastguard Worker
692*ec779b8eSAndroid Build Coastguard Worker // Save the current top uid.
693*ec779b8eSAndroid Build Coastguard Worker uid_t curTopUid = *mUidSortedList.begin();
694*ec779b8eSAndroid Build Coastguard Worker bool pushCurTopToFront = false;
695*ec779b8eSAndroid Build Coastguard Worker int32_t numUidsMoved = 0;
696*ec779b8eSAndroid Build Coastguard Worker
697*ec779b8eSAndroid Build Coastguard Worker // Go through the sorted uid list once, and move the ones in top set to front.
698*ec779b8eSAndroid Build Coastguard Worker for (auto it = mUidSortedList.begin(); it != mUidSortedList.end();) {
699*ec779b8eSAndroid Build Coastguard Worker uid_t uid = *it;
700*ec779b8eSAndroid Build Coastguard Worker
701*ec779b8eSAndroid Build Coastguard Worker if (uid != OFFLINE_UID && uids.count(uid) > 0) {
702*ec779b8eSAndroid Build Coastguard Worker it = mUidSortedList.erase(it);
703*ec779b8eSAndroid Build Coastguard Worker
704*ec779b8eSAndroid Build Coastguard Worker // If this is the top we're preserving, don't push it here, push
705*ec779b8eSAndroid Build Coastguard Worker // it after the for-loop.
706*ec779b8eSAndroid Build Coastguard Worker if (uid == curTopUid && preserveTopUid) {
707*ec779b8eSAndroid Build Coastguard Worker pushCurTopToFront = true;
708*ec779b8eSAndroid Build Coastguard Worker } else {
709*ec779b8eSAndroid Build Coastguard Worker mUidSortedList.push_front(uid);
710*ec779b8eSAndroid Build Coastguard Worker }
711*ec779b8eSAndroid Build Coastguard Worker
712*ec779b8eSAndroid Build Coastguard Worker // If we found all uids in the set, break out.
713*ec779b8eSAndroid Build Coastguard Worker if (++numUidsMoved == uids.size()) {
714*ec779b8eSAndroid Build Coastguard Worker break;
715*ec779b8eSAndroid Build Coastguard Worker }
716*ec779b8eSAndroid Build Coastguard Worker } else {
717*ec779b8eSAndroid Build Coastguard Worker ++it;
718*ec779b8eSAndroid Build Coastguard Worker }
719*ec779b8eSAndroid Build Coastguard Worker }
720*ec779b8eSAndroid Build Coastguard Worker
721*ec779b8eSAndroid Build Coastguard Worker if (pushCurTopToFront) {
722*ec779b8eSAndroid Build Coastguard Worker mUidSortedList.push_front(curTopUid);
723*ec779b8eSAndroid Build Coastguard Worker }
724*ec779b8eSAndroid Build Coastguard Worker }
725*ec779b8eSAndroid Build Coastguard Worker
submit(ClientIdType clientId,SessionIdType sessionId,uid_t callingUid,uid_t clientUid,const TranscodingRequestParcel & request,const std::weak_ptr<ITranscodingClientCallback> & callback)726*ec779b8eSAndroid Build Coastguard Worker bool TranscodingSessionController::submit(
727*ec779b8eSAndroid Build Coastguard Worker ClientIdType clientId, SessionIdType sessionId, uid_t callingUid, uid_t clientUid,
728*ec779b8eSAndroid Build Coastguard Worker const TranscodingRequestParcel& request,
729*ec779b8eSAndroid Build Coastguard Worker const std::weak_ptr<ITranscodingClientCallback>& callback) {
730*ec779b8eSAndroid Build Coastguard Worker SessionKeyType sessionKey = std::make_pair(clientId, sessionId);
731*ec779b8eSAndroid Build Coastguard Worker
732*ec779b8eSAndroid Build Coastguard Worker ALOGV("%s: session %s, uid %d, prioirty %d", __FUNCTION__, sessionToString(sessionKey).c_str(),
733*ec779b8eSAndroid Build Coastguard Worker clientUid, (int32_t)request.priority);
734*ec779b8eSAndroid Build Coastguard Worker
735*ec779b8eSAndroid Build Coastguard Worker std::scoped_lock lock{mLock};
736*ec779b8eSAndroid Build Coastguard Worker
737*ec779b8eSAndroid Build Coastguard Worker if (mSessionMap.count(sessionKey) > 0) {
738*ec779b8eSAndroid Build Coastguard Worker ALOGE("session %s already exists", sessionToString(sessionKey).c_str());
739*ec779b8eSAndroid Build Coastguard Worker return false;
740*ec779b8eSAndroid Build Coastguard Worker }
741*ec779b8eSAndroid Build Coastguard Worker
742*ec779b8eSAndroid Build Coastguard Worker // Add the uid package name to the store of package names we already know.
743*ec779b8eSAndroid Build Coastguard Worker if (mUidPackageNames.count(clientUid) == 0) {
744*ec779b8eSAndroid Build Coastguard Worker mUidPackageNames.emplace(clientUid, request.clientPackageName);
745*ec779b8eSAndroid Build Coastguard Worker }
746*ec779b8eSAndroid Build Coastguard Worker
747*ec779b8eSAndroid Build Coastguard Worker // TODO(chz): only support offline vs real-time for now. All kUnspecified sessions
748*ec779b8eSAndroid Build Coastguard Worker // go to offline queue.
749*ec779b8eSAndroid Build Coastguard Worker if (request.priority == TranscodingSessionPriority::kUnspecified) {
750*ec779b8eSAndroid Build Coastguard Worker clientUid = OFFLINE_UID;
751*ec779b8eSAndroid Build Coastguard Worker }
752*ec779b8eSAndroid Build Coastguard Worker
753*ec779b8eSAndroid Build Coastguard Worker // Add session to session map.
754*ec779b8eSAndroid Build Coastguard Worker mSessionMap[sessionKey].key = sessionKey;
755*ec779b8eSAndroid Build Coastguard Worker mSessionMap[sessionKey].callingUid = callingUid;
756*ec779b8eSAndroid Build Coastguard Worker mSessionMap[sessionKey].allClientUids.insert(clientUid);
757*ec779b8eSAndroid Build Coastguard Worker mSessionMap[sessionKey].request = request;
758*ec779b8eSAndroid Build Coastguard Worker mSessionMap[sessionKey].callback = callback;
759*ec779b8eSAndroid Build Coastguard Worker setSessionState_l(&mSessionMap[sessionKey], Session::NOT_STARTED);
760*ec779b8eSAndroid Build Coastguard Worker
761*ec779b8eSAndroid Build Coastguard Worker addUidToSession_l(clientUid, sessionKey);
762*ec779b8eSAndroid Build Coastguard Worker
763*ec779b8eSAndroid Build Coastguard Worker updateCurrentSession_l();
764*ec779b8eSAndroid Build Coastguard Worker
765*ec779b8eSAndroid Build Coastguard Worker validateState_l();
766*ec779b8eSAndroid Build Coastguard Worker return true;
767*ec779b8eSAndroid Build Coastguard Worker }
768*ec779b8eSAndroid Build Coastguard Worker
cancel(ClientIdType clientId,SessionIdType sessionId)769*ec779b8eSAndroid Build Coastguard Worker bool TranscodingSessionController::cancel(ClientIdType clientId, SessionIdType sessionId) {
770*ec779b8eSAndroid Build Coastguard Worker SessionKeyType sessionKey = std::make_pair(clientId, sessionId);
771*ec779b8eSAndroid Build Coastguard Worker
772*ec779b8eSAndroid Build Coastguard Worker ALOGV("%s: session %s", __FUNCTION__, sessionToString(sessionKey).c_str());
773*ec779b8eSAndroid Build Coastguard Worker
774*ec779b8eSAndroid Build Coastguard Worker std::list<SessionKeyType> sessionsToRemove, sessionsForOffline;
775*ec779b8eSAndroid Build Coastguard Worker
776*ec779b8eSAndroid Build Coastguard Worker std::scoped_lock lock{mLock};
777*ec779b8eSAndroid Build Coastguard Worker
778*ec779b8eSAndroid Build Coastguard Worker if (sessionId < 0) {
779*ec779b8eSAndroid Build Coastguard Worker for (auto it = mSessionMap.begin(); it != mSessionMap.end(); ++it) {
780*ec779b8eSAndroid Build Coastguard Worker if (it->first.first == clientId) {
781*ec779b8eSAndroid Build Coastguard Worker // If there is offline request, only keep the offline client;
782*ec779b8eSAndroid Build Coastguard Worker // otherwise remove the session.
783*ec779b8eSAndroid Build Coastguard Worker if (it->second.allClientUids.count(OFFLINE_UID) > 0) {
784*ec779b8eSAndroid Build Coastguard Worker sessionsForOffline.push_back(it->first);
785*ec779b8eSAndroid Build Coastguard Worker } else {
786*ec779b8eSAndroid Build Coastguard Worker sessionsToRemove.push_back(it->first);
787*ec779b8eSAndroid Build Coastguard Worker }
788*ec779b8eSAndroid Build Coastguard Worker }
789*ec779b8eSAndroid Build Coastguard Worker }
790*ec779b8eSAndroid Build Coastguard Worker } else {
791*ec779b8eSAndroid Build Coastguard Worker if (mSessionMap.count(sessionKey) == 0) {
792*ec779b8eSAndroid Build Coastguard Worker ALOGE("session %s doesn't exist", sessionToString(sessionKey).c_str());
793*ec779b8eSAndroid Build Coastguard Worker return false;
794*ec779b8eSAndroid Build Coastguard Worker }
795*ec779b8eSAndroid Build Coastguard Worker sessionsToRemove.push_back(sessionKey);
796*ec779b8eSAndroid Build Coastguard Worker }
797*ec779b8eSAndroid Build Coastguard Worker
798*ec779b8eSAndroid Build Coastguard Worker for (auto it = sessionsToRemove.begin(); it != sessionsToRemove.end(); ++it) {
799*ec779b8eSAndroid Build Coastguard Worker // If the session has ever been started, stop it now.
800*ec779b8eSAndroid Build Coastguard Worker // Note that stop() is needed even if the session is currently paused. This instructs
801*ec779b8eSAndroid Build Coastguard Worker // the transcoder to discard any states for the session, otherwise the states may
802*ec779b8eSAndroid Build Coastguard Worker // never be discarded.
803*ec779b8eSAndroid Build Coastguard Worker if (mSessionMap[*it].getState() != Session::NOT_STARTED) {
804*ec779b8eSAndroid Build Coastguard Worker mTranscoder->stop(it->first, it->second);
805*ec779b8eSAndroid Build Coastguard Worker }
806*ec779b8eSAndroid Build Coastguard Worker
807*ec779b8eSAndroid Build Coastguard Worker // Remove the session.
808*ec779b8eSAndroid Build Coastguard Worker removeSession_l(*it, Session::CANCELED);
809*ec779b8eSAndroid Build Coastguard Worker }
810*ec779b8eSAndroid Build Coastguard Worker
811*ec779b8eSAndroid Build Coastguard Worker auto keepUid = std::make_shared<std::function<bool(uid_t)>>(
812*ec779b8eSAndroid Build Coastguard Worker [](uid_t uid) { return uid == OFFLINE_UID; });
813*ec779b8eSAndroid Build Coastguard Worker for (auto it = sessionsForOffline.begin(); it != sessionsForOffline.end(); ++it) {
814*ec779b8eSAndroid Build Coastguard Worker removeSession_l(*it, Session::CANCELED, keepUid);
815*ec779b8eSAndroid Build Coastguard Worker }
816*ec779b8eSAndroid Build Coastguard Worker
817*ec779b8eSAndroid Build Coastguard Worker // Start next session.
818*ec779b8eSAndroid Build Coastguard Worker updateCurrentSession_l();
819*ec779b8eSAndroid Build Coastguard Worker
820*ec779b8eSAndroid Build Coastguard Worker validateState_l();
821*ec779b8eSAndroid Build Coastguard Worker return true;
822*ec779b8eSAndroid Build Coastguard Worker }
823*ec779b8eSAndroid Build Coastguard Worker
addClientUid(ClientIdType clientId,SessionIdType sessionId,uid_t clientUid)824*ec779b8eSAndroid Build Coastguard Worker bool TranscodingSessionController::addClientUid(ClientIdType clientId, SessionIdType sessionId,
825*ec779b8eSAndroid Build Coastguard Worker uid_t clientUid) {
826*ec779b8eSAndroid Build Coastguard Worker SessionKeyType sessionKey = std::make_pair(clientId, sessionId);
827*ec779b8eSAndroid Build Coastguard Worker
828*ec779b8eSAndroid Build Coastguard Worker std::scoped_lock lock{mLock};
829*ec779b8eSAndroid Build Coastguard Worker
830*ec779b8eSAndroid Build Coastguard Worker if (mSessionMap.count(sessionKey) == 0) {
831*ec779b8eSAndroid Build Coastguard Worker ALOGE("session %s doesn't exist", sessionToString(sessionKey).c_str());
832*ec779b8eSAndroid Build Coastguard Worker return false;
833*ec779b8eSAndroid Build Coastguard Worker }
834*ec779b8eSAndroid Build Coastguard Worker
835*ec779b8eSAndroid Build Coastguard Worker if (mSessionMap[sessionKey].allClientUids.count(clientUid) > 0) {
836*ec779b8eSAndroid Build Coastguard Worker ALOGE("session %s already has uid %d", sessionToString(sessionKey).c_str(), clientUid);
837*ec779b8eSAndroid Build Coastguard Worker return false;
838*ec779b8eSAndroid Build Coastguard Worker }
839*ec779b8eSAndroid Build Coastguard Worker
840*ec779b8eSAndroid Build Coastguard Worker mSessionMap[sessionKey].allClientUids.insert(clientUid);
841*ec779b8eSAndroid Build Coastguard Worker addUidToSession_l(clientUid, sessionKey);
842*ec779b8eSAndroid Build Coastguard Worker
843*ec779b8eSAndroid Build Coastguard Worker updateCurrentSession_l();
844*ec779b8eSAndroid Build Coastguard Worker
845*ec779b8eSAndroid Build Coastguard Worker validateState_l();
846*ec779b8eSAndroid Build Coastguard Worker return true;
847*ec779b8eSAndroid Build Coastguard Worker }
848*ec779b8eSAndroid Build Coastguard Worker
getClientUids(ClientIdType clientId,SessionIdType sessionId,std::vector<int32_t> * out_clientUids)849*ec779b8eSAndroid Build Coastguard Worker bool TranscodingSessionController::getClientUids(ClientIdType clientId, SessionIdType sessionId,
850*ec779b8eSAndroid Build Coastguard Worker std::vector<int32_t>* out_clientUids) {
851*ec779b8eSAndroid Build Coastguard Worker SessionKeyType sessionKey = std::make_pair(clientId, sessionId);
852*ec779b8eSAndroid Build Coastguard Worker
853*ec779b8eSAndroid Build Coastguard Worker std::scoped_lock lock{mLock};
854*ec779b8eSAndroid Build Coastguard Worker
855*ec779b8eSAndroid Build Coastguard Worker if (mSessionMap.count(sessionKey) == 0) {
856*ec779b8eSAndroid Build Coastguard Worker ALOGE("session %s doesn't exist", sessionToString(sessionKey).c_str());
857*ec779b8eSAndroid Build Coastguard Worker return false;
858*ec779b8eSAndroid Build Coastguard Worker }
859*ec779b8eSAndroid Build Coastguard Worker
860*ec779b8eSAndroid Build Coastguard Worker out_clientUids->clear();
861*ec779b8eSAndroid Build Coastguard Worker for (uid_t uid : mSessionMap[sessionKey].allClientUids) {
862*ec779b8eSAndroid Build Coastguard Worker if (uid != OFFLINE_UID) {
863*ec779b8eSAndroid Build Coastguard Worker out_clientUids->push_back(uid);
864*ec779b8eSAndroid Build Coastguard Worker }
865*ec779b8eSAndroid Build Coastguard Worker }
866*ec779b8eSAndroid Build Coastguard Worker return true;
867*ec779b8eSAndroid Build Coastguard Worker }
868*ec779b8eSAndroid Build Coastguard Worker
getSession(ClientIdType clientId,SessionIdType sessionId,TranscodingRequestParcel * request)869*ec779b8eSAndroid Build Coastguard Worker bool TranscodingSessionController::getSession(ClientIdType clientId, SessionIdType sessionId,
870*ec779b8eSAndroid Build Coastguard Worker TranscodingRequestParcel* request) {
871*ec779b8eSAndroid Build Coastguard Worker SessionKeyType sessionKey = std::make_pair(clientId, sessionId);
872*ec779b8eSAndroid Build Coastguard Worker
873*ec779b8eSAndroid Build Coastguard Worker std::scoped_lock lock{mLock};
874*ec779b8eSAndroid Build Coastguard Worker
875*ec779b8eSAndroid Build Coastguard Worker if (mSessionMap.count(sessionKey) == 0) {
876*ec779b8eSAndroid Build Coastguard Worker ALOGE("session %s doesn't exist", sessionToString(sessionKey).c_str());
877*ec779b8eSAndroid Build Coastguard Worker return false;
878*ec779b8eSAndroid Build Coastguard Worker }
879*ec779b8eSAndroid Build Coastguard Worker
880*ec779b8eSAndroid Build Coastguard Worker *(TranscodingRequest*)request = mSessionMap[sessionKey].request;
881*ec779b8eSAndroid Build Coastguard Worker return true;
882*ec779b8eSAndroid Build Coastguard Worker }
883*ec779b8eSAndroid Build Coastguard Worker
notifyClient(ClientIdType clientId,SessionIdType sessionId,const char * reason,std::function<void (const SessionKeyType &)> func)884*ec779b8eSAndroid Build Coastguard Worker void TranscodingSessionController::notifyClient(ClientIdType clientId, SessionIdType sessionId,
885*ec779b8eSAndroid Build Coastguard Worker const char* reason,
886*ec779b8eSAndroid Build Coastguard Worker std::function<void(const SessionKeyType&)> func) {
887*ec779b8eSAndroid Build Coastguard Worker SessionKeyType sessionKey = std::make_pair(clientId, sessionId);
888*ec779b8eSAndroid Build Coastguard Worker
889*ec779b8eSAndroid Build Coastguard Worker std::scoped_lock lock{mLock};
890*ec779b8eSAndroid Build Coastguard Worker
891*ec779b8eSAndroid Build Coastguard Worker if (mSessionMap.count(sessionKey) == 0) {
892*ec779b8eSAndroid Build Coastguard Worker ALOGW("%s: ignoring %s for session %s that doesn't exist", __FUNCTION__, reason,
893*ec779b8eSAndroid Build Coastguard Worker sessionToString(sessionKey).c_str());
894*ec779b8eSAndroid Build Coastguard Worker return;
895*ec779b8eSAndroid Build Coastguard Worker }
896*ec779b8eSAndroid Build Coastguard Worker
897*ec779b8eSAndroid Build Coastguard Worker // Only ignore if session was never started. In particular, propagate the status
898*ec779b8eSAndroid Build Coastguard Worker // to client if the session is paused. Transcoder could have posted finish when
899*ec779b8eSAndroid Build Coastguard Worker // we're pausing it, and the finish arrived after we changed current session.
900*ec779b8eSAndroid Build Coastguard Worker if (mSessionMap[sessionKey].getState() == Session::NOT_STARTED) {
901*ec779b8eSAndroid Build Coastguard Worker ALOGW("%s: ignoring %s for session %s that was never started", __FUNCTION__, reason,
902*ec779b8eSAndroid Build Coastguard Worker sessionToString(sessionKey).c_str());
903*ec779b8eSAndroid Build Coastguard Worker return;
904*ec779b8eSAndroid Build Coastguard Worker }
905*ec779b8eSAndroid Build Coastguard Worker
906*ec779b8eSAndroid Build Coastguard Worker ALOGV("%s: session %s %s", __FUNCTION__, sessionToString(sessionKey).c_str(), reason);
907*ec779b8eSAndroid Build Coastguard Worker func(sessionKey);
908*ec779b8eSAndroid Build Coastguard Worker }
909*ec779b8eSAndroid Build Coastguard Worker
onStarted(ClientIdType clientId,SessionIdType sessionId)910*ec779b8eSAndroid Build Coastguard Worker void TranscodingSessionController::onStarted(ClientIdType clientId, SessionIdType sessionId) {
911*ec779b8eSAndroid Build Coastguard Worker notifyClient(clientId, sessionId, "started", [=](const SessionKeyType& sessionKey) {
912*ec779b8eSAndroid Build Coastguard Worker auto callback = mSessionMap[sessionKey].callback.lock();
913*ec779b8eSAndroid Build Coastguard Worker if (callback != nullptr) {
914*ec779b8eSAndroid Build Coastguard Worker callback->onTranscodingStarted(sessionId);
915*ec779b8eSAndroid Build Coastguard Worker }
916*ec779b8eSAndroid Build Coastguard Worker });
917*ec779b8eSAndroid Build Coastguard Worker }
918*ec779b8eSAndroid Build Coastguard Worker
onPaused(ClientIdType clientId,SessionIdType sessionId)919*ec779b8eSAndroid Build Coastguard Worker void TranscodingSessionController::onPaused(ClientIdType clientId, SessionIdType sessionId) {
920*ec779b8eSAndroid Build Coastguard Worker notifyClient(clientId, sessionId, "paused", [=](const SessionKeyType& sessionKey) {
921*ec779b8eSAndroid Build Coastguard Worker auto callback = mSessionMap[sessionKey].callback.lock();
922*ec779b8eSAndroid Build Coastguard Worker if (callback != nullptr) {
923*ec779b8eSAndroid Build Coastguard Worker callback->onTranscodingPaused(sessionId);
924*ec779b8eSAndroid Build Coastguard Worker }
925*ec779b8eSAndroid Build Coastguard Worker });
926*ec779b8eSAndroid Build Coastguard Worker }
927*ec779b8eSAndroid Build Coastguard Worker
onResumed(ClientIdType clientId,SessionIdType sessionId)928*ec779b8eSAndroid Build Coastguard Worker void TranscodingSessionController::onResumed(ClientIdType clientId, SessionIdType sessionId) {
929*ec779b8eSAndroid Build Coastguard Worker notifyClient(clientId, sessionId, "resumed", [=](const SessionKeyType& sessionKey) {
930*ec779b8eSAndroid Build Coastguard Worker auto callback = mSessionMap[sessionKey].callback.lock();
931*ec779b8eSAndroid Build Coastguard Worker if (callback != nullptr) {
932*ec779b8eSAndroid Build Coastguard Worker callback->onTranscodingResumed(sessionId);
933*ec779b8eSAndroid Build Coastguard Worker }
934*ec779b8eSAndroid Build Coastguard Worker });
935*ec779b8eSAndroid Build Coastguard Worker }
936*ec779b8eSAndroid Build Coastguard Worker
onFinish(ClientIdType clientId,SessionIdType sessionId)937*ec779b8eSAndroid Build Coastguard Worker void TranscodingSessionController::onFinish(ClientIdType clientId, SessionIdType sessionId) {
938*ec779b8eSAndroid Build Coastguard Worker notifyClient(clientId, sessionId, "finish", [=](const SessionKeyType& sessionKey) {
939*ec779b8eSAndroid Build Coastguard Worker {
940*ec779b8eSAndroid Build Coastguard Worker auto clientCallback = mSessionMap[sessionKey].callback.lock();
941*ec779b8eSAndroid Build Coastguard Worker if (clientCallback != nullptr) {
942*ec779b8eSAndroid Build Coastguard Worker clientCallback->onTranscodingFinished(
943*ec779b8eSAndroid Build Coastguard Worker sessionId, TranscodingResultParcel({sessionId, -1 /*actualBitrateBps*/,
944*ec779b8eSAndroid Build Coastguard Worker std::nullopt /*sessionStats*/}));
945*ec779b8eSAndroid Build Coastguard Worker }
946*ec779b8eSAndroid Build Coastguard Worker }
947*ec779b8eSAndroid Build Coastguard Worker
948*ec779b8eSAndroid Build Coastguard Worker // Remove the session.
949*ec779b8eSAndroid Build Coastguard Worker removeSession_l(sessionKey, Session::FINISHED);
950*ec779b8eSAndroid Build Coastguard Worker
951*ec779b8eSAndroid Build Coastguard Worker // Start next session.
952*ec779b8eSAndroid Build Coastguard Worker updateCurrentSession_l();
953*ec779b8eSAndroid Build Coastguard Worker
954*ec779b8eSAndroid Build Coastguard Worker validateState_l();
955*ec779b8eSAndroid Build Coastguard Worker });
956*ec779b8eSAndroid Build Coastguard Worker }
957*ec779b8eSAndroid Build Coastguard Worker
onError(ClientIdType clientId,SessionIdType sessionId,TranscodingErrorCode err)958*ec779b8eSAndroid Build Coastguard Worker void TranscodingSessionController::onError(ClientIdType clientId, SessionIdType sessionId,
959*ec779b8eSAndroid Build Coastguard Worker TranscodingErrorCode err) {
960*ec779b8eSAndroid Build Coastguard Worker notifyClient(clientId, sessionId, "error", [=](const SessionKeyType& sessionKey) {
961*ec779b8eSAndroid Build Coastguard Worker if (err == TranscodingErrorCode::kWatchdogTimeout) {
962*ec779b8eSAndroid Build Coastguard Worker // Abandon the transcoder, as its handler thread might be stuck in some call to
963*ec779b8eSAndroid Build Coastguard Worker // MediaTranscoder altogether, and may not be able to handle any new tasks.
964*ec779b8eSAndroid Build Coastguard Worker mTranscoder->stop(clientId, sessionId, true /*abandon*/);
965*ec779b8eSAndroid Build Coastguard Worker // Clear the last ref count before we create new transcoder.
966*ec779b8eSAndroid Build Coastguard Worker mTranscoder = nullptr;
967*ec779b8eSAndroid Build Coastguard Worker mTranscoder = mTranscoderFactory(shared_from_this());
968*ec779b8eSAndroid Build Coastguard Worker }
969*ec779b8eSAndroid Build Coastguard Worker
970*ec779b8eSAndroid Build Coastguard Worker {
971*ec779b8eSAndroid Build Coastguard Worker auto clientCallback = mSessionMap[sessionKey].callback.lock();
972*ec779b8eSAndroid Build Coastguard Worker if (clientCallback != nullptr) {
973*ec779b8eSAndroid Build Coastguard Worker clientCallback->onTranscodingFailed(sessionId, err);
974*ec779b8eSAndroid Build Coastguard Worker }
975*ec779b8eSAndroid Build Coastguard Worker }
976*ec779b8eSAndroid Build Coastguard Worker
977*ec779b8eSAndroid Build Coastguard Worker // Remove the session.
978*ec779b8eSAndroid Build Coastguard Worker removeSession_l(sessionKey, Session::ERROR);
979*ec779b8eSAndroid Build Coastguard Worker
980*ec779b8eSAndroid Build Coastguard Worker // Start next session.
981*ec779b8eSAndroid Build Coastguard Worker updateCurrentSession_l();
982*ec779b8eSAndroid Build Coastguard Worker
983*ec779b8eSAndroid Build Coastguard Worker validateState_l();
984*ec779b8eSAndroid Build Coastguard Worker });
985*ec779b8eSAndroid Build Coastguard Worker }
986*ec779b8eSAndroid Build Coastguard Worker
onProgressUpdate(ClientIdType clientId,SessionIdType sessionId,int32_t progress)987*ec779b8eSAndroid Build Coastguard Worker void TranscodingSessionController::onProgressUpdate(ClientIdType clientId, SessionIdType sessionId,
988*ec779b8eSAndroid Build Coastguard Worker int32_t progress) {
989*ec779b8eSAndroid Build Coastguard Worker notifyClient(clientId, sessionId, "progress", [=](const SessionKeyType& sessionKey) {
990*ec779b8eSAndroid Build Coastguard Worker auto callback = mSessionMap[sessionKey].callback.lock();
991*ec779b8eSAndroid Build Coastguard Worker if (callback != nullptr) {
992*ec779b8eSAndroid Build Coastguard Worker callback->onProgressUpdate(sessionId, progress);
993*ec779b8eSAndroid Build Coastguard Worker }
994*ec779b8eSAndroid Build Coastguard Worker mSessionMap[sessionKey].lastProgress = progress;
995*ec779b8eSAndroid Build Coastguard Worker });
996*ec779b8eSAndroid Build Coastguard Worker }
997*ec779b8eSAndroid Build Coastguard Worker
onHeartBeat(ClientIdType clientId,SessionIdType sessionId)998*ec779b8eSAndroid Build Coastguard Worker void TranscodingSessionController::onHeartBeat(ClientIdType clientId, SessionIdType sessionId) {
999*ec779b8eSAndroid Build Coastguard Worker notifyClient(clientId, sessionId, "heart-beat",
1000*ec779b8eSAndroid Build Coastguard Worker [=](const SessionKeyType& /*sessionKey*/) { mWatchdog->keepAlive(); });
1001*ec779b8eSAndroid Build Coastguard Worker }
1002*ec779b8eSAndroid Build Coastguard Worker
onResourceLost(ClientIdType clientId,SessionIdType sessionId)1003*ec779b8eSAndroid Build Coastguard Worker void TranscodingSessionController::onResourceLost(ClientIdType clientId, SessionIdType sessionId) {
1004*ec779b8eSAndroid Build Coastguard Worker ALOGI("%s", __FUNCTION__);
1005*ec779b8eSAndroid Build Coastguard Worker
1006*ec779b8eSAndroid Build Coastguard Worker notifyClient(clientId, sessionId, "resource_lost", [=](const SessionKeyType& sessionKey) {
1007*ec779b8eSAndroid Build Coastguard Worker if (mResourceLost) {
1008*ec779b8eSAndroid Build Coastguard Worker return;
1009*ec779b8eSAndroid Build Coastguard Worker }
1010*ec779b8eSAndroid Build Coastguard Worker
1011*ec779b8eSAndroid Build Coastguard Worker Session* resourceLostSession = &mSessionMap[sessionKey];
1012*ec779b8eSAndroid Build Coastguard Worker if (resourceLostSession->getState() != Session::RUNNING) {
1013*ec779b8eSAndroid Build Coastguard Worker ALOGW("session %s lost resource but is no longer running",
1014*ec779b8eSAndroid Build Coastguard Worker sessionToString(sessionKey).c_str());
1015*ec779b8eSAndroid Build Coastguard Worker return;
1016*ec779b8eSAndroid Build Coastguard Worker }
1017*ec779b8eSAndroid Build Coastguard Worker // If we receive a resource loss event, the transcoder already paused the transcoding,
1018*ec779b8eSAndroid Build Coastguard Worker // so we don't need to call onPaused() to pause it. However, we still need to notify
1019*ec779b8eSAndroid Build Coastguard Worker // the client and update the session state here.
1020*ec779b8eSAndroid Build Coastguard Worker setSessionState_l(resourceLostSession, Session::PAUSED);
1021*ec779b8eSAndroid Build Coastguard Worker // Notify the client as a paused event.
1022*ec779b8eSAndroid Build Coastguard Worker auto clientCallback = resourceLostSession->callback.lock();
1023*ec779b8eSAndroid Build Coastguard Worker if (clientCallback != nullptr) {
1024*ec779b8eSAndroid Build Coastguard Worker clientCallback->onTranscodingPaused(sessionKey.second);
1025*ec779b8eSAndroid Build Coastguard Worker }
1026*ec779b8eSAndroid Build Coastguard Worker if (mResourcePolicy != nullptr) {
1027*ec779b8eSAndroid Build Coastguard Worker mResourcePolicy->setPidResourceLost(resourceLostSession->request.clientPid);
1028*ec779b8eSAndroid Build Coastguard Worker }
1029*ec779b8eSAndroid Build Coastguard Worker mResourceLost = true;
1030*ec779b8eSAndroid Build Coastguard Worker
1031*ec779b8eSAndroid Build Coastguard Worker validateState_l();
1032*ec779b8eSAndroid Build Coastguard Worker });
1033*ec779b8eSAndroid Build Coastguard Worker }
1034*ec779b8eSAndroid Build Coastguard Worker
onTopUidsChanged(const std::unordered_set<uid_t> & uids)1035*ec779b8eSAndroid Build Coastguard Worker void TranscodingSessionController::onTopUidsChanged(const std::unordered_set<uid_t>& uids) {
1036*ec779b8eSAndroid Build Coastguard Worker if (uids.empty()) {
1037*ec779b8eSAndroid Build Coastguard Worker ALOGW("%s: ignoring empty uids", __FUNCTION__);
1038*ec779b8eSAndroid Build Coastguard Worker return;
1039*ec779b8eSAndroid Build Coastguard Worker }
1040*ec779b8eSAndroid Build Coastguard Worker
1041*ec779b8eSAndroid Build Coastguard Worker std::string uidStr;
1042*ec779b8eSAndroid Build Coastguard Worker for (auto it = uids.begin(); it != uids.end(); it++) {
1043*ec779b8eSAndroid Build Coastguard Worker if (!uidStr.empty()) {
1044*ec779b8eSAndroid Build Coastguard Worker uidStr += ", ";
1045*ec779b8eSAndroid Build Coastguard Worker }
1046*ec779b8eSAndroid Build Coastguard Worker uidStr += std::to_string(*it);
1047*ec779b8eSAndroid Build Coastguard Worker }
1048*ec779b8eSAndroid Build Coastguard Worker
1049*ec779b8eSAndroid Build Coastguard Worker ALOGD("%s: topUids: size %zu, uids: %s", __FUNCTION__, uids.size(), uidStr.c_str());
1050*ec779b8eSAndroid Build Coastguard Worker
1051*ec779b8eSAndroid Build Coastguard Worker std::scoped_lock lock{mLock};
1052*ec779b8eSAndroid Build Coastguard Worker
1053*ec779b8eSAndroid Build Coastguard Worker moveUidsToTop_l(uids, true /*preserveTopUid*/);
1054*ec779b8eSAndroid Build Coastguard Worker
1055*ec779b8eSAndroid Build Coastguard Worker updateCurrentSession_l();
1056*ec779b8eSAndroid Build Coastguard Worker
1057*ec779b8eSAndroid Build Coastguard Worker validateState_l();
1058*ec779b8eSAndroid Build Coastguard Worker }
1059*ec779b8eSAndroid Build Coastguard Worker
onUidGone(uid_t goneUid)1060*ec779b8eSAndroid Build Coastguard Worker void TranscodingSessionController::onUidGone(uid_t goneUid) {
1061*ec779b8eSAndroid Build Coastguard Worker ALOGD("%s: gone uid %u", __FUNCTION__, goneUid);
1062*ec779b8eSAndroid Build Coastguard Worker
1063*ec779b8eSAndroid Build Coastguard Worker std::list<SessionKeyType> sessionsToRemove, sessionsForOtherUids;
1064*ec779b8eSAndroid Build Coastguard Worker
1065*ec779b8eSAndroid Build Coastguard Worker std::scoped_lock lock{mLock};
1066*ec779b8eSAndroid Build Coastguard Worker
1067*ec779b8eSAndroid Build Coastguard Worker for (auto it = mSessionMap.begin(); it != mSessionMap.end(); ++it) {
1068*ec779b8eSAndroid Build Coastguard Worker if (it->second.allClientUids.count(goneUid) > 0) {
1069*ec779b8eSAndroid Build Coastguard Worker // If goneUid is the only uid, remove the session; otherwise, only
1070*ec779b8eSAndroid Build Coastguard Worker // remove the uid from the session.
1071*ec779b8eSAndroid Build Coastguard Worker if (it->second.allClientUids.size() > 1) {
1072*ec779b8eSAndroid Build Coastguard Worker sessionsForOtherUids.push_back(it->first);
1073*ec779b8eSAndroid Build Coastguard Worker } else {
1074*ec779b8eSAndroid Build Coastguard Worker sessionsToRemove.push_back(it->first);
1075*ec779b8eSAndroid Build Coastguard Worker }
1076*ec779b8eSAndroid Build Coastguard Worker }
1077*ec779b8eSAndroid Build Coastguard Worker }
1078*ec779b8eSAndroid Build Coastguard Worker
1079*ec779b8eSAndroid Build Coastguard Worker for (auto it = sessionsToRemove.begin(); it != sessionsToRemove.end(); ++it) {
1080*ec779b8eSAndroid Build Coastguard Worker // If the session has ever been started, stop it now.
1081*ec779b8eSAndroid Build Coastguard Worker // Note that stop() is needed even if the session is currently paused. This instructs
1082*ec779b8eSAndroid Build Coastguard Worker // the transcoder to discard any states for the session, otherwise the states may
1083*ec779b8eSAndroid Build Coastguard Worker // never be discarded.
1084*ec779b8eSAndroid Build Coastguard Worker if (mSessionMap[*it].getState() != Session::NOT_STARTED) {
1085*ec779b8eSAndroid Build Coastguard Worker mTranscoder->stop(it->first, it->second);
1086*ec779b8eSAndroid Build Coastguard Worker }
1087*ec779b8eSAndroid Build Coastguard Worker
1088*ec779b8eSAndroid Build Coastguard Worker {
1089*ec779b8eSAndroid Build Coastguard Worker auto clientCallback = mSessionMap[*it].callback.lock();
1090*ec779b8eSAndroid Build Coastguard Worker if (clientCallback != nullptr) {
1091*ec779b8eSAndroid Build Coastguard Worker clientCallback->onTranscodingFailed(it->second,
1092*ec779b8eSAndroid Build Coastguard Worker TranscodingErrorCode::kUidGoneCancelled);
1093*ec779b8eSAndroid Build Coastguard Worker }
1094*ec779b8eSAndroid Build Coastguard Worker }
1095*ec779b8eSAndroid Build Coastguard Worker
1096*ec779b8eSAndroid Build Coastguard Worker // Remove the session.
1097*ec779b8eSAndroid Build Coastguard Worker removeSession_l(*it, Session::CANCELED);
1098*ec779b8eSAndroid Build Coastguard Worker }
1099*ec779b8eSAndroid Build Coastguard Worker
1100*ec779b8eSAndroid Build Coastguard Worker auto keepUid = std::make_shared<std::function<bool(uid_t)>>(
1101*ec779b8eSAndroid Build Coastguard Worker [goneUid](uid_t uid) { return uid != goneUid; });
1102*ec779b8eSAndroid Build Coastguard Worker for (auto it = sessionsForOtherUids.begin(); it != sessionsForOtherUids.end(); ++it) {
1103*ec779b8eSAndroid Build Coastguard Worker removeSession_l(*it, Session::CANCELED, keepUid);
1104*ec779b8eSAndroid Build Coastguard Worker }
1105*ec779b8eSAndroid Build Coastguard Worker
1106*ec779b8eSAndroid Build Coastguard Worker // Start next session.
1107*ec779b8eSAndroid Build Coastguard Worker updateCurrentSession_l();
1108*ec779b8eSAndroid Build Coastguard Worker
1109*ec779b8eSAndroid Build Coastguard Worker validateState_l();
1110*ec779b8eSAndroid Build Coastguard Worker }
1111*ec779b8eSAndroid Build Coastguard Worker
onResourceAvailable()1112*ec779b8eSAndroid Build Coastguard Worker void TranscodingSessionController::onResourceAvailable() {
1113*ec779b8eSAndroid Build Coastguard Worker std::scoped_lock lock{mLock};
1114*ec779b8eSAndroid Build Coastguard Worker
1115*ec779b8eSAndroid Build Coastguard Worker if (!mResourceLost) {
1116*ec779b8eSAndroid Build Coastguard Worker return;
1117*ec779b8eSAndroid Build Coastguard Worker }
1118*ec779b8eSAndroid Build Coastguard Worker
1119*ec779b8eSAndroid Build Coastguard Worker ALOGI("%s", __FUNCTION__);
1120*ec779b8eSAndroid Build Coastguard Worker
1121*ec779b8eSAndroid Build Coastguard Worker mResourceLost = false;
1122*ec779b8eSAndroid Build Coastguard Worker updateCurrentSession_l();
1123*ec779b8eSAndroid Build Coastguard Worker
1124*ec779b8eSAndroid Build Coastguard Worker validateState_l();
1125*ec779b8eSAndroid Build Coastguard Worker }
1126*ec779b8eSAndroid Build Coastguard Worker
onThrottlingStarted()1127*ec779b8eSAndroid Build Coastguard Worker void TranscodingSessionController::onThrottlingStarted() {
1128*ec779b8eSAndroid Build Coastguard Worker std::scoped_lock lock{mLock};
1129*ec779b8eSAndroid Build Coastguard Worker
1130*ec779b8eSAndroid Build Coastguard Worker if (mThermalThrottling) {
1131*ec779b8eSAndroid Build Coastguard Worker return;
1132*ec779b8eSAndroid Build Coastguard Worker }
1133*ec779b8eSAndroid Build Coastguard Worker
1134*ec779b8eSAndroid Build Coastguard Worker ALOGI("%s", __FUNCTION__);
1135*ec779b8eSAndroid Build Coastguard Worker
1136*ec779b8eSAndroid Build Coastguard Worker mThermalThrottling = true;
1137*ec779b8eSAndroid Build Coastguard Worker updateCurrentSession_l();
1138*ec779b8eSAndroid Build Coastguard Worker
1139*ec779b8eSAndroid Build Coastguard Worker validateState_l();
1140*ec779b8eSAndroid Build Coastguard Worker }
1141*ec779b8eSAndroid Build Coastguard Worker
onThrottlingStopped()1142*ec779b8eSAndroid Build Coastguard Worker void TranscodingSessionController::onThrottlingStopped() {
1143*ec779b8eSAndroid Build Coastguard Worker std::scoped_lock lock{mLock};
1144*ec779b8eSAndroid Build Coastguard Worker
1145*ec779b8eSAndroid Build Coastguard Worker if (!mThermalThrottling) {
1146*ec779b8eSAndroid Build Coastguard Worker return;
1147*ec779b8eSAndroid Build Coastguard Worker }
1148*ec779b8eSAndroid Build Coastguard Worker
1149*ec779b8eSAndroid Build Coastguard Worker ALOGI("%s", __FUNCTION__);
1150*ec779b8eSAndroid Build Coastguard Worker
1151*ec779b8eSAndroid Build Coastguard Worker mThermalThrottling = false;
1152*ec779b8eSAndroid Build Coastguard Worker updateCurrentSession_l();
1153*ec779b8eSAndroid Build Coastguard Worker
1154*ec779b8eSAndroid Build Coastguard Worker validateState_l();
1155*ec779b8eSAndroid Build Coastguard Worker }
1156*ec779b8eSAndroid Build Coastguard Worker
validateState_l()1157*ec779b8eSAndroid Build Coastguard Worker void TranscodingSessionController::validateState_l() {
1158*ec779b8eSAndroid Build Coastguard Worker #ifdef VALIDATE_STATE
1159*ec779b8eSAndroid Build Coastguard Worker LOG_ALWAYS_FATAL_IF(mSessionQueues.count(OFFLINE_UID) != 1,
1160*ec779b8eSAndroid Build Coastguard Worker "mSessionQueues offline queue number is not 1");
1161*ec779b8eSAndroid Build Coastguard Worker LOG_ALWAYS_FATAL_IF(*mOfflineUidIterator != OFFLINE_UID,
1162*ec779b8eSAndroid Build Coastguard Worker "mOfflineUidIterator not pointing to offline uid");
1163*ec779b8eSAndroid Build Coastguard Worker LOG_ALWAYS_FATAL_IF(mUidSortedList.size() != mSessionQueues.size(),
1164*ec779b8eSAndroid Build Coastguard Worker "mUidSortedList and mSessionQueues size mismatch, %zu vs %zu",
1165*ec779b8eSAndroid Build Coastguard Worker mUidSortedList.size(), mSessionQueues.size());
1166*ec779b8eSAndroid Build Coastguard Worker
1167*ec779b8eSAndroid Build Coastguard Worker int32_t totalSessions = 0;
1168*ec779b8eSAndroid Build Coastguard Worker for (auto uid : mUidSortedList) {
1169*ec779b8eSAndroid Build Coastguard Worker LOG_ALWAYS_FATAL_IF(mSessionQueues.count(uid) != 1,
1170*ec779b8eSAndroid Build Coastguard Worker "mSessionQueues count for uid %d is not 1", uid);
1171*ec779b8eSAndroid Build Coastguard Worker for (auto& sessionKey : mSessionQueues[uid]) {
1172*ec779b8eSAndroid Build Coastguard Worker LOG_ALWAYS_FATAL_IF(mSessionMap.count(sessionKey) != 1,
1173*ec779b8eSAndroid Build Coastguard Worker "mSessions count for session %s is not 1",
1174*ec779b8eSAndroid Build Coastguard Worker sessionToString(sessionKey).c_str());
1175*ec779b8eSAndroid Build Coastguard Worker }
1176*ec779b8eSAndroid Build Coastguard Worker
1177*ec779b8eSAndroid Build Coastguard Worker totalSessions += mSessionQueues[uid].size();
1178*ec779b8eSAndroid Build Coastguard Worker }
1179*ec779b8eSAndroid Build Coastguard Worker int32_t totalSessionsAlternative = 0;
1180*ec779b8eSAndroid Build Coastguard Worker for (auto const& s : mSessionMap) {
1181*ec779b8eSAndroid Build Coastguard Worker totalSessionsAlternative += s.second.allClientUids.size();
1182*ec779b8eSAndroid Build Coastguard Worker }
1183*ec779b8eSAndroid Build Coastguard Worker LOG_ALWAYS_FATAL_IF(totalSessions != totalSessionsAlternative,
1184*ec779b8eSAndroid Build Coastguard Worker "session count (including dup) from mSessionQueues doesn't match that from "
1185*ec779b8eSAndroid Build Coastguard Worker "mSessionMap, %d vs %d",
1186*ec779b8eSAndroid Build Coastguard Worker totalSessions, totalSessionsAlternative);
1187*ec779b8eSAndroid Build Coastguard Worker #endif // VALIDATE_STATE
1188*ec779b8eSAndroid Build Coastguard Worker }
1189*ec779b8eSAndroid Build Coastguard Worker
1190*ec779b8eSAndroid Build Coastguard Worker } // namespace android
1191