Throttler for incidentd based on size putting into dropbox.

The incidentd will accumulate the total size put into dropbox and once
it exceeds a threshold (currently 20MB) daily, it will stop further
requests. It allows collection again 24 hours later.

Bug: 64219725
Test: atest incidentd_test and manually flashed incidentd and test.
Change-Id: Iea21fbae40d5d01108797b190231d73e74eff213
diff --git a/cmds/incidentd/Android.mk b/cmds/incidentd/Android.mk
index d2d24c8..6bdd9be 100644
--- a/cmds/incidentd/Android.mk
+++ b/cmds/incidentd/Android.mk
@@ -25,16 +25,7 @@
 
 LOCAL_MODULE := incidentd
 
-LOCAL_SRC_FILES := \
-        src/PrivacyBuffer.cpp \
-        src/FdBuffer.cpp \
-        src/IncidentService.cpp \
-        src/Privacy.cpp \
-        src/Reporter.cpp \
-        src/Section.cpp \
-        src/incidentd_util.cpp \
-        src/main.cpp \
-        src/report_directory.cpp
+LOCAL_SRC_FILES := $(call all-cpp-files-under, src) \
 
 LOCAL_CFLAGS += \
         -Wall -Werror -Wno-missing-field-initializers -Wno-unused-variable -Wunused-parameter
@@ -110,19 +101,15 @@
 
 LOCAL_C_INCLUDES += $(LOCAL_PATH)/src
 
-LOCAL_SRC_FILES := \
+LOCAL_SRC_FILES := $(call all-cpp-files-under, tests) \
     src/PrivacyBuffer.cpp \
     src/FdBuffer.cpp \
     src/Privacy.cpp \
     src/Reporter.cpp \
     src/Section.cpp \
+    src/Throttler.cpp \
     src/incidentd_util.cpp \
     src/report_directory.cpp \
-    tests/section_list.cpp \
-    tests/PrivacyBuffer_test.cpp \
-    tests/FdBuffer_test.cpp \
-    tests/Reporter_test.cpp \
-    tests/Section_test.cpp \
 
 LOCAL_STATIC_LIBRARIES := \
     libgmock \
diff --git a/cmds/incidentd/src/FdBuffer.cpp b/cmds/incidentd/src/FdBuffer.cpp
index 883924c8..db60794 100644
--- a/cmds/incidentd/src/FdBuffer.cpp
+++ b/cmds/incidentd/src/FdBuffer.cpp
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+#define DEBUG false
 #include "Log.h"
 
 #include "FdBuffer.h"
diff --git a/cmds/incidentd/src/IncidentService.cpp b/cmds/incidentd/src/IncidentService.cpp
index 9ae6240..28fb38a 100644
--- a/cmds/incidentd/src/IncidentService.cpp
+++ b/cmds/incidentd/src/IncidentService.cpp
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+#define DEBUG false
 #include "Log.h"
 
 #include "IncidentService.h"
@@ -38,9 +39,11 @@
 
 enum { WHAT_RUN_REPORT = 1, WHAT_SEND_BACKLOG_TO_DROPBOX = 2 };
 
-//#define DEFAULT_BACKLOG_DELAY_NS (1000000000LL * 60 * 5)
 #define DEFAULT_BACKLOG_DELAY_NS (1000000000LL)
 
+#define DEFAULT_BYTES_SIZE_LIMIT (20 * 1024 * 1024)        // 20MB
+#define DEFAULT_REFACTORY_PERIOD_MS (24 * 60 * 60 * 1000)  // 1 Day
+
 // ================================================================================
 String16 const DUMP_PERMISSION("android.permission.DUMP");
 String16 const USAGE_STATS_PERMISSION("android.permission.PACKAGE_USAGE_STATS");
@@ -113,8 +116,12 @@
 }
 
 // ================================================================================
-ReportHandler::ReportHandler(const sp<Looper>& handlerLooper, const sp<ReportRequestQueue>& queue)
-    : mBacklogDelay(DEFAULT_BACKLOG_DELAY_NS), mHandlerLooper(handlerLooper), mQueue(queue) {}
+ReportHandler::ReportHandler(const sp<Looper>& handlerLooper, const sp<ReportRequestQueue>& queue,
+                             const sp<Throttler>& throttler)
+    : mBacklogDelay(DEFAULT_BACKLOG_DELAY_NS),
+      mHandlerLooper(handlerLooper),
+      mQueue(queue),
+      mThrottler(throttler) {}
 
 ReportHandler::~ReportHandler() {}
 
