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/services/mediatranscoding/MediaTranscodingService.cpp b/services/mediatranscoding/MediaTranscodingService.cpp
index 4433f33..8b64134 100644
--- a/services/mediatranscoding/MediaTranscodingService.cpp
+++ b/services/mediatranscoding/MediaTranscodingService.cpp
@@ -42,22 +42,49 @@
             errorCode,                                \
             String8::format("%s:%d: " errorString, __FUNCTION__, __LINE__, ##__VA_ARGS__))
 
-MediaTranscodingService::MediaTranscodingService(bool simulated)
+static constexpr int64_t kTranscoderHeartBeatIntervalUs = 1000000LL;
+
+MediaTranscodingService::MediaTranscodingService()
       : mUidPolicy(new TranscodingUidPolicy()),
         mResourcePolicy(new TranscodingResourcePolicy()),
         mThermalPolicy(new TranscodingThermalPolicy()),
         mLogger(new TranscodingLogger()) {
     ALOGV("MediaTranscodingService is created");
-    mSessionController.reset(new TranscodingSessionController(
-            [simulated, logger = mLogger](
-                    const std::shared_ptr<TranscoderCallbackInterface>& cb,
-                    int64_t heartBeatUs) -> std::shared_ptr<TranscoderInterface> {
-                if (simulated) {
-                    return std::make_shared<SimulatedTranscoder>(cb, heartBeatUs);
-                }
-                return std::make_shared<TranscoderWrapper>(cb, logger, heartBeatUs);
-            },
-            mUidPolicy, mResourcePolicy, mThermalPolicy));
+    bool simulated = property_get_bool("debug.transcoding.simulated_transcoder", false);
+    if (simulated) {
+        // Overrid default config params with shorter values for testing.
+        TranscodingSessionController::ControllerConfig config = {
+                .pacerBurstThresholdMs = 500,
+                .pacerBurstCountQuota = 10,
+                .pacerBurstTimeQuotaSeconds = 3,
+        };
+        mSessionController.reset(new TranscodingSessionController(
+                [](const std::shared_ptr<TranscoderCallbackInterface>& cb)
+                        -> std::shared_ptr<TranscoderInterface> {
+                    return std::make_shared<SimulatedTranscoder>(cb);
+                },
+                mUidPolicy, mResourcePolicy, mThermalPolicy, &config));
+    } else {
+        int32_t overrideBurstCountQuota =
+                property_get_int32("persist.transcoding.burst_count_quota", -1);
+        int32_t pacerBurstTimeQuotaSeconds =
+                property_get_int32("persist.transcoding.burst_time_quota_seconds", -1);
+        // Override default config params with properties if present.
+        TranscodingSessionController::ControllerConfig config;
+        if (overrideBurstCountQuota > 0) {
+            config.pacerBurstCountQuota = overrideBurstCountQuota;
+        }
+        if (pacerBurstTimeQuotaSeconds > 0) {
+            config.pacerBurstTimeQuotaSeconds = pacerBurstTimeQuotaSeconds;
+        }
+        mSessionController.reset(new TranscodingSessionController(
+                [logger = mLogger](const std::shared_ptr<TranscoderCallbackInterface>& cb)
+                        -> std::shared_ptr<TranscoderInterface> {
+                    return std::make_shared<TranscoderWrapper>(cb, logger,
+                                                               kTranscoderHeartBeatIntervalUs);
+                },
+                mUidPolicy, mResourcePolicy, mThermalPolicy, &config));
+    }
     mClientManager.reset(new TranscodingClientManager(mSessionController));
     mUidPolicy->setCallback(mSessionController);
     mResourcePolicy->setCallback(mSessionController);
@@ -103,8 +130,7 @@
 //static
 void MediaTranscodingService::instantiate() {
     std::shared_ptr<MediaTranscodingService> service =
-            ::ndk::SharedRefBase::make<MediaTranscodingService>(
-                    property_get_bool("debug.transcoding.simulated_transcoder", false));
+            ::ndk::SharedRefBase::make<MediaTranscodingService>();
     binder_status_t status =
             AServiceManager_addService(service->asBinder().get(), getServiceName());
     if (status != STATUS_OK) {
diff --git a/services/mediatranscoding/MediaTranscodingService.h b/services/mediatranscoding/MediaTranscodingService.h
index 9384641..12be131 100644
--- a/services/mediatranscoding/MediaTranscodingService.h
+++ b/services/mediatranscoding/MediaTranscodingService.h
@@ -40,7 +40,7 @@
     static constexpr int32_t kInvalidSessionId = -1;
     static constexpr int32_t kInvalidClientId = -1;
 
-    MediaTranscodingService(bool simulated);
+    MediaTranscodingService();
     virtual ~MediaTranscodingService();
 
     static void instantiate();
diff --git a/services/mediatranscoding/SimulatedTranscoder.cpp b/services/mediatranscoding/SimulatedTranscoder.cpp
index 0151b3d..e80dbc5 100644
--- a/services/mediatranscoding/SimulatedTranscoder.cpp
+++ b/services/mediatranscoding/SimulatedTranscoder.cpp
@@ -47,8 +47,7 @@
     return "(unknown)";
 }
 
-SimulatedTranscoder::SimulatedTranscoder(const std::shared_ptr<TranscoderCallbackInterface>& cb,
-                                         int64_t heartBeatUs __unused)
+SimulatedTranscoder::SimulatedTranscoder(const std::shared_ptr<TranscoderCallbackInterface>& cb)
       : mCallback(cb), mLooperReady(false) {
     ALOGV("SimulatedTranscoder CTOR: %p", this);
 }
@@ -132,7 +131,7 @@
 
 void SimulatedTranscoder::threadLoop() {
     bool running = false;
-    std::chrono::system_clock::time_point lastRunningTime;
+    std::chrono::steady_clock::time_point lastRunningTime;
     Event lastRunningEvent;
 
     std::unique_lock<std::mutex> lock(mLock);
@@ -164,8 +163,9 @@
                 // Advance last running time and remaining time. This is needed to guard
                 // against bad events (which will be ignored) or spurious wakeups, in that
                 // case we don't want to wait for the same time again.
-                auto now = std::chrono::system_clock::now();
-                mRemainingTimeMap[key] -= (now - lastRunningTime);
+                auto now = std::chrono::steady_clock::now();
+                mRemainingTimeMap[key] -= std::chrono::duration_cast<std::chrono::microseconds>(
+                        now - lastRunningTime);
                 lastRunningTime = now;
             }
         }
@@ -184,7 +184,7 @@
         SessionKeyType key = std::make_pair(event.clientId, event.sessionId);
         if (!running && (event.type == Event::Start || event.type == Event::Resume)) {
             running = true;
-            lastRunningTime = std::chrono::system_clock::now();
+            lastRunningTime = std::chrono::steady_clock::now();
             lastRunningEvent = event;
             ALOGV("%s: session {%lld, %d}: remaining time: %lld", __FUNCTION__,
                   (long long)event.clientId, event.sessionId,
@@ -195,7 +195,8 @@
             if (event.type == Event::Stop) {
                 mRemainingTimeMap.erase(key);
             } else {
-                mRemainingTimeMap[key] -= (std::chrono::system_clock::now() - lastRunningTime);
+                mRemainingTimeMap[key] -= std::chrono::duration_cast<std::chrono::microseconds>(
+                        std::chrono::steady_clock::now() - lastRunningTime);
             }
         } else {
             ALOGW("%s: discarding bad event: session {%lld, %d}: %s", __FUNCTION__,
diff --git a/services/mediatranscoding/SimulatedTranscoder.h b/services/mediatranscoding/SimulatedTranscoder.h
index 6990576..58e2e30 100644
--- a/services/mediatranscoding/SimulatedTranscoder.h
+++ b/services/mediatranscoding/SimulatedTranscoder.h
@@ -49,8 +49,7 @@
 
     static constexpr int64_t kSessionDurationUs = 1000000;
 
-    SimulatedTranscoder(const std::shared_ptr<TranscoderCallbackInterface>& cb,
-                        int64_t heartBeatUs);
+    SimulatedTranscoder(const std::shared_ptr<TranscoderCallbackInterface>& cb);
     ~SimulatedTranscoder();
 
     // TranscoderInterface
diff --git a/services/mediatranscoding/tests/MediaTranscodingServiceTestHelper.h b/services/mediatranscoding/tests/MediaTranscodingServiceTestHelper.h
index 5256a3f..3f7d8d6 100644
--- a/services/mediatranscoding/tests/MediaTranscodingServiceTestHelper.h
+++ b/services/mediatranscoding/tests/MediaTranscodingServiceTestHelper.h
@@ -208,7 +208,9 @@
         std::unique_lock lock(mLock);
 
         mEventQueue.push_back(event);
-        mLastErr = err;
+        if (err != TranscodingErrorCode::kNoError) {
+            mLastErrQueue.push_back(err);
+        }
         mCondition.notify_one();
     }
 
@@ -226,7 +228,12 @@
 
     TranscodingErrorCode getLastError() {
         std::unique_lock lock(mLock);
-        return mLastErr;
+        if (mLastErrQueue.empty()) {
+            return TranscodingErrorCode::kNoError;
+        }
+        TranscodingErrorCode err = mLastErrQueue.front();
+        mLastErrQueue.pop_front();
+        return err;
     }
 
 private:
@@ -234,7 +241,7 @@
     std::condition_variable mCondition;
     Event mPoppedEvent;
     std::list<Event> mEventQueue;
-    TranscodingErrorCode mLastErr;
+    std::list<TranscodingErrorCode> mLastErrQueue;
     int mUpdateCount = 0;
     int mLastProgress = -1;
 };
diff --git a/services/mediatranscoding/tests/mediatranscodingservice_simulated_tests.cpp b/services/mediatranscoding/tests/mediatranscodingservice_simulated_tests.cpp
index b8a6f76..c8994ac 100644
--- a/services/mediatranscoding/tests/mediatranscodingservice_simulated_tests.cpp
+++ b/services/mediatranscoding/tests/mediatranscodingservice_simulated_tests.cpp
@@ -54,6 +54,10 @@
 constexpr int64_t kPaddingUs = 1000000;
 constexpr int64_t kSessionWithPaddingUs = SimulatedTranscoder::kSessionDurationUs + kPaddingUs;
 constexpr int64_t kWatchdogTimeoutUs = 3000000;
+// Pacer settings used for simulated tests. Listed here for reference.
+constexpr int32_t kSimulatedPacerBurstThresholdMs = 500;
+//constexpr int32_t kSimulatedPacerBurstCountQuota = 10;
+//constexpr int32_t kSimulatedPacerBurstTimeQuotaSec = 3;
 
 constexpr const char* kClientOpPackageName = "TestClientPackage";
 
@@ -64,6 +68,25 @@
     virtual ~MediaTranscodingServiceSimulatedTest() {
         ALOGI("MediaTranscodingServiceResourceTest destroyed");
     }
+
+    void testPacerHelper(int numSubmits, int sessionDurationMs, int expectedSuccess) {
+        // Idle to clear out burst history.
+        usleep(kSimulatedPacerBurstThresholdMs * 2 * 1000);
+        for (int i = 0; i < numSubmits; i++) {
+            EXPECT_TRUE(mClient3->submit(i, "test_source_file_0", "test_destination_file_0",
+                                         TranscodingSessionPriority::kNormal, -1 /*bitrateBps*/,
+                                         -1 /*overridePid*/, -1 /*overrideUid*/,
+                                         sessionDurationMs));
+        }
+        for (int i = 0; i < expectedSuccess; i++) {
+            EXPECT_EQ(mClient3->pop(kPaddingUs), EventTracker::Start(CLIENT(3), i));
+            EXPECT_EQ(mClient3->pop(kSessionWithPaddingUs), EventTracker::Finished(CLIENT(3), i));
+        }
+        for (int i = expectedSuccess; i < numSubmits; i++) {
+            EXPECT_EQ(mClient3->pop(kPaddingUs), EventTracker::Failed(CLIENT(3), i));
+            EXPECT_EQ(mClient3->getLastError(), TranscodingErrorCode::kDroppedByService);
+        }
+    }
 };
 
 TEST_F(MediaTranscodingServiceSimulatedTest, TestRegisterNullClient) {
@@ -414,5 +437,36 @@
     ALOGD("TestTranscodingWatchdog finished.");
 }
 
+TEST_F(MediaTranscodingServiceSimulatedTest, TestTranscodingPacerOverCountQuotaOnly) {
+    ALOGD("TestTranscodingPacerOverCountQuotaOnly starting...");
+
+    registerMultipleClients();
+    testPacerHelper(12 /*numSubmits*/, 100 /*sessionDurationMs*/, 12 /*expectedSuccess*/);
+    unregisterMultipleClients();
+
+    ALOGD("TestTranscodingPacerOverCountQuotaOnly finished.");
+}
+
+TEST_F(MediaTranscodingServiceSimulatedTest, TestTranscodingPacerOverTimeQuotaOnly) {
+    ALOGD("TestTranscodingPacerOverTimeQuotaOnly starting...");
+
+    registerMultipleClients();
+    testPacerHelper(5 /*numSubmits*/, 1000 /*sessionDurationMs*/, 5 /*expectedSuccess*/);
+    unregisterMultipleClients();
+
+    ALOGD("TestTranscodingPacerOverTimeQuotaOnly finished.");
+}
+
+TEST_F(MediaTranscodingServiceSimulatedTest, TestTranscodingPacerOverQuota) {
+    ALOGD("TestTranscodingPacerOverQuota starting...");
+
+    registerMultipleClients();
+    testPacerHelper(12 /*numSubmits*/, 400 /*sessionDurationMs*/, 10 /*expectedSuccess*/);
+    unregisterMultipleClients();
+
+    // Idle to clear out burst history. Since we expect it to actually fail, wait for cooldown.
+    ALOGD("TestTranscodingPacerOverQuota finished.");
+}
+
 }  // namespace media
 }  // namespace android