transcoding: drop transcoding request for bad actors

Drop requests for a uid if it submits more than X back-to-back requests
that lasted longer than Y seconds.

bug: 177631807
test: new and existing unit tests; CTS MediaTranscodeManagerTest;
Manual testing Yelp app (with shortened thresholds)

Change-Id: I88287be62190ee5bc815dedb2a62c54ed3c41824
diff --git a/media/libmediatranscoding/tests/TranscodingSessionController_tests.cpp b/media/libmediatranscoding/tests/TranscodingSessionController_tests.cpp
index 3cd9112..560d1fe 100644
--- a/media/libmediatranscoding/tests/TranscodingSessionController_tests.cpp
+++ b/media/libmediatranscoding/tests/TranscodingSessionController_tests.cpp
@@ -118,7 +118,7 @@
 
 class TestTranscoder : public TranscoderInterface {
 public:
-    TestTranscoder() : mLastError(TranscodingErrorCode::kUnknown), mGeneration(0) {}
+    TestTranscoder() : mGeneration(0) {}
     virtual ~TestTranscoder() {}
 
     // TranscoderInterface
@@ -152,19 +152,6 @@
         mGeneration++;
     }
 
-    TranscodingErrorCode getLastError() {
-        std::scoped_lock lock{mLock};
-        // Clear last error.
-        TranscodingErrorCode result = mLastError;
-        mLastError = TranscodingErrorCode::kNoError;
-        return result;
-    }
-
-    int32_t getGeneration() {
-        std::scoped_lock lock{mLock};
-        return mGeneration;
-    }
-
     struct Event {
         enum { NoEvent, Start, Pause, Resume, Stop, Finished, Failed, Abandon } type;
         ClientIdType clientId;
@@ -195,7 +182,7 @@
         // Error is sticky, non-error event will not erase it, only getLastError()
         // clears last error.
         if (err != TranscodingErrorCode::kNoError) {
-            mLastError = err;
+            mLastErrorQueue.push_back(err);
         }
         mCondition.notify_one();
     }
@@ -218,12 +205,27 @@
         return mPoppedEvent;
     }
 
+    TranscodingErrorCode getLastError() {
+        std::scoped_lock lock{mLock};
+        if (mLastErrorQueue.empty()) {
+            return TranscodingErrorCode::kNoError;
+        }
+        TranscodingErrorCode err = mLastErrorQueue.front();
+        mLastErrorQueue.pop_front();
+        return err;
+    }
+
+    int32_t getGeneration() {
+        std::scoped_lock lock{mLock};
+        return mGeneration;
+    }
+
 private:
     std::mutex mLock;
     std::condition_variable mCondition;
     Event mPoppedEvent;
     std::list<Event> mEventQueue;
-    TranscodingErrorCode mLastError;
+    std::list<TranscodingErrorCode> mLastErrorQueue;
     int32_t mGeneration;
 };
 
@@ -291,16 +293,21 @@
         mUidPolicy.reset(new TestUidPolicy());
         mResourcePolicy.reset(new TestResourcePolicy());
         mThermalPolicy.reset(new TestThermalPolicy());
+        // Overrid default burst params with shorter values for testing.
+        TranscodingSessionController::ControllerConfig config = {
+                .pacerBurstThresholdMs = 500,
+                .pacerBurstCountQuota = 10,
+                .pacerBurstTimeQuotaSeconds = 3,
+        };
         mController.reset(new TranscodingSessionController(
-                [this](const std::shared_ptr<TranscoderCallbackInterface>& /*cb*/,
-                       int64_t /*heartBeatIntervalUs*/) {
+                [this](const std::shared_ptr<TranscoderCallbackInterface>& /*cb*/) {
                     // Here we require that the SessionController clears out all its refcounts of
                     // the transcoder object when it calls create.
                     EXPECT_EQ(mTranscoder.use_count(), 1);
                     mTranscoder->onCreated();
                     return mTranscoder;
                 },
-                mUidPolicy, mResourcePolicy, mThermalPolicy));
+                mUidPolicy, mResourcePolicy, mThermalPolicy, &config));
         mUidPolicy->setCallback(mController);
 
         // Set priority only, ignore other fields for now.
@@ -328,6 +335,40 @@
         EXPECT_EQ(mTranscoder.use_count(), 2);
     }
 
+    void testPacerHelper(int numSubmits, int sessionDurationMs, int expectedSuccess,
+                         bool pauseLastSuccessSession = false) {
+        for (int i = 0; i < numSubmits; i++) {
+            mController->submit(CLIENT(0), SESSION(i), UID(0), UID(0),
+                                mRealtimeRequest, mClientCallback0);
+        }
+        for (int i = 0; i < expectedSuccess; i++) {
+            EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), SESSION(i)));
+            if ((i == expectedSuccess - 1) && pauseLastSuccessSession) {
+                // Insert a pause of 3 sec to the last success running session
+                mController->onThrottlingStarted();
+                EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Pause(CLIENT(0), SESSION(i)));
+                sleep(3);
+                mController->onThrottlingStopped();
+                EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Resume(CLIENT(0), SESSION(i)));
+            }
+            usleep(sessionDurationMs * 1000);
+            // Test half of Finish and half of Error, both should be counted as burst runs.
+            if (i & 1) {
+                mController->onFinish(CLIENT(0), SESSION(i));
+                EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Finished(CLIENT(0), SESSION(i)));
+            } else {
+                mController->onError(CLIENT(0), SESSION(i), TranscodingErrorCode::kUnknown);
+                EXPECT_EQ(mTranscoder->popEvent(100000),
+                          TestTranscoder::Failed(CLIENT(0), SESSION(i)));
+                EXPECT_EQ(mTranscoder->getLastError(), TranscodingErrorCode::kUnknown);
+            }
+        }
+        for (int i = expectedSuccess; i < numSubmits; i++) {
+            EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Failed(CLIENT(0), SESSION(i)));
+            EXPECT_EQ(mTranscoder->getLastError(), TranscodingErrorCode::kDroppedByService);
+        }
+    }
+
     std::shared_ptr<TestTranscoder> mTranscoder;
     std::shared_ptr<TestUidPolicy> mUidPolicy;
     std::shared_ptr<TestResourcePolicy> mResourcePolicy;
