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/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.