Implement PII stripper in incidentd, part 1

1. automatically parse privacy options and generate lookup table
2. create FdBuffer iterator API in order to remove dependency on Reporter.h

Bug: 64687253
Test: Unit test for iterator API, and manually tested lookup table
Change-Id: I1ea376a4481fc4afc7bdf447936f767b63690fd3
diff --git a/cmds/incidentd/src/FdBuffer.cpp b/cmds/incidentd/src/FdBuffer.cpp
index 7743301..4d6a36c 100644
--- a/cmds/incidentd/src/FdBuffer.cpp
+++ b/cmds/incidentd/src/FdBuffer.cpp
@@ -174,7 +174,7 @@
             if (rpos >= wpos) {
                 amt = ::read(fd, cirBuf + rpos, BUFFER_SIZE - rpos);
             } else {
-                amt = :: read(fd, cirBuf + rpos, wpos - rpos);
+                amt = ::read(fd, cirBuf + rpos, wpos - rpos);
             }
             if (amt < 0) {
                 if (!(errno == EAGAIN || errno == EWOULDBLOCK)) {
@@ -241,6 +241,7 @@
 size_t
 FdBuffer::size()
 {
+    if (mBuffers.empty()) return 0;
     return ((mBuffers.size() - 1) * BUFFER_SIZE) + mCurrentWritten;
 }
 
@@ -255,4 +256,30 @@
     return NO_ERROR;
 }
 
+FdBuffer::iterator
+FdBuffer::end()
+{
+    if (mBuffers.empty() || mCurrentWritten < 0) return begin();
+    if (mCurrentWritten == BUFFER_SIZE)
+        // FdBuffer doesn't allocate another buf since no more bytes to read.
+        return FdBuffer::iterator(*this, mBuffers.size(), 0);
+    return FdBuffer::iterator(*this, mBuffers.size() - 1, mCurrentWritten);
+}
 
+FdBuffer::iterator&
+FdBuffer::iterator::operator+(size_t offset)
+{
+    size_t newOffset = mOffset + offset;
+    while (newOffset >= BUFFER_SIZE) {
+        mIndex++;
+        newOffset -= BUFFER_SIZE;
+    }
+    mOffset = newOffset;
+    return *this;
+}
+
+size_t
+FdBuffer::iterator::bytesRead()
+{
+    return mIndex * BUFFER_SIZE + mOffset;
+}
diff --git a/cmds/incidentd/src/FdBuffer.h b/cmds/incidentd/src/FdBuffer.h
index 03a6d18..e9a53ff 100644
--- a/cmds/incidentd/src/FdBuffer.h
+++ b/cmds/incidentd/src/FdBuffer.h
@@ -21,7 +21,6 @@
 
 #include <utils/Errors.h>
 
-#include <set>
 #include <vector>
 
 using namespace android;
@@ -74,7 +73,8 @@
     size_t size();
 
     /**
-     * Write the data that we recorded to the fd given.
+     * [Deprecated] Write the data that we recorded to the fd given.
+     * TODO: remove it once the iterator api is working
      */
     status_t write(ReportRequestSet* requests);
 
@@ -83,6 +83,37 @@
      */
     int64_t durationMs() { return mFinishTime - mStartTime; }
 
+    /**
+     * Read data stored in FdBuffer
+     */
+    class iterator;
+    friend class iterator;
+    class iterator : public std::iterator<std::random_access_iterator_tag, uint8_t> {
+    private:
+        FdBuffer& mFdBuffer;
+        size_t mIndex;
+        size_t mOffset;
+    public:
+        explicit iterator(FdBuffer& buffer, ssize_t index, ssize_t offset)
+                : mFdBuffer(buffer), mIndex(index), mOffset(offset) {}
+        iterator& operator=(iterator& other) { return other; }
+        iterator& operator+(size_t offset); // this is implemented in .cpp
+        iterator& operator+=(size_t offset) { return *this + offset; }
+        iterator& operator++() { return *this + 1; }
+        iterator operator++(int) { return *this + 1; }
+        bool operator==(iterator other) const {
+            return mIndex == other.mIndex && mOffset == other.mOffset;
+        }
+        bool operator!=(iterator other) const { return !(*this == other); }
+        reference operator*() const { return mFdBuffer.mBuffers[mIndex][mOffset]; }
+
+        // random access could make the iterator out of bound
+        size_t bytesRead();
+        bool outOfBound() { return bytesRead() > mFdBuffer.size(); };
+    };
+    iterator begin() { return iterator(*this, 0, 0); }
+    iterator end();
+
 private:
     vector<uint8_t*> mBuffers;
     int64_t mStartTime;
diff --git a/cmds/incidentd/src/Section.cpp b/cmds/incidentd/src/Section.cpp
index 0f6f38e..ac87fe3 100644
--- a/cmds/incidentd/src/Section.cpp
+++ b/cmds/incidentd/src/Section.cpp
@@ -22,9 +22,6 @@
 #include <private/android_filesystem_config.h>
 #include <binder/IServiceManager.h>
 #include <mutex>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
 #include <wait.h>
 #include <unistd.h>
 
@@ -37,7 +34,7 @@
 static pid_t
 forkAndExecuteIncidentHelper(const int id, const char* name, Fpipe& p2cPipe, Fpipe& c2pPipe)
 {
-    const char* ihArgs[] { INCIDENT_HELPER, "-s", to_string(id).c_str(), NULL };
+    const char* ihArgs[] { INCIDENT_HELPER, "-s", String8::format("%d", id).string(), NULL };
 
     // fork used in multithreaded environment, avoid adding unnecessary code in child process
     pid_t pid = fork();
diff --git a/cmds/incidentd/src/protobuf.cpp b/cmds/incidentd/src/protobuf.cpp
index a703ef9..b865339 100644
--- a/cmds/incidentd/src/protobuf.cpp
+++ b/cmds/incidentd/src/protobuf.cpp
@@ -16,6 +16,7 @@
 
 #include "protobuf.h"
 
+
 uint8_t* 
 write_raw_varint(uint8_t* buf, uint32_t val)
 {
diff --git a/cmds/incidentd/src/section_list.h b/cmds/incidentd/src/section_list.h
index c977519..1abdb52 100644
--- a/cmds/incidentd/src/section_list.h
+++ b/cmds/incidentd/src/section_list.h
@@ -21,8 +21,46 @@
 
 /**
  * This is the mapping of section IDs to the commands that are run to get those commands.
+ * The section IDs are guaranteed in ascending order
  */
 extern const Section* SECTION_LIST[];
 
+/*
+ * In order not to use libprotobuf-cpp-full nor libplatformprotos in incidentd
+ * privacy options's data structure are explicityly redefined in this file.
+ */
+
+// DESTINATION enum
+extern const uint8_t DEST_LOCAL;
+extern const uint8_t DEST_EXPLICIT;
+extern const uint8_t DEST_AUTOMATIC;
+
+// This is the default value of DEST enum
+// field with this value doesn't generate Privacy to save too much generated code
+extern const uint8_t DEST_DEFAULT_VALUE;
+
+// type of the field, identitical to protobuf definition
+extern const uint8_t TYPE_STRING;
+extern const uint8_t TYPE_MESSAGE;
+
+struct Privacy {
+    int field_id;
+    uint8_t type;
+
+    // the following two fields are identitical to
+    // frameworks/base/libs/incident/proto/android/privacy.proto
+    uint8_t dest;
+    const char** patterns;
+
+    // ignore parent's privacy flags if children are set, NULL-terminated
+    const Privacy** children;
+};
+
+/**
+ * This is the mapping of section IDs to each section's privacy policy.
+ * The section IDs are guaranteed in ascending order
+ */
+extern const Privacy* PRIVACY_POLICY_LIST[];
+
 #endif // SECTION_LIST_H
 
diff --git a/cmds/incidentd/tests/FdBuffer_test.cpp b/cmds/incidentd/tests/FdBuffer_test.cpp
index ba8b77a..403a2ab 100644
--- a/cmds/incidentd/tests/FdBuffer_test.cpp
+++ b/cmds/incidentd/tests/FdBuffer_test.cpp
@@ -92,6 +92,30 @@
     AssertBufferContent(testdata.c_str());
 }
 
+TEST_F(FdBufferTest, IterateEmpty) {
+    FdBuffer::iterator it = buffer.begin();
+    EXPECT_EQ(it, buffer.end());
+    it += 1;
+    EXPECT_TRUE(it.outOfBound());
+}
+
+TEST_F(FdBufferTest, ReadAndIterate) {
+    std::string testdata = "FdBuffer test string";
+    ASSERT_TRUE(WriteStringToFile(testdata, tf.path, false));
+    ASSERT_EQ(NO_ERROR, buffer.read(tf.fd, READ_TIMEOUT));
+
+    int i=0;
+    for (FdBuffer::iterator it = buffer.begin(); it != buffer.end(); ++it) {
+        EXPECT_EQ(*it, (uint8_t)testdata[i++]);
+    }
+
+    FdBuffer::iterator it = buffer.begin();
+    it += buffer.size();
+    EXPECT_EQ(it, buffer.end());
+    EXPECT_EQ(it.bytesRead(), testdata.size());
+    EXPECT_FALSE(it.outOfBound());
+}
+
 TEST_F(FdBufferTest, ReadTimeout) {
     int pid = fork();
     ASSERT_TRUE(pid != -1);
@@ -202,6 +226,7 @@
 
 TEST_F(FdBufferTest, ReadInStreamMoreThan4MB) {
     const std::string testFile = kTestDataPath + "morethan4MB.txt";
+    size_t fourMB = (size_t) 4 * 1024 * 1024;
     int fd = open(testFile.c_str(), O_RDONLY);
     ASSERT_NE(fd, -1);
     int pid = fork();
@@ -220,10 +245,18 @@
 
         ASSERT_EQ(NO_ERROR, buffer.readProcessedDataInStream(fd,
             p2cPipe.writeFd(), c2pPipe.readFd(), READ_TIMEOUT));
-        EXPECT_EQ(buffer.size(), (size_t) (4 * 1024 * 1024));
+        EXPECT_EQ(buffer.size(), fourMB);
         EXPECT_FALSE(buffer.timedOut());
         EXPECT_TRUE(buffer.truncated());
         wait(&pid);
+        FdBuffer::iterator it = buffer.begin();
+        it += fourMB;
+        EXPECT_EQ(it.bytesRead(), fourMB);
+        EXPECT_EQ(it, buffer.end());
+        for (FdBuffer::iterator it = buffer.begin(); it != buffer.end(); it++) {
+            char c = 'A' + (it.bytesRead() % 64 / 8);
+            ASSERT_TRUE(*it == c);
+        }
     }
 }