Loading media/libmediatranscoding/TranscodingClientManager.cpp +61 −0 Original line number Diff line number Diff line Loading @@ -94,6 +94,12 @@ struct TranscodingClientManager::ClientImpl : public BnTranscodingClient { Status getSessionWithId(int32_t /*in_sessionId*/, TranscodingSessionParcel* /*out_session*/, bool* /*_aidl_return*/) override; Status addClientUid(int32_t /*in_sessionId*/, int32_t /*in_clientUid*/, bool* /*_aidl_return*/) override; Status getClientUids(int32_t /*in_sessionId*/, std::vector<int32_t>* /*out_clientUids*/, bool* /*_aidl_return*/) override; Status unregister() override; }; Loading Loading @@ -217,6 +223,61 @@ Status TranscodingClientManager::ClientImpl::getSessionWithId(int32_t in_session return Status::ok(); } Status TranscodingClientManager::ClientImpl::addClientUid(int32_t in_sessionId, int32_t in_clientUid, bool* _aidl_return) { *_aidl_return = false; std::shared_ptr<TranscodingClientManager> owner; if (mAbandoned || (owner = mOwner.lock()) == nullptr) { return Status::fromServiceSpecificError(IMediaTranscodingService::ERROR_DISCONNECTED); } if (in_sessionId < 0) { return Status::ok(); } int32_t callingPid = AIBinder_getCallingPid(); int32_t callingUid = AIBinder_getCallingUid(); // Check if we can trust clientUid. Only privilege caller could add uid to existing sessions. if (in_clientUid == IMediaTranscodingService::USE_CALLING_UID) { in_clientUid = callingUid; } else if (in_clientUid < 0) { return Status::ok(); } else if (in_clientUid != callingUid && !owner->isTrustedCaller(callingPid, callingUid)) { ALOGE("addClientUid rejected (clientUid %d) " "(don't trust callingUid %d)", in_clientUid, callingUid); return STATUS_ERROR_FMT(IMediaTranscodingService::ERROR_PERMISSION_DENIED, "addClientUid rejected (clientUid %d) " "(don't trust callingUid %d)", in_clientUid, callingUid); } *_aidl_return = owner->mSessionController->addClientUid(mClientId, in_sessionId, in_clientUid); return Status::ok(); } Status TranscodingClientManager::ClientImpl::getClientUids(int32_t in_sessionId, std::vector<int32_t>* out_clientUids, bool* _aidl_return) { *_aidl_return = false; std::shared_ptr<TranscodingClientManager> owner; if (mAbandoned || (owner = mOwner.lock()) == nullptr) { return Status::fromServiceSpecificError(IMediaTranscodingService::ERROR_DISCONNECTED); } if (in_sessionId < 0) { return Status::ok(); } *_aidl_return = owner->mSessionController->getClientUids(mClientId, in_sessionId, out_clientUids); return Status::ok(); } Status TranscodingClientManager::ClientImpl::unregister() { bool abandoned = mAbandoned.exchange(true); Loading media/libmediatranscoding/TranscodingSessionController.cpp +179 −68 Original line number Diff line number Diff line Loading @@ -193,8 +193,8 @@ struct TranscodingSessionController::Pacer { ~Pacer() = default; void onSessionCompleted(uid_t uid, std::chrono::microseconds runningTime); bool onSessionStarted(uid_t uid); void onSessionCompleted(uid_t uid, std::chrono::microseconds runningTime); private: // Threshold of time between finish/start below which a back-to-back start is counted. Loading @@ -205,26 +205,20 @@ private: int32_t mBurstTimeQuotaSec; struct UidHistoryEntry { std::chrono::steady_clock::time_point lastCompletedTime; bool sessionActive = false; int32_t burstCount = 0; std::chrono::steady_clock::duration burstDuration{0}; std::chrono::steady_clock::time_point lastCompletedTime; }; std::map<uid_t, UidHistoryEntry> mUidHistoryMap; }; void TranscodingSessionController::Pacer::onSessionCompleted( uid_t uid, std::chrono::microseconds runningTime) { if (mUidHistoryMap.find(uid) == mUidHistoryMap.end()) { mUidHistoryMap.emplace(uid, UidHistoryEntry{}); } mUidHistoryMap[uid].lastCompletedTime = std::chrono::steady_clock::now(); mUidHistoryMap[uid].burstCount++; mUidHistoryMap[uid].burstDuration += runningTime; } bool TranscodingSessionController::Pacer::onSessionStarted(uid_t uid) { // If uid doesn't exist, this uid has no completed sessions. Skip. // If uid doesn't exist, only insert the entry and mark session active. Skip quota checking. if (mUidHistoryMap.find(uid) == mUidHistoryMap.end()) { mUidHistoryMap.emplace(uid, UidHistoryEntry{}); mUidHistoryMap[uid].sessionActive = true; ALOGV("Pacer::onSessionStarted: uid %d: new", uid); return true; } Loading @@ -236,25 +230,43 @@ bool TranscodingSessionController::Pacer::onSessionStarted(uid_t uid) { std::chrono::steady_clock::now() - mUidHistoryMap[uid].lastCompletedTime; if (mUidHistoryMap[uid].burstCount >= mBurstCountQuota && mUidHistoryMap[uid].burstDuration >= std::chrono::seconds(mBurstTimeQuotaSec)) { ALOGW("Pacer: uid %d: over quota, burst count %d, time %lldms", uid, mUidHistoryMap[uid].burstCount, (long long)mUidHistoryMap[uid].burstDuration.count()); ALOGW("Pacer::onSessionStarted: uid %d: over quota, burst count %d, time %lldms", uid, mUidHistoryMap[uid].burstCount, (long long)mUidHistoryMap[uid].burstDuration.count() / 1000000); return false; } // If not over quota, allow the session, and reset as long as this is not too close // to previous completion. if (timeSinceLastComplete > std::chrono::milliseconds(mBurstThresholdMs)) { ALOGV("Pacer: uid %d: reset quota", uid); ALOGV("Pacer::onSessionStarted: uid %d: reset quota", uid); mUidHistoryMap[uid].burstCount = 0; mUidHistoryMap[uid].burstDuration = std::chrono::milliseconds(0); } else { ALOGV("Pacer: uid %d: burst count %d, time %lldms", uid, mUidHistoryMap[uid].burstCount, (long long)mUidHistoryMap[uid].burstDuration.count()); ALOGV("Pacer::onSessionStarted: uid %d: burst count %d, time %lldms", uid, mUidHistoryMap[uid].burstCount, (long long)mUidHistoryMap[uid].burstDuration.count() / 1000000); } mUidHistoryMap[uid].sessionActive = true; return true; } void TranscodingSessionController::Pacer::onSessionCompleted( uid_t uid, std::chrono::microseconds runningTime) { // Skip quota update if this uid missed the start. (Could happen if the uid is added via // addClientUid() after the session start.) if (mUidHistoryMap.find(uid) == mUidHistoryMap.end() || !mUidHistoryMap[uid].sessionActive) { ALOGV("Pacer::onSessionCompleted: uid %d: not started", uid); return; } ALOGV("Pacer::onSessionCompleted: uid %d: runningTime %lld", uid, runningTime.count() / 1000); mUidHistoryMap[uid].sessionActive = false; mUidHistoryMap[uid].burstCount++; mUidHistoryMap[uid].burstDuration += runningTime; mUidHistoryMap[uid].lastCompletedTime = std::chrono::steady_clock::now(); } /////////////////////////////////////////////////////////////////////////////// TranscodingSessionController::TranscodingSessionController( Loading Loading @@ -372,6 +384,14 @@ TranscodingSessionController::Session* TranscodingSessionController::getTopSessi } uid_t topUid = *mUidSortedList.begin(); // If the current session is running, and it's in the topUid's queue, let it continue // to run even if it's not the earliest in that uid's queue. // For example, uid(B) is added to a session while it's pending in uid(A)'s queue, then // B is brought to front which caused the session to run, then user switches back to A. if (mCurrentSession != nullptr && mCurrentSession->getState() == Session::RUNNING && mCurrentSession->allClientUids.count(topUid) > 0) { return mCurrentSession; } SessionKeyType topSessionKey = *mSessionQueues[topUid].begin(); return &mSessionMap[topSessionKey]; } Loading Loading @@ -427,7 +447,7 @@ void TranscodingSessionController::Session::setState(Session::State newState) { void TranscodingSessionController::updateCurrentSession_l() { Session* curSession = mCurrentSession; Session* topSession = getTopSession_l(); Session* topSession = nullptr; // Delayed init of transcoder and watchdog. if (mTranscoder == nullptr) { Loading Loading @@ -458,9 +478,18 @@ void TranscodingSessionController::updateCurrentSession_l() { // Otherwise, ensure topSession is running. if (topSession->getState() == Session::NOT_STARTED) { if (!mPacer->onSessionStarted(topSession->clientUid)) { // Unfortunately this uid is out of quota for new sessions. // Drop this sesion and try another one. // Check if at least one client has quota to start the session. bool keepForClient = false; for (uid_t uid : topSession->allClientUids) { if (mPacer->onSessionStarted(uid)) { keepForClient = true; // DO NOT break here, because book-keeping still needs to happen // for the other uids. } } if (!keepForClient) { // Unfortunately all uids requesting this session are out of quota. // Drop this session and try the next one. { auto clientCallback = mSessionMap[topSession->key].callback.lock(); if (clientCallback != nullptr) { Loading @@ -484,8 +513,34 @@ void TranscodingSessionController::updateCurrentSession_l() { mCurrentSession = topSession; } void TranscodingSessionController::addUidToSession_l(uid_t clientUid, const SessionKeyType& sessionKey) { // If it's an offline session, the queue was already added in constructor. // If it's a real-time sessions, check if a queue is already present for the uid, // and add a new queue if needed. if (clientUid != OFFLINE_UID) { if (mSessionQueues.count(clientUid) == 0) { mUidPolicy->registerMonitorUid(clientUid); if (mUidPolicy->isUidOnTop(clientUid)) { mUidSortedList.push_front(clientUid); } else { // Shouldn't be submitting real-time requests from non-top app, // put it in front of the offline queue. mUidSortedList.insert(mOfflineUidIterator, clientUid); } } else if (clientUid != *mUidSortedList.begin()) { if (mUidPolicy->isUidOnTop(clientUid)) { mUidSortedList.remove(clientUid); mUidSortedList.push_front(clientUid); } } } // Append this session to the uid's queue. mSessionQueues[clientUid].push_back(sessionKey); } void TranscodingSessionController::removeSession_l(const SessionKeyType& sessionKey, Session::State finalState) { Session::State finalState, bool keepForOffline) { ALOGV("%s: session %s", __FUNCTION__, sessionToString(sessionKey).c_str()); if (mSessionMap.count(sessionKey) == 0) { Loading @@ -494,13 +549,17 @@ void TranscodingSessionController::removeSession_l(const SessionKeyType& session } // Remove session from uid's queue. const uid_t uid = mSessionMap[sessionKey].clientUid; bool uidQueueRemoved = false; for (uid_t uid : mSessionMap[sessionKey].allClientUids) { if (keepForOffline && uid == OFFLINE_UID) { continue; } SessionQueueType& sessionQueue = mSessionQueues[uid]; auto it = std::find(sessionQueue.begin(), sessionQueue.end(), sessionKey); if (it == sessionQueue.end()) { ALOGE("couldn't find session %s in queue for uid %d", sessionToString(sessionKey).c_str(), uid); return; ALOGW("couldn't find session %s in queue for uid %d", sessionToString(sessionKey).c_str(), uid); continue; } sessionQueue.erase(it); Loading @@ -510,10 +569,20 @@ void TranscodingSessionController::removeSession_l(const SessionKeyType& session mSessionQueues.erase(uid); mUidPolicy->unregisterMonitorUid(uid); uidQueueRemoved = true; } } if (uidQueueRemoved) { std::unordered_set<uid_t> topUids = mUidPolicy->getTopUids(); moveUidsToTop_l(topUids, false /*preserveTopUid*/); } if (keepForOffline) { mSessionMap[sessionKey].allClientUids = {OFFLINE_UID}; return; } // Clear current session. if (mCurrentSession == &mSessionMap[sessionKey]) { mCurrentSession = nullptr; Loading @@ -522,8 +591,9 @@ void TranscodingSessionController::removeSession_l(const SessionKeyType& session setSessionState_l(&mSessionMap[sessionKey], finalState); if (finalState == Session::FINISHED || finalState == Session::ERROR) { mPacer->onSessionCompleted(mSessionMap[sessionKey].clientUid, mSessionMap[sessionKey].runningTime); for (uid_t uid : mSessionMap[sessionKey].allClientUids) { mPacer->onSessionCompleted(uid, mSessionMap[sessionKey].runningTime); } } mSessionHistory.push_back(mSessionMap[sessionKey]); Loading Loading @@ -617,34 +687,13 @@ bool TranscodingSessionController::submit( // Add session to session map. mSessionMap[sessionKey].key = sessionKey; mSessionMap[sessionKey].clientUid = clientUid; mSessionMap[sessionKey].callingUid = callingUid; mSessionMap[sessionKey].allClientUids.insert(clientUid); mSessionMap[sessionKey].request = request; mSessionMap[sessionKey].callback = callback; setSessionState_l(&mSessionMap[sessionKey], Session::NOT_STARTED); // If it's an offline session, the queue was already added in constructor. // If it's a real-time sessions, check if a queue is already present for the uid, // and add a new queue if needed. if (clientUid != OFFLINE_UID) { if (mSessionQueues.count(clientUid) == 0) { mUidPolicy->registerMonitorUid(clientUid); if (mUidPolicy->isUidOnTop(clientUid)) { mUidSortedList.push_front(clientUid); } else { // Shouldn't be submitting real-time requests from non-top app, // put it in front of the offline queue. mUidSortedList.insert(mOfflineUidIterator, clientUid); } } else if (clientUid != *mUidSortedList.begin()) { if (mUidPolicy->isUidOnTop(clientUid)) { mUidSortedList.remove(clientUid); mUidSortedList.push_front(clientUid); } } } // Append this session to the uid's queue. mSessionQueues[clientUid].push_back(sessionKey); addUidToSession_l(clientUid, sessionKey); updateCurrentSession_l(); Loading @@ -657,16 +706,22 @@ bool TranscodingSessionController::cancel(ClientIdType clientId, SessionIdType s ALOGV("%s: session %s", __FUNCTION__, sessionToString(sessionKey).c_str()); std::list<SessionKeyType> sessionsToRemove; std::list<SessionKeyType> sessionsToRemove, sessionsForOffline; std::scoped_lock lock{mLock}; if (sessionId < 0) { for (auto it = mSessionMap.begin(); it != mSessionMap.end(); ++it) { if (it->first.first == clientId && it->second.clientUid != OFFLINE_UID) { if (it->first.first == clientId) { // If there is offline request, only keep the offline client; // otherwise remove the session. if (it->second.allClientUids.count(OFFLINE_UID) > 0) { sessionsForOffline.push_back(it->first); } else { sessionsToRemove.push_back(it->first); } } } } else { if (mSessionMap.count(sessionKey) == 0) { ALOGE("session %s doesn't exist", sessionToString(sessionKey).c_str()); Loading @@ -688,6 +743,10 @@ bool TranscodingSessionController::cancel(ClientIdType clientId, SessionIdType s removeSession_l(*it, Session::CANCELED); } for (auto it = sessionsForOffline.begin(); it != sessionsForOffline.end(); ++it) { removeSession_l(*it, Session::CANCELED, true /*keepForOffline*/); } // Start next session. updateCurrentSession_l(); Loading @@ -695,6 +754,51 @@ bool TranscodingSessionController::cancel(ClientIdType clientId, SessionIdType s return true; } bool TranscodingSessionController::addClientUid(ClientIdType clientId, SessionIdType sessionId, uid_t clientUid) { SessionKeyType sessionKey = std::make_pair(clientId, sessionId); std::scoped_lock lock{mLock}; if (mSessionMap.count(sessionKey) == 0) { ALOGE("session %s doesn't exist", sessionToString(sessionKey).c_str()); return false; } if (mSessionMap[sessionKey].allClientUids.count(clientUid) > 0) { ALOGE("session %s already has uid %d", sessionToString(sessionKey).c_str(), clientUid); return false; } mSessionMap[sessionKey].allClientUids.insert(clientUid); addUidToSession_l(clientUid, sessionKey); updateCurrentSession_l(); validateState_l(); return true; } bool TranscodingSessionController::getClientUids(ClientIdType clientId, SessionIdType sessionId, std::vector<int32_t>* out_clientUids) { SessionKeyType sessionKey = std::make_pair(clientId, sessionId); std::scoped_lock lock{mLock}; if (mSessionMap.count(sessionKey) == 0) { ALOGE("session %s doesn't exist", sessionToString(sessionKey).c_str()); return false; } out_clientUids->clear(); for (uid_t uid : mSessionMap[sessionKey].allClientUids) { if (uid != OFFLINE_UID) { out_clientUids->push_back(uid); } } return true; } bool TranscodingSessionController::getSession(ClientIdType clientId, SessionIdType sessionId, TranscodingRequestParcel* request) { SessionKeyType sessionKey = std::make_pair(clientId, sessionId); Loading Loading @@ -938,7 +1042,8 @@ void TranscodingSessionController::validateState_l() { LOG_ALWAYS_FATAL_IF(*mOfflineUidIterator != OFFLINE_UID, "mOfflineUidIterator not pointing to offline uid"); LOG_ALWAYS_FATAL_IF(mUidSortedList.size() != mSessionQueues.size(), "mUidList and mSessionQueues size mismatch"); "mUidSortedList and mSessionQueues size mismatch, %zu vs %zu", mUidSortedList.size(), mSessionQueues.size()); int32_t totalSessions = 0; for (auto uid : mUidSortedList) { Loading @@ -952,8 +1057,14 @@ void TranscodingSessionController::validateState_l() { totalSessions += mSessionQueues[uid].size(); } LOG_ALWAYS_FATAL_IF(mSessionMap.size() != totalSessions, "mSessions size doesn't match total sessions counted from uid queues"); int32_t totalSessionsAlternative = 0; for (auto const& s : mSessionMap) { totalSessionsAlternative += s.second.allClientUids.size(); } LOG_ALWAYS_FATAL_IF(totalSessions != totalSessionsAlternative, "session count (including dup) from mSessionQueues doesn't match that from " "mSessionMap, %d vs %d", totalSessions, totalSessionsAlternative); #endif // VALIDATE_STATE } Loading media/libmediatranscoding/aidl/android/media/ITranscodingClient.aidl +25 −0 Original line number Diff line number Diff line Loading @@ -54,6 +54,31 @@ interface ITranscodingClient { */ boolean getSessionWithId(in int sessionId, out TranscodingSessionParcel session); /** * Add an additional client uid requesting a session. * * @sessionId the session id to which to add the additional client uid. * @clientUid the additional client uid to be added. * @return false if the session doesn't exist or the client is already requesting the * session, true otherwise. */ boolean addClientUid(in int sessionId, int clientUid); /** * Retrieves the (unsorted) list of all clients requesting a session. * * Note that if a session was submitted with offline priority ( * TranscodingSessionPriority::kUnspecified), it initially will not be considered requested * by any particular client, because the client could go away any time after the submission. * However, additional uids could be added via addClientUid() after the submission, which * essentially make the request a real-time request instead of an offline request. * * @sessionId the session id for which to retrieve the client uid list. * @clientUids array to hold the retrieved client uid list. * @return false if the session doesn't exist, true otherwise. */ boolean getClientUids(in int sessionId, out int[] clientUids); /** * Unregister the client with the MediaTranscodingService. * Loading media/libmediatranscoding/include/media/ControllerClientInterface.h +23 −0 Original line number Diff line number Diff line Loading @@ -60,6 +60,29 @@ public: virtual bool getSession(ClientIdType clientId, SessionIdType sessionId, TranscodingRequestParcel* request) = 0; /** * Add an additional client uid requesting the session identified by <clientId, sessionId>. * * Returns false if the session doesn't exist, or the client is already requesting the * session. Returns true otherwise. */ virtual bool addClientUid(ClientIdType clientId, SessionIdType sessionId, uid_t clientUid); /** * Retrieves the (unsorted) list of all clients requesting the session identified by * <clientId, sessionId>. * * Note that if a session was submitted with offline priority ( * TranscodingSessionPriority::kUnspecified), it initially will not be considered requested * by any particular client, because the client could go away any time after the submission. * However, additional uids could be added via addClientUid() after the submission, which * essentially make the request a real-time request instead of an offline request. * * Returns false if the session doesn't exist. Returns true otherwise. */ virtual bool getClientUids(ClientIdType clientId, SessionIdType sessionId, std::vector<int32_t>* out_clientUids); protected: virtual ~ControllerClientInterface() = default; }; Loading media/libmediatranscoding/include/media/TranscodingSessionController.h +7 −2 Original line number Diff line number Diff line Loading @@ -54,6 +54,9 @@ public: bool cancel(ClientIdType clientId, SessionIdType sessionId) override; bool getSession(ClientIdType clientId, SessionIdType sessionId, TranscodingRequestParcel* request) override; bool addClientUid(ClientIdType clientId, SessionIdType sessionId, uid_t clientUid) override; bool getClientUids(ClientIdType clientId, SessionIdType sessionId, std::vector<int32_t>* out_clientUids) override; // ~ControllerClientInterface // TranscoderCallbackInterface Loading Loading @@ -120,8 +123,8 @@ private: DROPPED_BY_PACER, }; SessionKeyType key; uid_t clientUid; uid_t callingUid; std::unordered_set<uid_t> allClientUids; int32_t lastProgress = 0; int32_t pauseCount = 0; std::chrono::time_point<std::chrono::steady_clock> stateEnterTime; Loading Loading @@ -184,7 +187,9 @@ private: void dumpSession_l(const Session& session, String8& result, bool closedSession = false); Session* getTopSession_l(); void updateCurrentSession_l(); void removeSession_l(const SessionKeyType& sessionKey, Session::State finalState); void addUidToSession_l(uid_t uid, const SessionKeyType& sessionKey); void removeSession_l(const SessionKeyType& sessionKey, Session::State finalState, bool keepForOffline = false); void moveUidsToTop_l(const std::unordered_set<uid_t>& uids, bool preserveTopUid); void setSessionState_l(Session* session, Session::State state); void notifyClient(ClientIdType clientId, SessionIdType sessionId, const char* reason, Loading Loading
media/libmediatranscoding/TranscodingClientManager.cpp +61 −0 Original line number Diff line number Diff line Loading @@ -94,6 +94,12 @@ struct TranscodingClientManager::ClientImpl : public BnTranscodingClient { Status getSessionWithId(int32_t /*in_sessionId*/, TranscodingSessionParcel* /*out_session*/, bool* /*_aidl_return*/) override; Status addClientUid(int32_t /*in_sessionId*/, int32_t /*in_clientUid*/, bool* /*_aidl_return*/) override; Status getClientUids(int32_t /*in_sessionId*/, std::vector<int32_t>* /*out_clientUids*/, bool* /*_aidl_return*/) override; Status unregister() override; }; Loading Loading @@ -217,6 +223,61 @@ Status TranscodingClientManager::ClientImpl::getSessionWithId(int32_t in_session return Status::ok(); } Status TranscodingClientManager::ClientImpl::addClientUid(int32_t in_sessionId, int32_t in_clientUid, bool* _aidl_return) { *_aidl_return = false; std::shared_ptr<TranscodingClientManager> owner; if (mAbandoned || (owner = mOwner.lock()) == nullptr) { return Status::fromServiceSpecificError(IMediaTranscodingService::ERROR_DISCONNECTED); } if (in_sessionId < 0) { return Status::ok(); } int32_t callingPid = AIBinder_getCallingPid(); int32_t callingUid = AIBinder_getCallingUid(); // Check if we can trust clientUid. Only privilege caller could add uid to existing sessions. if (in_clientUid == IMediaTranscodingService::USE_CALLING_UID) { in_clientUid = callingUid; } else if (in_clientUid < 0) { return Status::ok(); } else if (in_clientUid != callingUid && !owner->isTrustedCaller(callingPid, callingUid)) { ALOGE("addClientUid rejected (clientUid %d) " "(don't trust callingUid %d)", in_clientUid, callingUid); return STATUS_ERROR_FMT(IMediaTranscodingService::ERROR_PERMISSION_DENIED, "addClientUid rejected (clientUid %d) " "(don't trust callingUid %d)", in_clientUid, callingUid); } *_aidl_return = owner->mSessionController->addClientUid(mClientId, in_sessionId, in_clientUid); return Status::ok(); } Status TranscodingClientManager::ClientImpl::getClientUids(int32_t in_sessionId, std::vector<int32_t>* out_clientUids, bool* _aidl_return) { *_aidl_return = false; std::shared_ptr<TranscodingClientManager> owner; if (mAbandoned || (owner = mOwner.lock()) == nullptr) { return Status::fromServiceSpecificError(IMediaTranscodingService::ERROR_DISCONNECTED); } if (in_sessionId < 0) { return Status::ok(); } *_aidl_return = owner->mSessionController->getClientUids(mClientId, in_sessionId, out_clientUids); return Status::ok(); } Status TranscodingClientManager::ClientImpl::unregister() { bool abandoned = mAbandoned.exchange(true); Loading
media/libmediatranscoding/TranscodingSessionController.cpp +179 −68 Original line number Diff line number Diff line Loading @@ -193,8 +193,8 @@ struct TranscodingSessionController::Pacer { ~Pacer() = default; void onSessionCompleted(uid_t uid, std::chrono::microseconds runningTime); bool onSessionStarted(uid_t uid); void onSessionCompleted(uid_t uid, std::chrono::microseconds runningTime); private: // Threshold of time between finish/start below which a back-to-back start is counted. Loading @@ -205,26 +205,20 @@ private: int32_t mBurstTimeQuotaSec; struct UidHistoryEntry { std::chrono::steady_clock::time_point lastCompletedTime; bool sessionActive = false; int32_t burstCount = 0; std::chrono::steady_clock::duration burstDuration{0}; std::chrono::steady_clock::time_point lastCompletedTime; }; std::map<uid_t, UidHistoryEntry> mUidHistoryMap; }; void TranscodingSessionController::Pacer::onSessionCompleted( uid_t uid, std::chrono::microseconds runningTime) { if (mUidHistoryMap.find(uid) == mUidHistoryMap.end()) { mUidHistoryMap.emplace(uid, UidHistoryEntry{}); } mUidHistoryMap[uid].lastCompletedTime = std::chrono::steady_clock::now(); mUidHistoryMap[uid].burstCount++; mUidHistoryMap[uid].burstDuration += runningTime; } bool TranscodingSessionController::Pacer::onSessionStarted(uid_t uid) { // If uid doesn't exist, this uid has no completed sessions. Skip. // If uid doesn't exist, only insert the entry and mark session active. Skip quota checking. if (mUidHistoryMap.find(uid) == mUidHistoryMap.end()) { mUidHistoryMap.emplace(uid, UidHistoryEntry{}); mUidHistoryMap[uid].sessionActive = true; ALOGV("Pacer::onSessionStarted: uid %d: new", uid); return true; } Loading @@ -236,25 +230,43 @@ bool TranscodingSessionController::Pacer::onSessionStarted(uid_t uid) { std::chrono::steady_clock::now() - mUidHistoryMap[uid].lastCompletedTime; if (mUidHistoryMap[uid].burstCount >= mBurstCountQuota && mUidHistoryMap[uid].burstDuration >= std::chrono::seconds(mBurstTimeQuotaSec)) { ALOGW("Pacer: uid %d: over quota, burst count %d, time %lldms", uid, mUidHistoryMap[uid].burstCount, (long long)mUidHistoryMap[uid].burstDuration.count()); ALOGW("Pacer::onSessionStarted: uid %d: over quota, burst count %d, time %lldms", uid, mUidHistoryMap[uid].burstCount, (long long)mUidHistoryMap[uid].burstDuration.count() / 1000000); return false; } // If not over quota, allow the session, and reset as long as this is not too close // to previous completion. if (timeSinceLastComplete > std::chrono::milliseconds(mBurstThresholdMs)) { ALOGV("Pacer: uid %d: reset quota", uid); ALOGV("Pacer::onSessionStarted: uid %d: reset quota", uid); mUidHistoryMap[uid].burstCount = 0; mUidHistoryMap[uid].burstDuration = std::chrono::milliseconds(0); } else { ALOGV("Pacer: uid %d: burst count %d, time %lldms", uid, mUidHistoryMap[uid].burstCount, (long long)mUidHistoryMap[uid].burstDuration.count()); ALOGV("Pacer::onSessionStarted: uid %d: burst count %d, time %lldms", uid, mUidHistoryMap[uid].burstCount, (long long)mUidHistoryMap[uid].burstDuration.count() / 1000000); } mUidHistoryMap[uid].sessionActive = true; return true; } void TranscodingSessionController::Pacer::onSessionCompleted( uid_t uid, std::chrono::microseconds runningTime) { // Skip quota update if this uid missed the start. (Could happen if the uid is added via // addClientUid() after the session start.) if (mUidHistoryMap.find(uid) == mUidHistoryMap.end() || !mUidHistoryMap[uid].sessionActive) { ALOGV("Pacer::onSessionCompleted: uid %d: not started", uid); return; } ALOGV("Pacer::onSessionCompleted: uid %d: runningTime %lld", uid, runningTime.count() / 1000); mUidHistoryMap[uid].sessionActive = false; mUidHistoryMap[uid].burstCount++; mUidHistoryMap[uid].burstDuration += runningTime; mUidHistoryMap[uid].lastCompletedTime = std::chrono::steady_clock::now(); } /////////////////////////////////////////////////////////////////////////////// TranscodingSessionController::TranscodingSessionController( Loading Loading @@ -372,6 +384,14 @@ TranscodingSessionController::Session* TranscodingSessionController::getTopSessi } uid_t topUid = *mUidSortedList.begin(); // If the current session is running, and it's in the topUid's queue, let it continue // to run even if it's not the earliest in that uid's queue. // For example, uid(B) is added to a session while it's pending in uid(A)'s queue, then // B is brought to front which caused the session to run, then user switches back to A. if (mCurrentSession != nullptr && mCurrentSession->getState() == Session::RUNNING && mCurrentSession->allClientUids.count(topUid) > 0) { return mCurrentSession; } SessionKeyType topSessionKey = *mSessionQueues[topUid].begin(); return &mSessionMap[topSessionKey]; } Loading Loading @@ -427,7 +447,7 @@ void TranscodingSessionController::Session::setState(Session::State newState) { void TranscodingSessionController::updateCurrentSession_l() { Session* curSession = mCurrentSession; Session* topSession = getTopSession_l(); Session* topSession = nullptr; // Delayed init of transcoder and watchdog. if (mTranscoder == nullptr) { Loading Loading @@ -458,9 +478,18 @@ void TranscodingSessionController::updateCurrentSession_l() { // Otherwise, ensure topSession is running. if (topSession->getState() == Session::NOT_STARTED) { if (!mPacer->onSessionStarted(topSession->clientUid)) { // Unfortunately this uid is out of quota for new sessions. // Drop this sesion and try another one. // Check if at least one client has quota to start the session. bool keepForClient = false; for (uid_t uid : topSession->allClientUids) { if (mPacer->onSessionStarted(uid)) { keepForClient = true; // DO NOT break here, because book-keeping still needs to happen // for the other uids. } } if (!keepForClient) { // Unfortunately all uids requesting this session are out of quota. // Drop this session and try the next one. { auto clientCallback = mSessionMap[topSession->key].callback.lock(); if (clientCallback != nullptr) { Loading @@ -484,8 +513,34 @@ void TranscodingSessionController::updateCurrentSession_l() { mCurrentSession = topSession; } void TranscodingSessionController::addUidToSession_l(uid_t clientUid, const SessionKeyType& sessionKey) { // If it's an offline session, the queue was already added in constructor. // If it's a real-time sessions, check if a queue is already present for the uid, // and add a new queue if needed. if (clientUid != OFFLINE_UID) { if (mSessionQueues.count(clientUid) == 0) { mUidPolicy->registerMonitorUid(clientUid); if (mUidPolicy->isUidOnTop(clientUid)) { mUidSortedList.push_front(clientUid); } else { // Shouldn't be submitting real-time requests from non-top app, // put it in front of the offline queue. mUidSortedList.insert(mOfflineUidIterator, clientUid); } } else if (clientUid != *mUidSortedList.begin()) { if (mUidPolicy->isUidOnTop(clientUid)) { mUidSortedList.remove(clientUid); mUidSortedList.push_front(clientUid); } } } // Append this session to the uid's queue. mSessionQueues[clientUid].push_back(sessionKey); } void TranscodingSessionController::removeSession_l(const SessionKeyType& sessionKey, Session::State finalState) { Session::State finalState, bool keepForOffline) { ALOGV("%s: session %s", __FUNCTION__, sessionToString(sessionKey).c_str()); if (mSessionMap.count(sessionKey) == 0) { Loading @@ -494,13 +549,17 @@ void TranscodingSessionController::removeSession_l(const SessionKeyType& session } // Remove session from uid's queue. const uid_t uid = mSessionMap[sessionKey].clientUid; bool uidQueueRemoved = false; for (uid_t uid : mSessionMap[sessionKey].allClientUids) { if (keepForOffline && uid == OFFLINE_UID) { continue; } SessionQueueType& sessionQueue = mSessionQueues[uid]; auto it = std::find(sessionQueue.begin(), sessionQueue.end(), sessionKey); if (it == sessionQueue.end()) { ALOGE("couldn't find session %s in queue for uid %d", sessionToString(sessionKey).c_str(), uid); return; ALOGW("couldn't find session %s in queue for uid %d", sessionToString(sessionKey).c_str(), uid); continue; } sessionQueue.erase(it); Loading @@ -510,10 +569,20 @@ void TranscodingSessionController::removeSession_l(const SessionKeyType& session mSessionQueues.erase(uid); mUidPolicy->unregisterMonitorUid(uid); uidQueueRemoved = true; } } if (uidQueueRemoved) { std::unordered_set<uid_t> topUids = mUidPolicy->getTopUids(); moveUidsToTop_l(topUids, false /*preserveTopUid*/); } if (keepForOffline) { mSessionMap[sessionKey].allClientUids = {OFFLINE_UID}; return; } // Clear current session. if (mCurrentSession == &mSessionMap[sessionKey]) { mCurrentSession = nullptr; Loading @@ -522,8 +591,9 @@ void TranscodingSessionController::removeSession_l(const SessionKeyType& session setSessionState_l(&mSessionMap[sessionKey], finalState); if (finalState == Session::FINISHED || finalState == Session::ERROR) { mPacer->onSessionCompleted(mSessionMap[sessionKey].clientUid, mSessionMap[sessionKey].runningTime); for (uid_t uid : mSessionMap[sessionKey].allClientUids) { mPacer->onSessionCompleted(uid, mSessionMap[sessionKey].runningTime); } } mSessionHistory.push_back(mSessionMap[sessionKey]); Loading Loading @@ -617,34 +687,13 @@ bool TranscodingSessionController::submit( // Add session to session map. mSessionMap[sessionKey].key = sessionKey; mSessionMap[sessionKey].clientUid = clientUid; mSessionMap[sessionKey].callingUid = callingUid; mSessionMap[sessionKey].allClientUids.insert(clientUid); mSessionMap[sessionKey].request = request; mSessionMap[sessionKey].callback = callback; setSessionState_l(&mSessionMap[sessionKey], Session::NOT_STARTED); // If it's an offline session, the queue was already added in constructor. // If it's a real-time sessions, check if a queue is already present for the uid, // and add a new queue if needed. if (clientUid != OFFLINE_UID) { if (mSessionQueues.count(clientUid) == 0) { mUidPolicy->registerMonitorUid(clientUid); if (mUidPolicy->isUidOnTop(clientUid)) { mUidSortedList.push_front(clientUid); } else { // Shouldn't be submitting real-time requests from non-top app, // put it in front of the offline queue. mUidSortedList.insert(mOfflineUidIterator, clientUid); } } else if (clientUid != *mUidSortedList.begin()) { if (mUidPolicy->isUidOnTop(clientUid)) { mUidSortedList.remove(clientUid); mUidSortedList.push_front(clientUid); } } } // Append this session to the uid's queue. mSessionQueues[clientUid].push_back(sessionKey); addUidToSession_l(clientUid, sessionKey); updateCurrentSession_l(); Loading @@ -657,16 +706,22 @@ bool TranscodingSessionController::cancel(ClientIdType clientId, SessionIdType s ALOGV("%s: session %s", __FUNCTION__, sessionToString(sessionKey).c_str()); std::list<SessionKeyType> sessionsToRemove; std::list<SessionKeyType> sessionsToRemove, sessionsForOffline; std::scoped_lock lock{mLock}; if (sessionId < 0) { for (auto it = mSessionMap.begin(); it != mSessionMap.end(); ++it) { if (it->first.first == clientId && it->second.clientUid != OFFLINE_UID) { if (it->first.first == clientId) { // If there is offline request, only keep the offline client; // otherwise remove the session. if (it->second.allClientUids.count(OFFLINE_UID) > 0) { sessionsForOffline.push_back(it->first); } else { sessionsToRemove.push_back(it->first); } } } } else { if (mSessionMap.count(sessionKey) == 0) { ALOGE("session %s doesn't exist", sessionToString(sessionKey).c_str()); Loading @@ -688,6 +743,10 @@ bool TranscodingSessionController::cancel(ClientIdType clientId, SessionIdType s removeSession_l(*it, Session::CANCELED); } for (auto it = sessionsForOffline.begin(); it != sessionsForOffline.end(); ++it) { removeSession_l(*it, Session::CANCELED, true /*keepForOffline*/); } // Start next session. updateCurrentSession_l(); Loading @@ -695,6 +754,51 @@ bool TranscodingSessionController::cancel(ClientIdType clientId, SessionIdType s return true; } bool TranscodingSessionController::addClientUid(ClientIdType clientId, SessionIdType sessionId, uid_t clientUid) { SessionKeyType sessionKey = std::make_pair(clientId, sessionId); std::scoped_lock lock{mLock}; if (mSessionMap.count(sessionKey) == 0) { ALOGE("session %s doesn't exist", sessionToString(sessionKey).c_str()); return false; } if (mSessionMap[sessionKey].allClientUids.count(clientUid) > 0) { ALOGE("session %s already has uid %d", sessionToString(sessionKey).c_str(), clientUid); return false; } mSessionMap[sessionKey].allClientUids.insert(clientUid); addUidToSession_l(clientUid, sessionKey); updateCurrentSession_l(); validateState_l(); return true; } bool TranscodingSessionController::getClientUids(ClientIdType clientId, SessionIdType sessionId, std::vector<int32_t>* out_clientUids) { SessionKeyType sessionKey = std::make_pair(clientId, sessionId); std::scoped_lock lock{mLock}; if (mSessionMap.count(sessionKey) == 0) { ALOGE("session %s doesn't exist", sessionToString(sessionKey).c_str()); return false; } out_clientUids->clear(); for (uid_t uid : mSessionMap[sessionKey].allClientUids) { if (uid != OFFLINE_UID) { out_clientUids->push_back(uid); } } return true; } bool TranscodingSessionController::getSession(ClientIdType clientId, SessionIdType sessionId, TranscodingRequestParcel* request) { SessionKeyType sessionKey = std::make_pair(clientId, sessionId); Loading Loading @@ -938,7 +1042,8 @@ void TranscodingSessionController::validateState_l() { LOG_ALWAYS_FATAL_IF(*mOfflineUidIterator != OFFLINE_UID, "mOfflineUidIterator not pointing to offline uid"); LOG_ALWAYS_FATAL_IF(mUidSortedList.size() != mSessionQueues.size(), "mUidList and mSessionQueues size mismatch"); "mUidSortedList and mSessionQueues size mismatch, %zu vs %zu", mUidSortedList.size(), mSessionQueues.size()); int32_t totalSessions = 0; for (auto uid : mUidSortedList) { Loading @@ -952,8 +1057,14 @@ void TranscodingSessionController::validateState_l() { totalSessions += mSessionQueues[uid].size(); } LOG_ALWAYS_FATAL_IF(mSessionMap.size() != totalSessions, "mSessions size doesn't match total sessions counted from uid queues"); int32_t totalSessionsAlternative = 0; for (auto const& s : mSessionMap) { totalSessionsAlternative += s.second.allClientUids.size(); } LOG_ALWAYS_FATAL_IF(totalSessions != totalSessionsAlternative, "session count (including dup) from mSessionQueues doesn't match that from " "mSessionMap, %d vs %d", totalSessions, totalSessionsAlternative); #endif // VALIDATE_STATE } Loading
media/libmediatranscoding/aidl/android/media/ITranscodingClient.aidl +25 −0 Original line number Diff line number Diff line Loading @@ -54,6 +54,31 @@ interface ITranscodingClient { */ boolean getSessionWithId(in int sessionId, out TranscodingSessionParcel session); /** * Add an additional client uid requesting a session. * * @sessionId the session id to which to add the additional client uid. * @clientUid the additional client uid to be added. * @return false if the session doesn't exist or the client is already requesting the * session, true otherwise. */ boolean addClientUid(in int sessionId, int clientUid); /** * Retrieves the (unsorted) list of all clients requesting a session. * * Note that if a session was submitted with offline priority ( * TranscodingSessionPriority::kUnspecified), it initially will not be considered requested * by any particular client, because the client could go away any time after the submission. * However, additional uids could be added via addClientUid() after the submission, which * essentially make the request a real-time request instead of an offline request. * * @sessionId the session id for which to retrieve the client uid list. * @clientUids array to hold the retrieved client uid list. * @return false if the session doesn't exist, true otherwise. */ boolean getClientUids(in int sessionId, out int[] clientUids); /** * Unregister the client with the MediaTranscodingService. * Loading
media/libmediatranscoding/include/media/ControllerClientInterface.h +23 −0 Original line number Diff line number Diff line Loading @@ -60,6 +60,29 @@ public: virtual bool getSession(ClientIdType clientId, SessionIdType sessionId, TranscodingRequestParcel* request) = 0; /** * Add an additional client uid requesting the session identified by <clientId, sessionId>. * * Returns false if the session doesn't exist, or the client is already requesting the * session. Returns true otherwise. */ virtual bool addClientUid(ClientIdType clientId, SessionIdType sessionId, uid_t clientUid); /** * Retrieves the (unsorted) list of all clients requesting the session identified by * <clientId, sessionId>. * * Note that if a session was submitted with offline priority ( * TranscodingSessionPriority::kUnspecified), it initially will not be considered requested * by any particular client, because the client could go away any time after the submission. * However, additional uids could be added via addClientUid() after the submission, which * essentially make the request a real-time request instead of an offline request. * * Returns false if the session doesn't exist. Returns true otherwise. */ virtual bool getClientUids(ClientIdType clientId, SessionIdType sessionId, std::vector<int32_t>* out_clientUids); protected: virtual ~ControllerClientInterface() = default; }; Loading
media/libmediatranscoding/include/media/TranscodingSessionController.h +7 −2 Original line number Diff line number Diff line Loading @@ -54,6 +54,9 @@ public: bool cancel(ClientIdType clientId, SessionIdType sessionId) override; bool getSession(ClientIdType clientId, SessionIdType sessionId, TranscodingRequestParcel* request) override; bool addClientUid(ClientIdType clientId, SessionIdType sessionId, uid_t clientUid) override; bool getClientUids(ClientIdType clientId, SessionIdType sessionId, std::vector<int32_t>* out_clientUids) override; // ~ControllerClientInterface // TranscoderCallbackInterface Loading Loading @@ -120,8 +123,8 @@ private: DROPPED_BY_PACER, }; SessionKeyType key; uid_t clientUid; uid_t callingUid; std::unordered_set<uid_t> allClientUids; int32_t lastProgress = 0; int32_t pauseCount = 0; std::chrono::time_point<std::chrono::steady_clock> stateEnterTime; Loading Loading @@ -184,7 +187,9 @@ private: void dumpSession_l(const Session& session, String8& result, bool closedSession = false); Session* getTopSession_l(); void updateCurrentSession_l(); void removeSession_l(const SessionKeyType& sessionKey, Session::State finalState); void addUidToSession_l(uid_t uid, const SessionKeyType& sessionKey); void removeSession_l(const SessionKeyType& sessionKey, Session::State finalState, bool keepForOffline = false); void moveUidsToTop_l(const std::unordered_set<uid_t>& uids, bool preserveTopUid); void setSessionState_l(Session* session, Session::State state); void notifyClient(ClientIdType clientId, SessionIdType sessionId, const char* reason, Loading