This cl does the following things:

0) Implements a skeleton of incident_helper
1) Implements FileSection class which calls incident_helper to parse
   file content to protobuf
2) Adds Kernel Wake Sources to incident.proto and makes it parsed by
   FileSection
3) Adds basic gtests to test FdBuffer, io_utils, FileSection
implementation

Bug: 62923266
Bug: 62926061
Test: manual - push incidentd, incident_helper and incident to my device
      and verify kernel wakeup sources file is able to be parsed.
Change-Id: I2aa6b6158d962ce70e6fa6c8a9c42213a45ff41c
diff --git a/cmds/incidentd/src/FdBuffer.cpp b/cmds/incidentd/src/FdBuffer.cpp
index 527d7ee..7743301 100644
--- a/cmds/incidentd/src/FdBuffer.cpp
+++ b/cmds/incidentd/src/FdBuffer.cpp
@@ -24,11 +24,11 @@
 #include <fcntl.h>
 #include <poll.h>
 #include <unistd.h>
+#include <wait.h>
 
-const ssize_t BUFFER_SIZE = 16 * 1024;
+const ssize_t BUFFER_SIZE = 16 * 1024; // 16 KB
 const ssize_t MAX_BUFFER_COUNT = 256; // 4 MB max
 
-
 FdBuffer::FdBuffer()
     :mBuffers(),
      mStartTime(-1),
@@ -109,6 +109,135 @@
     return NO_ERROR;
 }
 
+status_t
+FdBuffer::readProcessedDataInStream(int fd, int toFd, int fromFd, int64_t timeoutMs)
+{
+    struct pollfd pfds[] = {
+        { .fd = fd,     .events = POLLIN  },
+        { .fd = toFd,   .events = POLLOUT },
+        { .fd = fromFd, .events = POLLIN  },
+    };
+
+    mStartTime = uptimeMillis();
+
+    // mark all fds non blocking
+    fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK);
+    fcntl(toFd, F_SETFL, fcntl(toFd, F_GETFL, 0) | O_NONBLOCK);
+    fcntl(fromFd, F_SETFL, fcntl(fromFd, F_GETFL, 0) | O_NONBLOCK);
+
+    // A circular buffer holds data read from fd and writes to parsing process
+    uint8_t cirBuf[BUFFER_SIZE];
+    size_t cirSize = 0;
+    int rpos = 0, wpos = 0;
+
+    // This is the buffer used to store processed data
+    uint8_t* buf = NULL;
+    while (true) {
+        if (mCurrentWritten >= BUFFER_SIZE || mCurrentWritten < 0) {
+            if (mBuffers.size() == MAX_BUFFER_COUNT) {
+                mTruncated = true;
+                break;
+            }
+            buf = (uint8_t*)malloc(BUFFER_SIZE);
+            if (buf == NULL) {
+                return NO_MEMORY;
+            }
+            mBuffers.push_back(buf);
+            mCurrentWritten = 0;
+        }
+
+        int64_t remainingTime = (mStartTime + timeoutMs) - uptimeMillis();
+        if (remainingTime <= 0) {
+            mTimedOut = true;
+            break;
+        }
+
+        // wait for any pfds to be ready to perform IO
+        int count = poll(pfds, 3, remainingTime);
+        if (count == 0) {
+            mTimedOut = true;
+            break;
+        } else if (count < 0) {
+            return -errno;
+        }
+
+        // make sure no errors occur on any fds
+        for (int i = 0; i < 3; ++i) {
+            if ((pfds[i].revents & POLLERR) != 0) {
+                return errno != 0 ? -errno : UNKNOWN_ERROR;
+            }
+        }
+
+        // read from fd
+        if (cirSize != BUFFER_SIZE && pfds[0].fd != -1) {
+            ssize_t amt;
+            if (rpos >= wpos) {
+                amt = ::read(fd, cirBuf + rpos, BUFFER_SIZE - rpos);
+            } else {
+                amt = :: read(fd, cirBuf + rpos, wpos - rpos);
+            }
+            if (amt < 0) {
+                if (!(errno == EAGAIN || errno == EWOULDBLOCK)) {
+                    return -errno;
+                } // otherwise just continue
+            } else if (amt == 0) {  // reach EOF so don't have to poll pfds[0].
+                ::close(pfds[0].fd);
+                pfds[0].fd = -1;
+            } else {
+                rpos += amt;
+                cirSize += amt;
+            }
+        }
+
+        // write to parsing process
+        if (cirSize > 0 && pfds[1].fd != -1) {
+            ssize_t amt;
+            if (rpos > wpos) {
+                amt = ::write(toFd, cirBuf + wpos, rpos - wpos);
+            } else {
+                amt = ::write(toFd, cirBuf + wpos, BUFFER_SIZE - wpos);
+            }
+            if (amt < 0) {
+                if (!(errno == EAGAIN || errno == EWOULDBLOCK)) {
+                    return -errno;
+                } // otherwise just continue
+            } else {
+                wpos += amt;
+                cirSize -= amt;
+            }
+        }
+
+        // if buffer is empty and fd is closed, close write fd.
+        if (cirSize == 0 && pfds[0].fd == -1 && pfds[1].fd != -1) {
+            ::close(pfds[1].fd);
+            pfds[1].fd = -1;
+        }
+
+        // circular buffer, reset rpos and wpos
+        if (rpos >= BUFFER_SIZE) {
+            rpos = 0;
+        }
+        if (wpos >= BUFFER_SIZE) {
+            wpos = 0;
+        }
+
+        // read from parsing process
+        ssize_t amt = ::read(fromFd, buf + mCurrentWritten, BUFFER_SIZE - mCurrentWritten);
+        if (amt < 0) {
+            if (!(errno == EAGAIN || errno == EWOULDBLOCK)) {
+                return -errno;
+            } // otherwise just continue
+        } else if (amt == 0) {
+            break;
+        } else {
+            mCurrentWritten += amt;
+        }
+    }
+
+    mFinishTime = uptimeMillis();
+    return NO_ERROR;
+}
+
 size_t
 FdBuffer::size()
 {
diff --git a/cmds/incidentd/src/FdBuffer.h b/cmds/incidentd/src/FdBuffer.h
index e12374f..7888442 100644
--- a/cmds/incidentd/src/FdBuffer.h
+++ b/cmds/incidentd/src/FdBuffer.h
@@ -44,6 +44,16 @@
     status_t read(int fd, int64_t timeoutMs);
 
     /**
+     * Read processed results by streaming data to a parsing process, e.g. incident helper.
+     * The parsing process provides IO fds which are 'toFd' and 'fromFd'. The function
+     * reads original data in 'fd' and writes to parsing process through 'toFd', then it reads
+     * and stores the processed data from 'fromFd' in memory for later usage.
+     * This function behaves in a streaming fashion in order to save memory usage.
+     * Returns NO_ERROR if there were no errors or if we timed out.
+     */
+    status_t readProcessedDataInStream(int fd, int toFd, int fromFd, int64_t timeoutMs);
+
+    /**
      * Whether we timed out.
      */
     bool timedOut() { return mTimedOut; }
@@ -82,5 +92,19 @@
     bool mTruncated;
 };
 