@@ -159,10 +166,17 @@
         reporter->batch.add(request);
     }
 
+    if (mThrottler->shouldThrottle()) {
+        ALOGW("RunReport got throttled.");
+        return;
+    }
+
     // Take the report, which might take a while. More requests might queue
     // up while we're doing this, and we'll handle them in their next batch.
     // TODO: We should further rate-limit the reports to no more than N per time-period.
-    Reporter::run_report_status_t reportStatus = reporter->runReport();
+    size_t reportByteSize = 0;
+    Reporter::run_report_status_t reportStatus = reporter->runReport(&reportByteSize);
+    mThrottler->addReportSize(reportByteSize);
     if (reportStatus == Reporter::REPORT_NEEDS_DROPBOX) {
         unique_lock<mutex> lock(mLock);
         schedule_send_backlog_to_dropbox_locked();
@@ -184,8 +198,9 @@
 
 // ================================================================================
 IncidentService::IncidentService(const sp<Looper>& handlerLooper)
-    : mQueue(new ReportRequestQueue()) {
-    mHandler = new ReportHandler(handlerLooper, mQueue);
+    : mQueue(new ReportRequestQueue()),
+      mThrottler(new Throttler(DEFAULT_BYTES_SIZE_LIMIT, DEFAULT_REFACTORY_PERIOD_MS)) {
+    mHandler = new ReportHandler(handlerLooper, mQueue, mThrottler);
 }
 
 IncidentService::~IncidentService() {}
@@ -294,6 +309,10 @@
         if (!args[0].compare(String8("privacy"))) {
             return cmd_privacy(in, out, err, args);
         }
+        if (!args[0].compare(String8("throttler"))) {
+            mThrottler->dump(out);
+            return NO_ERROR;
+        }
     }
     return cmd_help(out);
 }
@@ -302,6 +321,9 @@
     fprintf(out, "usage: adb shell cmd incident privacy print <section_id>\n");
     fprintf(out, "usage: adb shell cmd incident privacy parse <section_id> < proto.txt\n");
     fprintf(out, "    Prints/parses for the section id.\n");
+    fprintf(out, "\n");
+    fprintf(out, "usage: adb shell cmd incident throttler\n");
+    fprintf(out, "    Prints the current throttler state\n");
     return NO_ERROR;
 }
 
diff --git a/cmds/incidentd/src/IncidentService.h b/cmds/incidentd/src/IncidentService.h
index 3c66507..0ab34ed 100644
--- a/cmds/incidentd/src/IncidentService.h
+++ b/cmds/incidentd/src/IncidentService.h
@@ -26,6 +26,8 @@
 #include <deque>
 #include <mutex>
 
+#include "Throttler.h"
+
 using namespace android;
 using namespace android::base;
 using namespace android::binder;
