xref: /aosp_15_r20/frameworks/av/media/module/libmediatranscoding/TranscodingSessionController.cpp (revision ec779b8e0859a360c3d303172224686826e6e0e1)
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