transcoding: drop transcoding request when uid is gone

bug: 184181113
test: transcoding unit tests;
manually testing kill Yelp app during long transcoding,
verify in log that session is cancelled after app kill.

Change-Id: I721c1d4cbf8ae9d6ff328766e4b9fd725f86f8b8
diff --git a/media/libmediatranscoding/TranscodingSessionController.cpp b/media/libmediatranscoding/TranscodingSessionController.cpp
index 2518e70..68e2875 100644
--- a/media/libmediatranscoding/TranscodingSessionController.cpp
+++ b/media/libmediatranscoding/TranscodingSessionController.cpp
@@ -195,6 +195,7 @@
 
     bool onSessionStarted(uid_t uid);
     void onSessionCompleted(uid_t uid, std::chrono::microseconds runningTime);
+    void onSessionCancelled(uid_t uid);
 
 private:
     // Threshold of time between finish/start below which a back-to-back start is counted.
@@ -267,6 +268,18 @@
     mUidHistoryMap[uid].lastCompletedTime = std::chrono::steady_clock::now();
 }
 
+void TranscodingSessionController::Pacer::onSessionCancelled(uid_t uid) {
+    if (mUidHistoryMap.find(uid) == mUidHistoryMap.end()) {
+        ALOGV("Pacer::onSessionCancelled: uid %d: not present", uid);
+        return;
+    }
+    // This is only called if a uid is removed from a session (due to it being killed
+    // or the original submitting client was gone but session was kept for offline use).
+    // Since the uid is going to miss the onSessionCompleted(), we can't track this
+    // session, and have to check back at next onSessionStarted().
+    mUidHistoryMap[uid].sessionActive = false;
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 
 TranscodingSessionController::TranscodingSessionController(
@@ -539,8 +552,9 @@
     mSessionQueues[clientUid].push_back(sessionKey);
 }
 
-void TranscodingSessionController::removeSession_l(const SessionKeyType& sessionKey,
-                                                   Session::State finalState, bool keepForOffline) {
+void TranscodingSessionController::removeSession_l(
+        const SessionKeyType& sessionKey, Session::State finalState,
+        const std::shared_ptr<std::function<bool(uid_t uid)>>& keepUid) {
     ALOGV("%s: session %s", __FUNCTION__, sessionToString(sessionKey).c_str());
 
     if (mSessionMap.count(sessionKey) == 0) {
@@ -550,9 +564,17 @@
 
     // Remove session from uid's queue.
     bool uidQueueRemoved = false;
+    std::unordered_set<uid_t> remainingUids;
     for (uid_t uid : mSessionMap[sessionKey].allClientUids) {
-        if (keepForOffline && uid == OFFLINE_UID) {
-            continue;
+        if (keepUid != nullptr) {
+            if ((*keepUid)(uid)) {
+                remainingUids.insert(uid);
+                continue;
+            }
+            // If we have uids to keep, the session is not going to any final
+            // state we can't use onSessionCompleted as the running time will
+            // not be valid. Only notify pacer to stop tracking this session.
+            mPacer->onSessionCancelled(uid);
         }
         SessionQueueType& sessionQueue = mSessionQueues[uid];
         auto it = std::find(sessionQueue.begin(), sessionQueue.end(), sessionKey);
@@ -578,8 +600,8 @@
         moveUidsToTop_l(topUids, false /*preserveTopUid*/);
     }
 
-    if (keepForOffline) {
-        mSessionMap[sessionKey].allClientUids = {OFFLINE_UID};
+    if (keepUid != nullptr) {
+        mSessionMap[sessionKey].allClientUids = remainingUids;
         return;
     }
 
@@ -590,10 +612,10 @@
 
     setSessionState_l(&mSessionMap[sessionKey], finalState);
 
-    if (finalState == Session::FINISHED || finalState == Session::ERROR) {
-        for (uid_t uid : mSessionMap[sessionKey].allClientUids) {
-            mPacer->onSessionCompleted(uid, mSessionMap[sessionKey].runningTime);
-        }
+    // We can use onSessionCompleted() even for CANCELLED, because runningTime is
+    // now updated by setSessionState_l().
+    for (uid_t uid : mSessionMap[sessionKey].allClientUids) {
+        mPacer->onSessionCompleted(uid, mSessionMap[sessionKey].runningTime);
     }
 
     mSessionHistory.push_back(mSessionMap[sessionKey]);
@@ -743,8 +765,10 @@
         removeSession_l(*it, Session::CANCELED);
     }
 
+    auto keepUid = std::make_shared<std::function<bool(uid_t)>>(
+            [](uid_t uid) { return uid == OFFLINE_UID; });
     for (auto it = sessionsForOffline.begin(); it != sessionsForOffline.end(); ++it) {
-        removeSession_l(*it, Session::CANCELED, true /*keepForOffline*/);
+        removeSession_l(*it, Session::CANCELED, keepUid);
     }
 
     // Start next session.
@@ -990,6 +1014,58 @@
     validateState_l();
 }
 
+void TranscodingSessionController::onUidGone(uid_t goneUid) {
+    ALOGD("%s: gone uid %u", __FUNCTION__, goneUid);
+
+    std::list<SessionKeyType> sessionsToRemove, sessionsForOtherUids;
+
+    std::scoped_lock lock{mLock};
+
+    for (auto it = mSessionMap.begin(); it != mSessionMap.end(); ++it) {
+        if (it->second.allClientUids.count(goneUid) > 0) {
+            // If goneUid is the only uid, remove the session; otherwise, only
+            // remove the uid from the session.
+            if (it->second.allClientUids.size() > 1) {
+                sessionsForOtherUids.push_back(it->first);
+            } else {
+                sessionsToRemove.push_back(it->first);
+            }
+        }
+    }
+
+    for (auto it = sessionsToRemove.begin(); it != sessionsToRemove.end(); ++it) {
+        // If the session has ever been started, stop it now.
+        // Note that stop() is needed even if the session is currently paused. This instructs
+        // the transcoder to discard any states for the session, otherwise the states may
+        // never be discarded.
+        if (mSessionMap[*it].getState() != Session::NOT_STARTED) {
+            mTranscoder->stop(it->first, it->second);
+        }
+
+        {
+            auto clientCallback = mSessionMap[*it].callback.lock();
+            if (clientCallback != nullptr) {
+                clientCallback->onTranscodingFailed(it->second,
+                                                    TranscodingErrorCode::kUidGoneCancelled);
+            }
+        }
+
+        // Remove the session.
+        removeSession_l(*it, Session::CANCELED);
+    }
+
+    auto keepUid = std::make_shared<std::function<bool(uid_t)>>(
+            [goneUid](uid_t uid) { return uid != goneUid; });
+    for (auto it = sessionsForOtherUids.begin(); it != sessionsForOtherUids.end(); ++it) {
+        removeSession_l(*it, Session::CANCELED, keepUid);
+    }
+
+    // Start next session.
+    updateCurrentSession_l();
+
+    validateState_l();
+}
+
 void TranscodingSessionController::onResourceAvailable() {
     std::scoped_lock lock{mLock};