+class Fpipe {
+public:
+    Fpipe() {}
+    bool close() { return !(::close(mFds[0]) || ::close(mFds[1])); }
+    ~Fpipe() { close(); }
+
+    inline status_t init() { return pipe(mFds); }
+    inline int readFd() const { return mFds[0]; }
+    inline int writeFd() const { return mFds[1]; }
+
+private:
+    int mFds[2];
+};
+
 
 #endif // FD_BUFFER_H
diff --git a/cmds/incidentd/src/Reporter.cpp b/cmds/incidentd/src/Reporter.cpp
index 1ecb291..ba157de6 100644
--- a/cmds/incidentd/src/Reporter.cpp
+++ b/cmds/incidentd/src/Reporter.cpp
@@ -22,8 +22,8 @@
 #include "report_directory.h"
 #include "section_list.h"
 
-#include <private/android_filesystem_config.h>
 #include <android/os/DropBoxManager.h>
+#include <private/android_filesystem_config.h>
 #include <utils/SystemClock.h>
 
 #include <sys/types.h>
@@ -37,8 +37,8 @@
  */
 static const String8 INCIDENT_DIRECTORY("/data/incidents");
 
-static status_t
-write_all(int fd, uint8_t const* buf, size_t size)
+// ================================================================================
+static status_t write_all(int fd, uint8_t const* buf, size_t size)
 {
     while (size > 0) {
         ssize_t amt = ::write(fd, buf, size);
diff --git a/cmds/incidentd/src/Section.cpp b/cmds/incidentd/src/Section.cpp
index fac299e..8494f98 100644
--- a/cmds/incidentd/src/Section.cpp
+++ b/cmds/incidentd/src/Section.cpp
@@ -21,10 +21,19 @@
 
 #include <binder/IServiceManager.h>
 #include <mutex>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <wait.h>
+#include <unistd.h>
 
 using namespace std;
 
 const int64_t REMOTE_CALL_TIMEOUT_MS = 10 * 1000; // 10 seconds
+const int64_t INCIDENT_HELPER_TIMEOUT_MS = 5 * 1000;  // 5 seconds
+const char* INCIDENT_HELPER = "/system/bin/incident_helper";
+const uid_t IH_UID = 9999;  // run incident_helper as nobody
+const gid_t IH_GID = 9999;
 
 // ================================================================================
 Section::Section(int i)
@@ -46,6 +55,115 @@
 }
 
 // ================================================================================
+FileSection::FileSection(int id, const char* filename)
+        : Section(id), mFilename(filename) {
+    name = "cat ";
+    name += filename;
+}
+
+FileSection::~FileSection() {}
+
+status_t FileSection::Execute(ReportRequestSet* requests) const {
+    Fpipe p2cPipe;
+    Fpipe c2pPipe;
+    FdBuffer buffer;
+
+    // initiate pipes to pass data to/from incident_helper
+    if (p2cPipe.init() == -1) {
+        return -errno;
+    }
+    if (c2pPipe.init() == -1) {
+        return -errno;
+    }
+
+    // fork a child process
+    pid_t pid = fork();
+
+    if (pid == -1) {
+        ALOGW("FileSection '%s' failed to fork", this->name.string());
+        return -errno;
+    }
+
+    // child process
+    if (pid == 0) {
+        if (setgid(IH_GID) == -1) {
+            ALOGW("FileSection '%s' can't change gid: %s", this->name.string(), strerror(errno));
+            exit(EXIT_FAILURE);
+        }
+        if (setuid(IH_UID) == -1) {
+            ALOGW("FileSection '%s' can't change uid: %s", this->name.string(), strerror(errno));
+            exit(EXIT_FAILURE);
+        }
+
+        if (dup2(p2cPipe.readFd(), STDIN_FILENO) != 0 || !p2cPipe.close()) {
+            ALOGW("FileSection '%s' failed to set up stdin: %s", this->name.string(), strerror(errno));
+            exit(EXIT_FAILURE);
+        }
+        if (dup2(c2pPipe.writeFd(), STDOUT_FILENO) != 1 || !c2pPipe.close()) {
+            ALOGW("FileSection '%s' failed to set up stdout: %s", this->name.string(), strerror(errno));
+            exit(EXIT_FAILURE);
+        }
+
+        // execute incident_helper to parse raw file data and generate protobuf
+        char sectionID[8];  // section id is expected to be smaller than 8 digits
+        sprintf(sectionID, "%d", this->id);
+        const char* args[]{INCIDENT_HELPER, "-s", sectionID, NULL};
+        execv(INCIDENT_HELPER, const_cast<char**>(args));
+
+        ALOGW("FileSection '%s' failed in child process: %s", this->name.string(), strerror(errno));
+        return -1;
+    }
+
+    // parent process
+
+    // close fds used in child process
+    close(p2cPipe.readFd());
+    close(c2pPipe.writeFd());
+
+    // read from mFilename and pump buffer to incident_helper
+    status_t err = NO_ERROR;
+    int fd = open(mFilename, O_RDONLY, 0444);
+    if (fd == -1) {
+       ALOGW("FileSection '%s' failed to open file", this->name.string());
+       return -errno;
+    }
+
+    err = buffer.readProcessedDataInStream(fd,
+        p2cPipe.writeFd(), c2pPipe.readFd(), INCIDENT_HELPER_TIMEOUT_MS);
+    if (err != NO_ERROR) {
+        ALOGW("FileSection '%s' failed to read data from incident helper: %s",
+            this->name.string(), strerror(-err));
+        kill(pid, SIGKILL); // kill child process if meets error
+        return err;
+    }
+
+    if (buffer.timedOut()) {
+        ALOGW("FileSection '%s' timed out reading from incident helper!", this->name.string());
+        kill(pid, SIGKILL); // kill the child process if timed out
+    }
+
+    // has to block here to reap child process
+    int status;
+    int w = waitpid(pid, &status, 0);
+    if (w < 0 || status == -1) {
+        ALOGW("FileSection '%s' abnormal child process: %s", this->name.string(), strerror(-err));
+        return -errno;
+    }
+
+    // write parsed data to reporter
+    ALOGD("section '%s' wrote %zd bytes in %d ms", this->name.string(), buffer.size(),
+            (int)buffer.durationMs());
+    WriteHeader(requests, buffer.size());
+    err = buffer.write(requests);
+    if (err != NO_ERROR) {
+        ALOGW("FileSection '%s' failed writing: %s", this->name.string(), strerror(-err));
+        return err;
+    }
+
+    return NO_ERROR;
+}
+
+// ================================================================================
 struct WorkerThreadData : public virtual RefBase
 {
     const WorkerThreadSection* section;
@@ -223,7 +341,7 @@
     name += " ";
     va_start(args, first);
     for (int i=0; i<count; i++) {
-        const char* arg = va_arg(args, const char*); 
+        const char* arg = va_arg(args, const char*);
         mCommand[i+1] = arg;
         if (arg != NULL) {
             name += va_arg(args, const char*);
@@ -254,7 +372,7 @@
     va_list args;
     va_start(args, service);
     while (true) {
-        const char* arg = va_arg(args, const char*); 
+        const char* arg = va_arg(args, const char*);
         if (arg == NULL) {
             break;
         }
@@ -274,7 +392,7 @@
 {
     // checkService won't wait for the service to show up like getService will.
     sp<IBinder> service = defaultServiceManager()->checkService(mService);
-    
+
     if (service == NULL) {
         // Returning an error interrupts the entire incident report, so just
         // log the failure.
diff --git a/cmds/incidentd/src/protobuf.cpp b/cmds/incidentd/src/protobuf.cpp
index cb864fd..a703ef9 100644
--- a/cmds/incidentd/src/protobuf.cpp
+++ b/cmds/incidentd/src/protobuf.cpp
@@ -16,7 +16,7 @@
 
 #include "protobuf.h"
 
-uint8_t*
+uint8_t* 
 write_raw_varint(uint8_t* buf, uint32_t val)
 {
     uint8_t* p = buf;
@@ -31,7 +31,7 @@
     }
 }
 
-uint8_t*
+uint8_t* 
 write_length_delimited_tag_header(uint8_t* buf, uint32_t fieldId, size_t size)
 {
     buf = write_raw_varint(buf, (fieldId << 3) | 2);
diff --git a/cmds/incidentd/src/protobuf.h b/cmds/incidentd/src/protobuf.h
index a243998..f196ddc1 100644
--- a/cmds/incidentd/src/protobuf.h
+++ b/cmds/incidentd/src/protobuf.h
@@ -21,13 +21,14 @@
 
 /**
  * Write a varint into the buffer. Return the next position to write at.
- * There must be 10 bytes in the buffer. The same as EncodedBuffer.writeRawVarint32
+ * There must be 10 bytes in the buffer. The same as
+ * EncodedBuffer.writeRawVarint32
  */
 uint8_t* write_raw_varint(uint8_t* buf, uint32_t val);
 
 /**
- * Write a protobuf WIRE_TYPE_LENGTH_DELIMITED header. Return the next position to write at.
- * There must be 20 bytes in the buffer.
+ * Write a protobuf WIRE_TYPE_LENGTH_DELIMITED header. Return the next position
+ * to write at. There must be 20 bytes in the buffer.
  */
 uint8_t* write_length_delimited_tag_header(uint8_t* buf, uint32_t fieldId, size_t size);
 
@@ -36,5 +37,4 @@
     FIELD_ID_INCIDENT_HEADER = 1
 };
 
-#endif // PROTOBUF_H
-
+#endif  // PROTOBUF_H
diff --git a/cmds/incidentd/src/section_list.cpp b/cmds/incidentd/src/section_list.cpp
index b6112ed..72c54d2 100644
--- a/cmds/incidentd/src/section_list.cpp
+++ b/cmds/incidentd/src/section_list.cpp
@@ -16,14 +16,14 @@
 
 #include "section_list.h"
 
-//using namespace android::util;
-
 /**
  * This is the mapping of section IDs to the commands that are run to get those commands.
  */
 const Section* SECTION_LIST[] = {
-    new DumpsysSection(3000,
-            "fingerprint", "--proto", "--incident", NULL),
+    // Linux Services
+    new FileSection(2002, "/d/wakeup_sources"),
+
+    // System Services
+    new DumpsysSection(3000, "fingerprint", "--proto", "--incident", NULL),
     NULL
 };
-