@@ -49,7 +51,8 @@
 // ================================================================================
 class ReportHandler : public MessageHandler {
 public:
-    ReportHandler(const sp<Looper>& handlerLooper, const sp<ReportRequestQueue>& queue);
+    ReportHandler(const sp<Looper>& handlerLooper, const sp<ReportRequestQueue>& queue,
+                  const sp<Throttler>& throttler);
     virtual ~ReportHandler();
 
     virtual void handleMessage(const Message& message);
@@ -70,6 +73,7 @@
     nsecs_t mBacklogDelay;
     sp<Looper> mHandlerLooper;
     sp<ReportRequestQueue> mQueue;
+    sp<Throttler> mThrottler;
 
     /**
      * Runs all of the reports that have been queued.
@@ -109,6 +113,7 @@
 private:
     sp<ReportRequestQueue> mQueue;
     sp<ReportHandler> mHandler;
+    sp<Throttler> mThrottler;
 
     /**
      * Commands print out help.
diff --git a/cmds/incidentd/src/Log.h b/cmds/incidentd/src/Log.h
index 46efbd1..22de46d 100644
--- a/cmds/incidentd/src/Log.h
+++ b/cmds/incidentd/src/Log.h
@@ -23,7 +23,6 @@
 #pragma once
 
 #define LOG_TAG "incidentd"
-#define DEBUG false
 
 #include <log/log.h>
 
diff --git a/cmds/incidentd/src/PrivacyBuffer.cpp b/cmds/incidentd/src/PrivacyBuffer.cpp
index e4128f4..ee57f4d 100644
--- a/cmds/incidentd/src/PrivacyBuffer.cpp
+++ b/cmds/incidentd/src/PrivacyBuffer.cpp
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+#define DEBUG false
 #include "Log.h"
 
 #include "PrivacyBuffer.h"
diff --git a/cmds/incidentd/src/Reporter.cpp b/cmds/incidentd/src/Reporter.cpp
index c0b5358..12764f8 100644
--- a/cmds/incidentd/src/Reporter.cpp
+++ b/cmds/incidentd/src/Reporter.cpp
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+#define DEBUG false
 #include "Log.h"
 
 #include "Reporter.h"
@@ -118,7 +119,7 @@
 
 Reporter::~Reporter() {}
 
-Reporter::run_report_status_t Reporter::runReport() {
+Reporter::run_report_status_t Reporter::runReport(size_t* reportByteSize) {
     status_t err = NO_ERROR;
     bool needMainFd = false;
     int mainFd = -1;
@@ -185,7 +186,6 @@
             int64_t startTime = uptimeMillis();
             err = (*section)->Execute(&batch);
             int64_t endTime = uptimeMillis();
-
             stats->set_success(err == NO_ERROR);
             stats->set_exec_duration_ms(endTime - startTime);
             if (err != NO_ERROR) {
@@ -193,6 +193,7 @@
                       (*section)->name.string(), id, strerror(-err));
                 goto DONE;
             }
+            (*reportByteSize) += stats->report_size_bytes();
 
             // Notify listener of starting
             for (ReportRequestSet::iterator it = batch.begin(); it != batch.end(); it++) {
diff --git a/cmds/incidentd/src/Reporter.h b/cmds/incidentd/src/Reporter.h
index 0f3f221..ba8965e 100644
--- a/cmds/incidentd/src/Reporter.h
+++ b/cmds/incidentd/src/Reporter.h
@@ -18,8 +18,6 @@
 #ifndef REPORTER_H
 #define REPORTER_H
 
-#include "frameworks/base/libs/incident/proto/android/os/metadata.pb.h"
-
 #include <android/os/IIncidentReportStatusListener.h>
 #include <android/os/IncidentReportArgs.h>
 
@@ -29,6 +27,9 @@
 
 #include <time.h>
 
+#include "Throttler.h"
+#include "frameworks/base/libs/incident/proto/android/os/metadata.pb.h"
+
 using namespace android;
 using namespace android::os;
 using namespace std;
@@ -91,7 +92,7 @@
     virtual ~Reporter();
 
     // Run the report as described in the batch and args parameters.
-    run_report_status_t runReport();
+    run_report_status_t runReport(size_t* reportByteSize);
 
     static run_report_status_t upload_backlog();
 
diff --git a/cmds/incidentd/src/Section.cpp b/cmds/incidentd/src/Section.cpp
index 2e4e980..64eae3a 100644
--- a/cmds/incidentd/src/Section.cpp
+++ b/cmds/incidentd/src/Section.cpp
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+#define DEBUG false
 #include "Log.h"
 
 #include "Section.h"
@@ -244,7 +245,8 @@
     }
     if (requests->mainFd() >= 0 && !metadataBuf.empty()) {
         write_section_header(requests->mainFd(), id, metadataBuf.size());
-        if (!WriteFully(requests->mainFd(), (uint8_t const*)metadataBuf.data(), metadataBuf.size())) {
+        if (!WriteFully(requests->mainFd(), (uint8_t const*)metadataBuf.data(),
+                        metadataBuf.size())) {
             ALOGW("Failed to write metadata to dropbox fd %d", requests->mainFd());
             return -1;
         }
diff --git a/cmds/incidentd/src/Throttler.cpp b/cmds/incidentd/src/Throttler.cpp
new file mode 100644
index 0000000..1abf267
--- /dev/null
+++ b/cmds/incidentd/src/Throttler.cpp
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#define DEBUG false
+#include "Log.h"
+
+#include "Throttler.h"
+
+#include <utils/SystemClock.h>
+
+Throttler::Throttler(size_t limit, int64_t refractoryPeriodMs)
+    : mSizeLimit(limit),
+      mRefractoryPeriodMs(refractoryPeriodMs),
+      mAccumulatedSize(0),
+      mLastRefractoryMs(android::elapsedRealtime()) {}
+
+Throttler::~Throttler() {}
+
+bool Throttler::shouldThrottle() {
+    int64_t now = android::elapsedRealtime();
+    if (now > mRefractoryPeriodMs + mLastRefractoryMs) {
+        mLastRefractoryMs = now;
+        mAccumulatedSize = 0;
+    }
+    return mAccumulatedSize > mSizeLimit;
+}
+
+void Throttler::addReportSize(size_t reportByteSize) {
+    VLOG("The current request took %d bytes to dropbox", (int)reportByteSize);
+    mAccumulatedSize += reportByteSize;
+}
+
+void Throttler::dump(FILE* out) {
+    fprintf(out, "mSizeLimit=%d\n", (int)mSizeLimit);
+    fprintf(out, "mAccumulatedSize=%d\n", (int)mAccumulatedSize);
+    fprintf(out, "mRefractoryPeriodMs=%d\n", (int)mRefractoryPeriodMs);
+    fprintf(out, "mLastRefractoryMs=%d\n", (int)mLastRefractoryMs);
+}
diff --git a/cmds/incidentd/src/Throttler.h b/cmds/incidentd/src/Throttler.h
new file mode 100644
index 0000000..c56f753
--- /dev/null
+++ b/cmds/incidentd/src/Throttler.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef THROTTLER_H
+#define THROTTLER_H
+
+#include <utils/RefBase.h>
+
+#include <unistd.h>
+/**
+ * This is a size-based throttler which prevents incidentd to take more data.
+ */
+class Throttler : public virtual android::RefBase {
+public:
+    Throttler(size_t limit, int64_t refractoryPeriodMs);
+    ~Throttler();
+
+    /**
+     * Asserts this before starting taking report.
+     */
+    bool shouldThrottle();
+
+    void addReportSize(size_t reportByteSize);
+
+    void dump(FILE* out);
+
+private:
+    const size_t mSizeLimit;
+    const int64_t mRefractoryPeriodMs;
+
+    size_t mAccumulatedSize;
+    int64_t mLastRefractoryMs;
+};
+
+#endif  // THROTTLER_H
diff --git a/cmds/incidentd/src/report_directory.cpp b/cmds/incidentd/src/report_directory.cpp
index b71c066..f023ee1 100644
--- a/cmds/incidentd/src/report_directory.cpp
+++ b/cmds/incidentd/src/report_directory.cpp
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+#define DEBUG false
 #include "Log.h"
 
 #include "report_directory.h"
diff --git a/cmds/incidentd/tests/FdBuffer_test.cpp b/cmds/incidentd/tests/FdBuffer_test.cpp
index 956c8d3..0e5eec6 100644
--- a/cmds/incidentd/tests/FdBuffer_test.cpp
+++ b/cmds/incidentd/tests/FdBuffer_test.cpp
@@ -11,6 +11,7 @@
 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 // See the License for the specific language governing permissions and
 // limitations under the License.
+#define DEBUG false
 #include "Log.h"
 
 #include "FdBuffer.h"
diff --git a/cmds/incidentd/tests/PrivacyBuffer_test.cpp b/cmds/incidentd/tests/PrivacyBuffer_test.cpp
index 7ea9bbf..c7c69a7 100644
--- a/cmds/incidentd/tests/PrivacyBuffer_test.cpp
+++ b/cmds/incidentd/tests/PrivacyBuffer_test.cpp
@@ -11,6 +11,7 @@
 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 // See the License for the specific language governing permissions and
 // limitations under the License.
+#define DEBUG false
 #include "Log.h"
 
 #include "FdBuffer.h"
diff --git a/cmds/incidentd/tests/Reporter_test.cpp b/cmds/incidentd/tests/Reporter_test.cpp
index bd359ac..955dbac 100644
--- a/cmds/incidentd/tests/Reporter_test.cpp
+++ b/cmds/incidentd/tests/Reporter_test.cpp
@@ -11,6 +11,7 @@
 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 // See the License for the specific language governing permissions and
 // limitations under the License.
+#define DEBUG false
 #include "Log.h"
 
 #include "Reporter.h"
@@ -106,6 +107,7 @@
     ReportRequestSet requests;
     sp<Reporter> reporter;
     sp<TestListener> l;
+    size_t size;
 };
 
 TEST_F(ReporterTest, IncidentReportArgs) {
@@ -125,7 +127,7 @@
 }
 
 TEST_F(ReporterTest, RunReportEmpty) {
-    ASSERT_EQ(Reporter::REPORT_FINISHED, reporter->runReport());
+    ASSERT_EQ(Reporter::REPORT_FINISHED, reporter->runReport(&size));
     EXPECT_EQ(l->startInvoked, 0);
     EXPECT_EQ(l->finishInvoked, 0);
     EXPECT_TRUE(l->startSections.empty());
@@ -147,7 +149,7 @@
     reporter->batch.add(r1);
     reporter->batch.add(r2);
 
-    ASSERT_EQ(Reporter::REPORT_FINISHED, reporter->runReport());
+    ASSERT_EQ(Reporter::REPORT_FINISHED, reporter->runReport(&size));
 
     string result;
     ReadFileToString(tf.path, &result);
@@ -171,7 +173,7 @@
     sp<ReportRequest> r = new ReportRequest(args, l, -1);
     reporter->batch.add(r);
 
-    ASSERT_EQ(Reporter::REPORT_FINISHED, reporter->runReport());
+    ASSERT_EQ(Reporter::REPORT_FINISHED, reporter->runReport(&size));
     vector<string> results = InspectFiles();
     ASSERT_EQ((int)results.size(), 1);
     EXPECT_EQ(results[0],
@@ -188,7 +190,7 @@
     sp<ReportRequest> r = new ReportRequest(args, l, -1);
     reporter->batch.add(r);
 
-    ASSERT_EQ(Reporter::REPORT_FINISHED, reporter->runReport());
+    ASSERT_EQ(Reporter::REPORT_FINISHED, reporter->runReport(&size));
     auto metadata = reporter->batch.metadata();
     EXPECT_EQ(IncidentMetadata_Destination_EXPLICIT, metadata.dest());
     EXPECT_EQ(1, metadata.request_size());
diff --git a/cmds/incidentd/tests/Section_test.cpp b/cmds/incidentd/tests/Section_test.cpp
index a1f4fdc..026bf74 100644
--- a/cmds/incidentd/tests/Section_test.cpp
+++ b/cmds/incidentd/tests/Section_test.cpp
@@ -11,6 +11,7 @@
 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 // See the License for the specific language governing permissions and
 // limitations under the License.
+#define DEBUG false
 #include "Log.h"
 
 #include "Section.h"
diff --git a/cmds/incidentd/tests/Throttler_test.cpp b/cmds/incidentd/tests/Throttler_test.cpp
new file mode 100644
index 0000000..213dcef
--- /dev/null
+++ b/cmds/incidentd/tests/Throttler_test.cpp
@@ -0,0 +1,37 @@
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+#define DEBUG false
+#include "Log.h"
+
+#include "Throttler.h"
+
+#include <android-base/test_utils.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+TEST(ThrottlerTest, DataSizeExceeded) {
+    Throttler t(100, 100000);
+    EXPECT_FALSE(t.shouldThrottle());
+    t.addReportSize(200);
+    EXPECT_TRUE(t.shouldThrottle());
+}
+
+TEST(ThrottlerTest, TimeReset) {
+    Throttler t(100, 500);
+    EXPECT_FALSE(t.shouldThrottle());
+    t.addReportSize(200);
+    EXPECT_TRUE(t.shouldThrottle());
+    sleep(1);  // sleep for 1 second to make sure throttler resets
+    EXPECT_FALSE(t.shouldThrottle());
+}