@@ -523,15 +564,18 @@
     // Should still be propagated to client, but shouldn't trigger any new start.
     mController->onError(CLIENT(0), SESSION(1), TranscodingErrorCode::kUnknown);
     EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Failed(CLIENT(0), SESSION(1)));
+    EXPECT_EQ(mTranscoder->getLastError(), TranscodingErrorCode::kUnknown);
 
     // Fail running real-time session, should start next real-time session in queue.
     mController->onError(CLIENT(1), SESSION(0), TranscodingErrorCode::kUnknown);
     EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Failed(CLIENT(1), SESSION(0)));
+    EXPECT_EQ(mTranscoder->getLastError(), TranscodingErrorCode::kUnknown);
     EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), SESSION(2)));
 
     // Fail running real-time session, should resume next session (offline session) in queue.
     mController->onError(CLIENT(0), SESSION(2), TranscodingErrorCode::kUnknown);
     EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Failed(CLIENT(0), SESSION(2)));
+    EXPECT_EQ(mTranscoder->getLastError(), TranscodingErrorCode::kUnknown);
     EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Resume(CLIENT(0), SESSION(0)));
 
     // Fail running offline session, and test error code propagation.
@@ -854,7 +898,7 @@
     EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Resume(CLIENT(2), SESSION(0)));
 }
 
-TEST_F(TranscodingSessionControllerTest, TestTranscoderWatchdogTimeout) {
+TEST_F(TranscodingSessionControllerTest, TestTranscoderWatchdogNoHeartbeat) {
     ALOGD("TestTranscoderWatchdogTimeout");
 
     // Submit session to CLIENT(0) in UID(0).
@@ -862,18 +906,24 @@
     mController->submit(CLIENT(0), SESSION(0), UID(0), UID(0), mRealtimeRequest, mClientCallback0);
     EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), SESSION(0)));
 
-    int32_t expectedGen = 2;
     // Test 1: If not sending keep-alive at all, timeout after 3 seconds.
-    expectTimeout(CLIENT(0), SESSION(0), expectedGen++);
+    expectTimeout(CLIENT(0), SESSION(0), 2);
+}
 
+TEST_F(TranscodingSessionControllerTest, TestTranscoderWatchdogHeartbeat) {
     // Test 2: No timeout as long as keep-alive coming; timeout after keep-alive stops.
     mController->submit(CLIENT(0), SESSION(1), UID(0), UID(0), mRealtimeRequest, mClientCallback0);
     EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), SESSION(1)));
+
     for (int i = 0; i < 5; i++) {
         EXPECT_EQ(mTranscoder->popEvent(1000000), TestTranscoder::NoEvent);
         mController->onHeartBeat(CLIENT(0), SESSION(1));
     }
-    expectTimeout(CLIENT(0), SESSION(1), expectedGen++);
+    expectTimeout(CLIENT(0), SESSION(1), 2);
+}
+
+TEST_F(TranscodingSessionControllerTest, TestTranscoderWatchdogDuringPause) {
+    int expectedGen = 2;
 
     // Test 3a: No timeout for paused session even if no keep-alive is sent.
     mController->submit(CLIENT(0), SESSION(2), UID(0), UID(0), mOfflineRequest, mClientCallback0);
@@ -902,4 +952,25 @@
     expectTimeout(CLIENT(0), SESSION(3), expectedGen++);
 }
 
+TEST_F(TranscodingSessionControllerTest, TestTranscoderPacerOverCountOnly) {
+    ALOGD("TestTranscoderPacerOverCountOnly");
+    testPacerHelper(12 /*numSubmits*/, 100 /*sessionDurationMs*/, 12 /*expectedSuccess*/);
+}
+
+TEST_F(TranscodingSessionControllerTest, TestTranscoderPacerOverTimeOnly) {
+    ALOGD("TestTranscoderPacerOverTimeOnly");
+    testPacerHelper(5 /*numSubmits*/, 1000 /*sessionDurationMs*/, 5 /*expectedSuccess*/);
+}
+
+TEST_F(TranscodingSessionControllerTest, TestTranscoderPacerOverQuota) {
+    ALOGD("TestTranscoderPacerOverQuota");
+    testPacerHelper(12 /*numSubmits*/, 400 /*sessionDurationMs*/, 10 /*expectedSuccess*/);
+}
+
+TEST_F(TranscodingSessionControllerTest, TestTranscoderPacerWithPause) {
+    ALOGD("TestTranscoderPacerDuringPause");
+    testPacerHelper(12 /*numSubmits*/, 400 /*sessionDurationMs*/, 10 /*expectedSuccess*/,
+                    true /*pauseLastSuccessSession*/);
+}
+
 }  // namespace android