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