Merge "Tron/proto: Add ACTION and FIELD for logging the boot reason string value through metrics."
diff --git a/cmds/incident/main.cpp b/cmds/incident/main.cpp
index 47f1db8..519852d 100644
--- a/cmds/incident/main.cpp
+++ b/cmds/incident/main.cpp
@@ -25,6 +25,7 @@
 #include <binder/IServiceManager.h>
 #include <utils/Looper.h>
 
+#include <cstring>
 #include <fcntl.h>
 #include <getopt.h>
 #include <stdio.h>
@@ -144,6 +145,16 @@
 }
 
 // ================================================================================
+static int
+get_dest(const char* arg)
+{
+    if (strcmp(arg, "LOCAL") == 0) return 0;
+    if (strcmp(arg, "EXPLICIT") == 0) return 1;
+    if (strcmp(arg, "AUTOMATIC") == 0) return 2;
+    return -1; // return the default value
+}
+
+// ================================================================================
 static void
 usage(FILE* out)
 {
@@ -155,6 +166,7 @@
     fprintf(out, "  -b           (default) print the report to stdout (in proto format)\n");
     fprintf(out, "  -d           send the report into dropbox\n");
     fprintf(out, "  -l           list available sections\n");
+    fprintf(out, "  -p           privacy spec, LOCAL, EXPLICIT or AUTOMATIC\n");
     fprintf(out, "\n");
     fprintf(out, "  SECTION     the field numbers of the incident report fields to include\n");
     fprintf(out, "\n");
@@ -166,10 +178,11 @@
     Status status;
     IncidentReportArgs args;
     enum { DEST_DROPBOX, DEST_STDOUT } destination = DEST_STDOUT;
+    int dest = -1; // default
 
     // Parse the args
     int opt;
-    while ((opt = getopt(argc, argv, "bhdl")) != -1) {
+    while ((opt = getopt(argc, argv, "bhdlp:")) != -1) {
         switch (opt) {
             case 'h':
                 usage(stdout);
@@ -183,6 +196,9 @@
             case 'd':
                 destination = DEST_DROPBOX;
                 break;
+            case 'p':
+                dest = get_dest(optarg);
+                break;
             default:
                 usage(stderr);
                 return 1;
@@ -210,8 +226,7 @@
             }
         }
     }
-
-
+    args.setDest(dest);
 
     // Start the thread pool.
     sp<ProcessState> ps(ProcessState::self());
diff --git a/cmds/incident_helper/IncidentHelper.cpp b/cmds/incident_helper/IncidentHelper.cpp
index fba5e66..787d3a1 100644
--- a/cmds/incident_helper/IncidentHelper.cpp
+++ b/cmds/incident_helper/IncidentHelper.cpp
@@ -61,6 +61,21 @@
 }
 
 // ================================================================================
+status_t NoopParser::Parse(const int in, const int out) const
+{
+    string content;
+    if (!ReadFdToString(in, &content)) {
+        fprintf(stderr, "[%s]Failed to read data from incidentd\n", this->name.string());
+        return -1;
+    }
+    if (!WriteStringToFd(content, out)) {
+        fprintf(stderr, "[%s]Failed to write data to incidentd\n", this->name.string());
+        return -1;
+    }
+    return NO_ERROR;
+}
+
+// ================================================================================
 status_t ReverseParser::Parse(const int in, const int out) const
 {
     string content;
@@ -189,4 +204,4 @@
     }
     fprintf(stderr, "[%s]Proto size: %d bytes\n", this->name.string(), proto.ByteSize());
     return NO_ERROR;
-}
\ No newline at end of file
+}
diff --git a/cmds/incident_helper/IncidentHelper.h b/cmds/incident_helper/IncidentHelper.h
index f319c41..f6579a2 100644
--- a/cmds/incident_helper/IncidentHelper.h
+++ b/cmds/incident_helper/IncidentHelper.h
@@ -36,6 +36,17 @@
 };
 
 /**
+ * No op parser returns what it reads
+ */
+class NoopParser : public TextParserBase {
+public:
+    NoopParser() : TextParserBase(String8("NoopParser")) {};
+    ~NoopParser() {};
+
+    virtual status_t Parse(const int in, const int out) const;
+};
+
+/**
  * This parser is used for testing only, results in timeout.
  */
 class TimeoutParser : public TextParserBase {
diff --git a/cmds/incident_helper/main.cpp b/cmds/incident_helper/main.cpp
index 333344b..296d300 100644
--- a/cmds/incident_helper/main.cpp
+++ b/cmds/incident_helper/main.cpp
@@ -41,9 +41,11 @@
         case -1:
             return new TimeoutParser();
         case 0:
+            return new NoopParser();
+        case 1: // 1 is reserved for incident header so it won't be section id
             return new ReverseParser();
 /* ========================================================================= */
-        // IDs larger than 0 are reserved in incident.proto
+        // IDs larger than 1 are section ids reserved in incident.proto
         case 2000:
             return new ProcrankParser();
         case 2002:
diff --git a/cmds/incidentd/Android.mk b/cmds/incidentd/Android.mk
index 835a7b9..830bf9e 100644
--- a/cmds/incidentd/Android.mk
+++ b/cmds/incidentd/Android.mk
@@ -23,10 +23,13 @@
 LOCAL_MODULE := incidentd
 
 LOCAL_SRC_FILES := \
+        src/EncodedBuffer.cpp \
         src/FdBuffer.cpp \
         src/IncidentService.cpp \
+        src/Privacy.cpp \
         src/Reporter.cpp \
         src/Section.cpp \
+        src/io_util.cpp \
         src/main.cpp \
         src/protobuf.cpp \
         src/report_directory.cpp
@@ -69,7 +72,9 @@
 gen_src_dir:=
 GEN:=
 
+ifeq ($(BUILD_WITH_INCIDENTD_RC), true)
 LOCAL_INIT_RC := incidentd.rc
+endif
 
 include $(BUILD_EXECUTABLE)
 
@@ -88,12 +93,16 @@
 LOCAL_C_INCLUDES += $(LOCAL_PATH)/src
 
 LOCAL_SRC_FILES := \
+    src/EncodedBuffer.cpp \
     src/FdBuffer.cpp \
+    src/Privacy.cpp \
     src/Reporter.cpp \
     src/Section.cpp \
+    src/io_util.cpp \
     src/protobuf.cpp \
     src/report_directory.cpp \
     tests/section_list.cpp \
+    tests/EncodedBuffer_test.cpp \
     tests/FdBuffer_test.cpp \
     tests/Reporter_test.cpp \
     tests/Section_test.cpp \
diff --git a/cmds/incidentd/src/EncodedBuffer.cpp b/cmds/incidentd/src/EncodedBuffer.cpp
new file mode 100644
index 0000000..3d20548
--- /dev/null
+++ b/cmds/incidentd/src/EncodedBuffer.cpp
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#include "EncodedBuffer.h"
+#include "io_util.h"
+#include "protobuf.h"
+
+#include <deque>
+
+const size_t BUFFER_SIZE = 4 * 1024; // 4 KB
+
+/**
+ * Read varint from iterator, the iterator will point to next available byte.
+ * Return the number of bytes of the varint.
+ */
+static uint32_t
+read_raw_varint(FdBuffer::iterator& it)
+{
+    uint32_t val = 0;
+    int i = 0;
+    bool hasNext = true;
+    while (hasNext) {
+        hasNext = ((*it & 0x80) != 0);
+        val += (*it & 0x7F) << (7*i);
+        it++;
+        i++;
+    }
+    return val;
+}
+
+/**
+ * Write the field to buf based on the wire type, iterator will point to next field.
+ * If skip is set to true, no data will be written to buf. Return number of bytes written.
+ */
+static size_t
+write_field_or_skip(FdBuffer::iterator &iterator, vector<uint8_t> &buf, uint8_t wireType, bool skip)
+{
+    FdBuffer::iterator snapshot = iterator.snapshot();
+    size_t bytesToWrite = 0;
+    uint32_t varint = 0;
+    switch (wireType) {
+        case WIRE_TYPE_VARINT:
+            varint = read_raw_varint(iterator);
+            if(!skip) return write_raw_varint(buf, varint);
+            break;
+        case WIRE_TYPE_FIXED64:
+            bytesToWrite = 8;
+            break;
+        case WIRE_TYPE_LENGTH_DELIMITED:
+            bytesToWrite = read_raw_varint(iterator);
+            if(!skip) write_raw_varint(buf, bytesToWrite);
+            break;
+        case WIRE_TYPE_FIXED32:
+            bytesToWrite = 4;
+            break;
+    }
+    if (skip) {
+        iterator += bytesToWrite;
+    } else {
+        for (size_t i=0; i<bytesToWrite; i++) {
+            buf.push_back(*iterator);
+            iterator++;
+        }
+    }
+    return skip ? 0 : iterator - snapshot;
+}
+
+/**
+ * Strip next field based on its private policy and request spec, then stores data in buf.
+ * Return NO_ERROR if succeeds, otherwise BAD_VALUE is returned to indicate bad data in FdBuffer.
+ *
+ * The iterator must point to the head of a protobuf formatted field for successful operation.
+ * After exit with NO_ERROR, iterator points to the next protobuf field's head.
+ */
+static status_t
+stripField(FdBuffer::iterator &iterator, vector<uint8_t> &buf, const Privacy* parentPolicy, const PrivacySpec& spec)
+{
+    if (iterator.outOfBound() || parentPolicy == NULL) return BAD_VALUE;
+
+    uint32_t varint = read_raw_varint(iterator);
+    uint8_t wireType = read_wire_type(varint);
+    uint32_t fieldId = read_field_id(varint);
+    const Privacy* policy = parentPolicy->lookup(fieldId);
+
+    if (policy == NULL || !policy->IsMessageType() || !policy->HasChildren()) {
+        bool skip = !spec.CheckPremission(policy);
+        size_t amt = buf.size();
+        if (!skip) amt += write_header(buf, fieldId, wireType);
+        amt += write_field_or_skip(iterator, buf, wireType, skip); // point to head of next field
+        return buf.size() != amt ? BAD_VALUE : NO_ERROR;
+    }
+    // current field is message type and its sub-fields have extra privacy policies
+    deque<vector<uint8_t>> q;
+    uint32_t msgSize = read_raw_varint(iterator);
+    size_t finalSize = 0;
+    FdBuffer::iterator start = iterator.snapshot();
+    while ((iterator - start) != (int)msgSize) {
+        vector<uint8_t> v;
+        status_t err = stripField(iterator, v, policy, spec);
+        if (err != NO_ERROR) return err;
+        if (v.empty()) continue;
+        q.push_back(v);
+        finalSize += v.size();
+    }
+
+    write_header(buf, fieldId, wireType);
+    write_raw_varint(buf, finalSize);
+    buf.reserve(finalSize);
+    while (!q.empty()) {
+        vector<uint8_t> subField = q.front();
+        for (vector<uint8_t>::iterator it = subField.begin(); it != subField.end(); it++) {
+            buf.push_back(*it);
+        }
+        q.pop_front();
+    }
+    return NO_ERROR;
+}
+
+// ================================================================================
+EncodedBuffer::EncodedBuffer(const FdBuffer& buffer, const Privacy* policy)
+        : mFdBuffer(buffer),
+          mPolicy(policy),
+          mBuffers(),
+          mSize(0)
+{
+}
+
+EncodedBuffer::~EncodedBuffer()
+{
+}
+
+status_t
+EncodedBuffer::strip(const PrivacySpec& spec)
+{
+    // optimization when no strip happens
+    if (mPolicy == NULL || !mPolicy->HasChildren() || spec.RequireAll()) {
+        if (spec.CheckPremission(mPolicy)) mSize = mFdBuffer.size();
+        return NO_ERROR;
+    }
+
+    FdBuffer::iterator it = mFdBuffer.begin();
+    vector<uint8_t> field;
+    field.reserve(BUFFER_SIZE);
+
+    while (it != mFdBuffer.end()) {
+        status_t err = stripField(it, field, mPolicy, spec);
+        if (err != NO_ERROR) return err;
+        if (field.size() > BUFFER_SIZE) { // rotate to another chunk if buffer size exceeds
+            mBuffers.push_back(field);
+            mSize += field.size();
+            field.clear();
+        }
+    }
+    if (!field.empty()) {
+        mBuffers.push_back(field);
+        mSize += field.size();
+    }
+    return NO_ERROR;
+}
+
+void
+EncodedBuffer::clear()
+{
+    mSize = 0;
+    mBuffers.clear();
+}
+
+size_t
+EncodedBuffer::size() const { return mSize; }
+
+status_t
+EncodedBuffer::flush(int fd)
+{
+    if (size() == mFdBuffer.size()) return mFdBuffer.flush(fd);
+
+    for (vector<vector<uint8_t>>::iterator it = mBuffers.begin(); it != mBuffers.end(); it++) {
+        status_t err = write_all(fd, it->data(), it->size());
+        if (err != NO_ERROR) return err;
+    }
+    return NO_ERROR;
+}
+
diff --git a/cmds/incidentd/src/EncodedBuffer.h b/cmds/incidentd/src/EncodedBuffer.h
new file mode 100644
index 0000000..ea8603a
--- /dev/null
+++ b/cmds/incidentd/src/EncodedBuffer.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2017 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 ENCODED_BUFFER_H
+#define ENCODED_BUFFER_H
+
+#include "FdBuffer.h"
+#include "Privacy.h"
+
+#include <stdint.h>
+#include <vector>
+
+/**
+ * EncodedBuffer is constructed from FdBuffer which holds original protobuf formatted data and
+ * its privacy policy in its tagged proto message. The class strips PII-sensitive fields
+ * based on the request and holds stripped data in its buffer for output.
+ */
+class EncodedBuffer
+{
+public:
+    EncodedBuffer(const FdBuffer& buffer, const Privacy* policy);
+    ~EncodedBuffer();
+
+    /**
+     * Strip based on the request and hold data in its own buffer. Return NO_ERROR if strip succeeds.
+     */
+    status_t strip(const PrivacySpec& spec);
+
+    /**
+     * Clear encoded buffer so it can be reused by another request.
+     */
+    void clear();
+
+    /**
+     * Return the size of the stripped data.
+     */
+    size_t size() const;
+
+    /**
+     * Flush buffer to the given fd. NO_ERROR is returned if the flush succeeds.
+     */
+    status_t flush(int fd);
+
+private:
+    const FdBuffer& mFdBuffer;
+    const Privacy* mPolicy;
+    vector<vector<uint8_t>> mBuffers;
+    size_t mSize;
+};
+
+#endif // ENCODED_BUFFER_H
\ No newline at end of file
diff --git a/cmds/incidentd/src/FdBuffer.cpp b/cmds/incidentd/src/FdBuffer.cpp
index 4d6a36c..bb399b5 100644
--- a/cmds/incidentd/src/FdBuffer.cpp
+++ b/cmds/incidentd/src/FdBuffer.cpp
@@ -17,6 +17,7 @@
 #define LOG_TAG "incidentd"
 
 #include "FdBuffer.h"
+#include "io_util.h"
 
 #include <cutils/log.h>
 #include <utils/SystemClock.h>
@@ -239,25 +240,32 @@
 }
 
 size_t
-FdBuffer::size()
+FdBuffer::size() const
 {
     if (mBuffers.empty()) return 0;
     return ((mBuffers.size() - 1) * BUFFER_SIZE) + mCurrentWritten;
 }
 
 status_t
-FdBuffer::write(ReportRequestSet* reporter)
+FdBuffer::flush(int fd) const
 {
-    const int N = mBuffers.size() - 1;
-    for (int i=0; i<N; i++) {
-        reporter->write(mBuffers[i], BUFFER_SIZE);
+    size_t i=0;
+    status_t err = NO_ERROR;
+    for (i=0; i<mBuffers.size()-1; i++) {
+        err = write_all(fd, mBuffers[i], BUFFER_SIZE);
+        if (err != NO_ERROR) return err;
     }
-    reporter->write(mBuffers[N], mCurrentWritten);
-    return NO_ERROR;
+    return write_all(fd, mBuffers[i], mCurrentWritten);
 }
 
 FdBuffer::iterator
-FdBuffer::end()
+FdBuffer::begin() const
+{
+    return iterator(*this, 0, 0);
+}
+
+FdBuffer::iterator
+FdBuffer::end() const
 {
     if (mBuffers.empty() || mCurrentWritten < 0) return begin();
     if (mCurrentWritten == BUFFER_SIZE)
@@ -266,6 +274,17 @@
     return FdBuffer::iterator(*this, mBuffers.size() - 1, mCurrentWritten);
 }
 
+// ===============================================================================
+FdBuffer::iterator::iterator(const FdBuffer& buffer, ssize_t index, ssize_t offset)
+        : mFdBuffer(buffer),
+          mIndex(index),
+          mOffset(offset)
+{
+}
+
+FdBuffer::iterator&
+FdBuffer::iterator::operator=(iterator& other) const { return other; }
+
 FdBuffer::iterator&
 FdBuffer::iterator::operator+(size_t offset)
 {
@@ -278,8 +297,50 @@
     return *this;
 }
 
+FdBuffer::iterator&
+FdBuffer::iterator::operator+=(size_t offset) { return *this + offset; }
+
+FdBuffer::iterator&
+FdBuffer::iterator::operator++() { return *this + 1; }
+
+FdBuffer::iterator
+FdBuffer::iterator::operator++(int) { return *this + 1; }
+
+bool
+FdBuffer::iterator::operator==(iterator other) const
+{
+    return mIndex == other.mIndex && mOffset == other.mOffset;
+}
+
+bool
+FdBuffer::iterator::operator!=(iterator other) const { return !(*this == other); }
+
+int
+FdBuffer::iterator::operator-(iterator other) const
+{
+    return (int)bytesRead() - (int)other.bytesRead();
+}
+
+FdBuffer::iterator::reference
+FdBuffer::iterator::operator*() const
+{
+    return mFdBuffer.mBuffers[mIndex][mOffset];
+}
+
+FdBuffer::iterator
+FdBuffer::iterator::snapshot() const
+{
+    return FdBuffer::iterator(mFdBuffer, mIndex, mOffset);
+}
+
 size_t
-FdBuffer::iterator::bytesRead()
+FdBuffer::iterator::bytesRead() const
 {
     return mIndex * BUFFER_SIZE + mOffset;
 }
+
+bool
+FdBuffer::iterator::outOfBound() const
+{
+    return bytesRead() > mFdBuffer.size();
+}
diff --git a/cmds/incidentd/src/FdBuffer.h b/cmds/incidentd/src/FdBuffer.h
index e9a53ff..dfe39c6 100644
--- a/cmds/incidentd/src/FdBuffer.h
+++ b/cmds/incidentd/src/FdBuffer.h
@@ -17,8 +17,6 @@
 #ifndef FD_BUFFER_H
 #define FD_BUFFER_H
 
-#include "Reporter.h"
-
 #include <utils/Errors.h>
 
 #include <vector>
@@ -55,7 +53,7 @@
     /**
      * Whether we timed out.
      */
-    bool timedOut() { return mTimedOut; }
+    bool timedOut() const { return mTimedOut; }
 
     /**
      * If more than 4 MB is read, we truncate the data and return success.
@@ -65,23 +63,22 @@
      * happens, truncated() will return true so it can be marked. If the data is
      * exactly 4 MB, truncated is still set. Sorry.
      */
-    bool truncated() { return mTruncated; }
+    bool truncated() const { return mTruncated; }
 
     /**
      * How much data was read.
      */
-    size_t size();
+    size_t size() const;
 
     /**
-     * [Deprecated] Write the data that we recorded to the fd given.
-     * TODO: remove it once the iterator api is working
+     * Flush all the data to given file descriptor;
      */
-    status_t write(ReportRequestSet* requests);
+    status_t flush(int fd) const;
 
     /**
      * How long the read took in milliseconds.
      */
-    int64_t durationMs() { return mFinishTime - mStartTime; }
+    int64_t durationMs() const { return mFinishTime - mStartTime; }
 
     /**
      * Read data stored in FdBuffer
@@ -89,30 +86,31 @@
     class iterator;
     friend class iterator;
     class iterator : public std::iterator<std::random_access_iterator_tag, uint8_t> {
+    public:
+        iterator(const FdBuffer& buffer, ssize_t index, ssize_t offset);
+        iterator& operator=(iterator& other) const;
+        iterator& operator+(size_t offset);
+        iterator& operator+=(size_t offset);
+        iterator& operator++();
+        iterator operator++(int);
+        bool operator==(iterator other) const;
+        bool operator!=(iterator other) const;
+        int operator-(iterator other) const;
+        reference operator*() const;
+
+        // return the snapshot of the current iterator
+        iterator snapshot() const;
+        // how many bytes are read
+        size_t bytesRead() const;
+        // random access could make the iterator out of bound
+        bool outOfBound() const;
     private:
-        FdBuffer& mFdBuffer;
+        const 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();
+    iterator begin() const;
+    iterator end() const;
 
 private:
     vector<uint8_t*> mBuffers;
@@ -123,19 +121,4 @@
     bool mTruncated;
 };
 
-class Fpipe {
-public:
-    Fpipe() {}
-    bool close() { return !(::close(mFds[0]) || ::close(mFds[1])); }
-    ~Fpipe() { close(); }
-
-    inline bool init() { return pipe(mFds) != -1; }
-    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/Privacy.cpp b/cmds/incidentd/src/Privacy.cpp
new file mode 100644
index 0000000..dbab548
--- /dev/null
+++ b/cmds/incidentd/src/Privacy.cpp
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#include "Privacy.h"
+
+// DESTINATION enum value
+const uint8_t DEST_LOCAL = 0;
+const uint8_t DEST_EXPLICIT = 1;
+const uint8_t DEST_AUTOMATIC = 2;
+
+// type of the field, identitical to protobuf definition
+const uint8_t TYPE_STRING = 9;
+const uint8_t TYPE_MESSAGE = 11;
+
+Privacy::Privacy(uint32_t field_id, uint8_t type, uint8_t dest)
+    : field_id(field_id),
+      type(type),
+      children(NULL),
+      dest(dest),
+      patterns(NULL)
+{
+}
+
+Privacy::Privacy(uint32_t field_id, const Privacy** children)
+    : field_id(field_id),
+      type(TYPE_MESSAGE),
+      children(children),
+      dest(DEST_DEFAULT_VALUE), // this will be ignored
+      patterns(NULL)
+{
+}
+
+Privacy::Privacy(uint32_t field_id, uint8_t dest, const char** patterns)
+    : field_id(field_id),
+      type(TYPE_STRING),
+      children(NULL),
+      dest(dest),
+      patterns(patterns)
+{
+}
+
+bool
+Privacy::IsMessageType() const { return type == TYPE_MESSAGE; }
+
+bool
+Privacy::IsStringType() const { return type == TYPE_STRING; }
+
+bool
+Privacy::HasChildren() const { return children != NULL && children[0] != NULL; }
+
+const Privacy*
+Privacy::lookup(uint32_t fieldId) const
+{
+    if (children == NULL) return NULL;
+    for (int i=0; children[i] != NULL; i++) {
+        if (children[i]->field_id == fieldId) return children[i];
+        // This assumes the list's field id is in ascending order and must be true.
+        if (children[i]->field_id > fieldId) return NULL;
+    }
+    return NULL;
+}
+
+static bool allowDest(const uint8_t dest, const uint8_t policy)
+{
+    switch (policy) {
+    case DEST_LOCAL:
+        return dest == DEST_LOCAL;
+    case DEST_EXPLICIT:
+        return dest == DEST_LOCAL || dest == DEST_EXPLICIT;
+    case DEST_AUTOMATIC:
+        return true;
+    default:
+        return false;
+    }
+}
+
+bool
+PrivacySpec::operator<(const PrivacySpec& other) const
+{
+  return dest < other.dest;
+}
+
+bool
+PrivacySpec::CheckPremission(const Privacy* privacy) const
+{
+    uint8_t policy = privacy == NULL ? DEST_DEFAULT_VALUE : privacy->dest;
+    return allowDest(dest, policy);
+}
+
+bool
+PrivacySpec::RequireAll() const { return dest == DEST_LOCAL; }
+
+PrivacySpec new_spec_from_args(int dest) {
+  if (dest < 0) return PrivacySpec();
+  return PrivacySpec(dest);
+}
+
+PrivacySpec get_default_dropbox_spec() { return PrivacySpec(DEST_AUTOMATIC); }
\ No newline at end of file
diff --git a/cmds/incidentd/src/Privacy.h b/cmds/incidentd/src/Privacy.h
new file mode 100644
index 0000000..c56ba9b8
--- /dev/null
+++ b/cmds/incidentd/src/Privacy.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2017 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 PRIVACY_H
+#define PRIVACY_H
+
+#include <stdint.h>
+
+// This is the default value of DEST enum
+const uint8_t DEST_DEFAULT_VALUE = 1;
+
+/*
+ * In order not to depend on libprotobuf-cpp-full nor libplatformprotos in incidentd,
+ * privacy options's data structure are explicitly redefined in this file.
+ */
+struct Privacy {
+    uint32_t field_id;
+    uint8_t type;
+    // ignore parent's privacy flags if children are set, NULL-terminated
+    const Privacy** children;
+
+    // the following fields are identitical to
+    // frameworks/base/libs/incident/proto/android/privacy.proto
+    uint8_t dest;
+    const char** patterns; // only set when type is string
+
+    Privacy(uint32_t field_id, uint8_t type, uint8_t dest); // generic constructor
+    Privacy(uint32_t field_id, const Privacy** children); // used for message type
+    Privacy(uint32_t field_id, uint8_t dest, const char** patterns); // used for string type
+
+    bool IsMessageType() const;
+    bool IsStringType() const;
+    bool HasChildren() const;
+    const Privacy* lookup(uint32_t fieldId) const;
+};
+
+/**
+ * PrivacySpec defines the request has what level of privacy authorization.
+ * For example, a device without user consent should only be able to upload AUTOMATIC fields.
+ */
+class PrivacySpec {
+public:
+    const uint8_t dest;
+
+    PrivacySpec() : dest(DEST_DEFAULT_VALUE) {}
+    PrivacySpec(uint8_t dest) : dest(dest) {}
+
+    bool operator<(const PrivacySpec& other) const;
+
+    bool CheckPremission(const Privacy* privacy) const;
+    bool RequireAll() const;
+};
+
+PrivacySpec new_spec_from_args(int dest);
+PrivacySpec get_default_dropbox_spec();
+
+#endif // PRIVACY_H
diff --git a/cmds/incidentd/src/Reporter.cpp b/cmds/incidentd/src/Reporter.cpp
index 4ffc119..11347e2 100644
--- a/cmds/incidentd/src/Reporter.cpp
+++ b/cmds/incidentd/src/Reporter.cpp
@@ -17,7 +17,6 @@
 #define LOG_TAG "incidentd"
 
 #include "Reporter.h"
-#include "protobuf.h"
 
 #include "report_directory.h"
 #include "section_list.h"
@@ -38,20 +37,6 @@
 static const char* INCIDENT_DIRECTORY = "/data/misc/incidents/";
 
 // ================================================================================
-static status_t write_all(int fd, uint8_t const* buf, size_t size)
-{
-    while (size > 0) {
-        ssize_t amt = ::write(fd, buf, size);
-        if (amt < 0) {
-            return -errno;
-        }
-        size -= amt;
-        buf += amt;
-    }
-    return NO_ERROR;
-}
-
-// ================================================================================
 ReportRequest::ReportRequest(const IncidentReportArgs& a,
             const sp<IIncidentReportStatusListener> &l, int f)
     :args(a),
@@ -65,11 +50,16 @@
 {
 }
 
+bool
+ReportRequest::ok()
+{
+    return fd >= 0 && err == NO_ERROR;
+}
+
 // ================================================================================
 ReportRequestSet::ReportRequestSet()
     :mRequests(),
      mSections(),
-     mWritableCount(0),
      mMainFd(-1)
 {
 }
@@ -84,45 +74,12 @@
 {
     mRequests.push_back(request);
     mSections.merge(request->args);
-    mWritableCount++;
 }
 
 void
 ReportRequestSet::setMainFd(int fd)
 {
     mMainFd = fd;
-    mWritableCount++;
-}
-
-status_t
-ReportRequestSet::write(uint8_t const* buf, size_t size)
-{
-    status_t err = EBADF;
-
-    // The streaming ones
-    int const N = mRequests.size();
-    for (int i=N-1; i>=0; i--) {
-        sp<ReportRequest> request = mRequests[i];
-        if (request->fd >= 0 && request->err == NO_ERROR) {
-            err = write_all(request->fd, buf, size);
-            if (err != NO_ERROR) {
-                request->err = err;
-                mWritableCount--;
-            }
-        }
-    }
-
-    // The dropbox file
-    if (mMainFd >= 0) {
-        err = write_all(mMainFd, buf, size);
-        if (err != NO_ERROR) {
-            mMainFd = -1;
-            mWritableCount--;
-        }
-    }
-
-    // Return an error only when there are no FDs to write.
-    return mWritableCount > 0 ? NO_ERROR : err;
 }
 
 bool
@@ -164,6 +121,7 @@
     status_t err = NO_ERROR;
     bool needMainFd = false;
     int mainFd = -1;
+    HeaderSection headers;
 
     // See if we need the main file
     for (ReportRequestSet::iterator it=batch.begin(); it!=batch.end(); it++) {
@@ -176,7 +134,7 @@
         // Create the directory
         if (!isTest) err = create_directory(mIncidentDirectory);
         if (err != NO_ERROR) {
-            goto done;
+            goto DONE;
         }
 
         // If there are too many files in the directory (for whatever reason),
@@ -187,7 +145,7 @@
         // Open the file.
         err = create_file(&mainFd);
         if (err != NO_ERROR) {
-            goto done;
+            goto DONE;
         }
 
         // Add to the set
@@ -202,24 +160,7 @@
     }
 
     // Write the incident headers
-    for (ReportRequestSet::iterator it=batch.begin(); it!=batch.end(); it++) {
-        const sp<ReportRequest> request = (*it);
-        const vector<vector<int8_t>>& headers = request->args.headers();
-
-        for (vector<vector<int8_t>>::const_iterator buf=headers.begin(); buf!=headers.end();
-                buf++) {
-            int fd = request->fd >= 0 ? request->fd : mainFd;
-
-            uint8_t buffer[20];
-            uint8_t* p = write_length_delimited_tag_header(buffer, FIELD_ID_INCIDENT_HEADER,
-                    buf->size());
-            write_all(fd, buffer, p-buffer);
-
-            write_all(fd, (uint8_t const*)buf->data(), buf->size());
-            // If there was an error now, there will be an error later and we will remove
-            // it from the list then.
-        }
-    }
+    headers.Execute(&batch);
 
     // For each of the report fields, see if we need it, and if so, execute the command
     // and report to those that care that we're doing it.
@@ -240,7 +181,7 @@
             if (err != NO_ERROR) {
                 ALOGW("Incident section %s (%d) failed. Stopping report.",
                         (*section)->name.string(), id);
-                goto done;
+                goto DONE;
             }
 
             // Notify listener of starting
@@ -254,7 +195,7 @@
         }
     }
 
-done:
+DONE:
     // Close the file.
     if (mainFd >= 0) {
         close(mainFd);
diff --git a/cmds/incidentd/src/Reporter.h b/cmds/incidentd/src/Reporter.h
index 509611c..2615c62 100644
--- a/cmds/incidentd/src/Reporter.h
+++ b/cmds/incidentd/src/Reporter.h
@@ -40,6 +40,8 @@
     ReportRequest(const IncidentReportArgs& args,
             const sp<IIncidentReportStatusListener> &listener, int fd);
     virtual ~ReportRequest();
+
+    bool ok(); // returns true if the request is ok for write.
 };
 
 // ================================================================================
@@ -52,21 +54,16 @@
     void add(const sp<ReportRequest>& request);
     void setMainFd(int fd);
 
-    // Write to all of the fds for the requests. If a write fails, it stops
-    // writing to that fd and returns NO_ERROR. When we are out of fds to write
-    // to it returns an error.
-    status_t write(uint8_t const* buf, size_t size);
-
     typedef vector<sp<ReportRequest>>::iterator iterator;
 
     iterator begin() { return mRequests.begin(); }
     iterator end() { return mRequests.end(); }
 
+    int mainFd() { return mMainFd; }
     bool containsSection(int id);
 private:
     vector<sp<ReportRequest>> mRequests;
     IncidentReportArgs mSections;
-    int mWritableCount;
     int mMainFd;
 };
 
diff --git a/cmds/incidentd/src/Section.cpp b/cmds/incidentd/src/Section.cpp
index ac87fe3..6f052de 100644
--- a/cmds/incidentd/src/Section.cpp
+++ b/cmds/incidentd/src/Section.cpp
@@ -16,11 +16,18 @@
 
 #define LOG_TAG "incidentd"
 
+#include "EncodedBuffer.h"
+#include "FdBuffer.h"
+#include "Privacy.h"
 #include "Section.h"
+
+#include "io_util.h"
 #include "protobuf.h"
+#include "section_list.h"
 
 #include <private/android_filesystem_config.h>
 #include <binder/IServiceManager.h>
+#include <map>
 #include <mutex>
 #include <wait.h>
 #include <unistd.h>
@@ -32,7 +39,7 @@
 const char* INCIDENT_HELPER = "/system/bin/incident_helper";
 
 static pid_t
-forkAndExecuteIncidentHelper(const int id, const char* name, Fpipe& p2cPipe, Fpipe& c2pPipe)
+fork_execute_incident_helper(const int id, const char* name, Fpipe& p2cPipe, Fpipe& c2pPipe)
 {
     const char* ihArgs[] { INCIDENT_HELPER, "-s", String8::format("%d", id).string(), NULL };
 
@@ -66,14 +73,15 @@
     return pid;
 }
 
-static status_t killChild(pid_t pid) {
+// ================================================================================
+static status_t kill_child(pid_t pid) {
     int status;
     kill(pid, SIGKILL);
     if (waitpid(pid, &status, 0) == -1) return -1;
     return WIFEXITED(status) == 0 ? NO_ERROR : -WEXITSTATUS(status);
 }
 
-static status_t waitForChild(pid_t pid) {
+static status_t wait_child(pid_t pid) {
     int status;
     bool died = false;
     // wait for child to report status up to 1 seconds
@@ -82,13 +90,92 @@
         // sleep for 0.2 second
         nanosleep(&WAIT_INTERVAL_NS, NULL);
     }
-    if (!died) return killChild(pid);
+    if (!died) return kill_child(pid);
     return WIFEXITED(status) == 0 ? NO_ERROR : -WEXITSTATUS(status);
 }
+// ================================================================================
+static const Privacy*
+get_privacy_of_section(int id)
+{
+    if (id < 0) return NULL;
+    int i=0;
+    while (PRIVACY_POLICY_LIST[i] != NULL) {
+        const Privacy* p = PRIVACY_POLICY_LIST[i];
+        if (p->field_id == (uint32_t)id) return p;
+        if (p->field_id > (uint32_t)id) return NULL;
+        i++;
+    }
+    return NULL;
+}
+
+// ================================================================================
+static status_t
+write_section_header(int fd, int sectionId, size_t size)
+{
+    uint8_t buf[20];
+    uint8_t *p = write_length_delimited_tag_header(buf, sectionId, size);
+    return write_all(fd, buf, p-buf);
+}
+
+static status_t
+write_report_requests(const int id, const FdBuffer& buffer, ReportRequestSet* requests)
+{
+    status_t err = -EBADF;
+    EncodedBuffer encodedBuffer(buffer, get_privacy_of_section(id));
+    int writeable = 0;
+
+    // The streaming ones, group requests by spec in order to save unnecessary strip operations
+    map<PrivacySpec, vector<sp<ReportRequest>>> requestsBySpec;
+    for (ReportRequestSet::iterator it = requests->begin(); it != requests->end(); it++) {
+        sp<ReportRequest> request = *it;
+        if (!request->ok() || !request->args.containsSection(id)) {
+            continue;  // skip invalid request
+        }
+        PrivacySpec spec = new_spec_from_args(request->args.dest());
+        requestsBySpec[spec].push_back(request);
+    }
+
+    for (map<PrivacySpec, vector<sp<ReportRequest>>>::iterator mit = requestsBySpec.begin(); mit != requestsBySpec.end(); mit++) {
+        PrivacySpec spec = mit->first;
+        err = encodedBuffer.strip(spec);
+        if (err != NO_ERROR) return err; // it means the encodedBuffer data is corrupted.
+        if (encodedBuffer.size() == 0) continue;
+
+        for (vector<sp<ReportRequest>>::iterator it = mit->second.begin(); it != mit->second.end(); it++) {
+            sp<ReportRequest> request = *it;
+            err = write_section_header(request->fd, id, encodedBuffer.size());
+            if (err != NO_ERROR) { request->err = err; continue; }
+            err = encodedBuffer.flush(request->fd);
+            if (err != NO_ERROR) { request->err = err; continue; }
+            writeable++;
+            ALOGD("Section %d flushed %zu bytes to fd %d with spec %d", id, encodedBuffer.size(), request->fd, spec.dest);
+        }
+        encodedBuffer.clear();
+    }
+
+    // The dropbox file
+    if (requests->mainFd() >= 0) {
+        err = encodedBuffer.strip(get_default_dropbox_spec());
+        if (err != NO_ERROR) return err; // the buffer data is corrupted.
+        if (encodedBuffer.size() == 0) goto DONE;
+
+        err = write_section_header(requests->mainFd(), id, encodedBuffer.size());
+        if (err != NO_ERROR) { requests->setMainFd(-1); goto DONE; }
+        err = encodedBuffer.flush(requests->mainFd());
+        if (err != NO_ERROR) { requests->setMainFd(-1); goto DONE; }
+        writeable++;
+        ALOGD("Section %d flushed %zu bytes to dropbox %d", id, encodedBuffer.size(), requests->mainFd());
+    }
+
+DONE:
+    // only returns error if there is no fd to write to.
+    return writeable > 0 ? NO_ERROR : err;
+}
 
 // ================================================================================
 Section::Section(int i, const int64_t timeoutMs)
-    :id(i), timeoutMs(timeoutMs)
+    :id(i),
+     timeoutMs(timeoutMs)
 {
 }
 
@@ -96,24 +183,50 @@
 {
 }
 
-status_t
-Section::WriteHeader(ReportRequestSet* requests, size_t size) const
+// ================================================================================
+HeaderSection::HeaderSection()
+    :Section(FIELD_ID_INCIDENT_HEADER, 0)
 {
-    ssize_t amt;
-    uint8_t buf[20];
-    uint8_t* p = write_length_delimited_tag_header(buf, this->id, size);
-    return requests->write(buf, p-buf);
+}
+
+HeaderSection::~HeaderSection()
+{
+}
+
+status_t
+HeaderSection::Execute(ReportRequestSet* requests) const
+{
+    for (ReportRequestSet::iterator it=requests->begin(); it!=requests->end(); it++) {
+        const sp<ReportRequest> request = *it;
+        const vector<vector<int8_t>>& headers = request->args.headers();
+
+        for (vector<vector<int8_t>>::const_iterator buf=headers.begin(); buf!=headers.end(); buf++) {
+            if (buf->empty()) continue;
+
+            // So the idea is only requests with negative fd are written to dropbox file.
+            int fd = request->fd >= 0 ? request->fd : requests->mainFd();
+            write_section_header(fd, FIELD_ID_INCIDENT_HEADER, buf->size());
+            write_all(fd, (uint8_t const*)buf->data(), buf->size());
+            // If there was an error now, there will be an error later and we will remove
+            // it from the list then.
+        }
+    }
+    return NO_ERROR;
 }
 
 // ================================================================================
 FileSection::FileSection(int id, const char* filename, const int64_t timeoutMs)
-        : Section(id, timeoutMs), mFilename(filename) {
+    :Section(id, timeoutMs),
+     mFilename(filename)
+{
     name = filename;
 }
 
 FileSection::~FileSection() {}
 
-status_t FileSection::Execute(ReportRequestSet* requests) const {
+status_t
+FileSection::Execute(ReportRequestSet* requests) const
+{
     // read from mFilename first, make sure the file is available
     // add O_CLOEXEC to make sure it is closed when exec incident helper
     int fd = open(mFilename, O_RDONLY | O_CLOEXEC);
@@ -131,7 +244,7 @@
         return -errno;
     }
 
-    pid_t pid = forkAndExecuteIncidentHelper(this->id, this->name.string(), p2cPipe, c2pPipe);
+    pid_t pid = fork_execute_incident_helper(this->id, this->name.string(), p2cPipe, c2pPipe);
     if (pid == -1) {
         ALOGW("FileSection '%s' failed to fork", this->name.string());
         return -errno;
@@ -143,11 +256,11 @@
     if (readStatus != NO_ERROR || buffer.timedOut()) {
         ALOGW("FileSection '%s' failed to read data from incident helper: %s, timedout: %s, kill: %s",
             this->name.string(), strerror(-readStatus), buffer.timedOut() ? "true" : "false",
-            strerror(-killChild(pid)));
+            strerror(-kill_child(pid)));
         return readStatus;
     }
 
-    status_t ihStatus = waitForChild(pid);
+    status_t ihStatus = wait_child(pid);
     if (ihStatus != NO_ERROR) {
         ALOGW("FileSection '%s' abnormal child process: %s", this->name.string(), strerror(-ihStatus));
         return ihStatus;
@@ -155,8 +268,7 @@
 
     ALOGD("FileSection '%s' wrote %zd bytes in %d ms", this->name.string(), buffer.size(),
             (int)buffer.durationMs());
-    WriteHeader(requests, buffer.size());
-    status_t err = buffer.write(requests);
+    status_t err = write_report_requests(this->id, buffer, requests);
     if (err != NO_ERROR) {
         ALOGW("FileSection '%s' failed writing: %s", this->name.string(), strerror(-err));
         return err;
@@ -313,8 +425,7 @@
     // Write the data that was collected
     ALOGD("WorkerThreadSection '%s' wrote %zd bytes in %d ms", name.string(), buffer.size(),
             (int)buffer.durationMs());
-    WriteHeader(requests, buffer.size());
-    err = buffer.write(requests);
+    err = write_report_requests(this->id, buffer, requests);
     if (err != NO_ERROR) {
         ALOGW("WorkerThreadSection '%s' failed writing: '%s'", this->name.string(), strerror(-err));
         return err;
@@ -324,7 +435,8 @@
 }
 
 // ================================================================================
-void CommandSection::init(const char* command, va_list args)
+void
+CommandSection::init(const char* command, va_list args)
 {
     va_list copied_args;
     int numOfArgs = 0;
@@ -350,7 +462,7 @@
 }
 
 CommandSection::CommandSection(int id, const int64_t timeoutMs, const char* command, ...)
-        : Section(id, timeoutMs)
+    :Section(id, timeoutMs)
 {
     va_list args;
     va_start(args, command);
@@ -359,7 +471,7 @@
 }
 
 CommandSection::CommandSection(int id, const char* command, ...)
-        : Section(id)
+    :Section(id)
 {
     va_list args;
     va_start(args, command);
@@ -401,7 +513,7 @@
         ALOGW("CommandSection '%s' failed in executing command: %s", this->name.string(), strerror(errno));
         _exit(err); // exit with command error code
     }
-    pid_t ihPid = forkAndExecuteIncidentHelper(this->id, this->name.string(), cmdPipe, ihPipe);
+    pid_t ihPid = fork_execute_incident_helper(this->id, this->name.string(), cmdPipe, ihPipe);
     if (ihPid == -1) {
         ALOGW("CommandSection '%s' failed to fork", this->name.string());
         return -errno;
@@ -413,14 +525,14 @@
         ALOGW("CommandSection '%s' failed to read data from incident helper: %s, "
             "timedout: %s, kill command: %s, kill incident helper: %s",
             this->name.string(), strerror(-readStatus), buffer.timedOut() ? "true" : "false",
-            strerror(-killChild(cmdPid)), strerror(-killChild(ihPid)));
+            strerror(-kill_child(cmdPid)), strerror(-kill_child(ihPid)));
         return readStatus;
     }
 
     // TODO: wait for command here has one trade-off: the failed status of command won't be detected until
     //       buffer timeout, but it has advatage on starting the data stream earlier.
-    status_t cmdStatus = waitForChild(cmdPid);
-    status_t ihStatus  = waitForChild(ihPid);
+    status_t cmdStatus = wait_child(cmdPid);
+    status_t ihStatus  = wait_child(ihPid);
     if (cmdStatus != NO_ERROR || ihStatus != NO_ERROR) {
         ALOGW("CommandSection '%s' abnormal child processes, return status: command: %s, incident helper: %s",
             this->name.string(), strerror(-cmdStatus), strerror(-ihStatus));
@@ -429,8 +541,7 @@
 
     ALOGD("CommandSection '%s' wrote %zd bytes in %d ms", this->name.string(), buffer.size(),
             (int)buffer.durationMs());
-    WriteHeader(requests, buffer.size());
-    status_t err = buffer.write(requests);
+    status_t err = write_report_requests(this->id, buffer, requests);
     if (err != NO_ERROR) {
         ALOGW("CommandSection '%s' failed writing: %s", this->name.string(), strerror(-err));
         return err;
diff --git a/cmds/incidentd/src/Section.h b/cmds/incidentd/src/Section.h
index 93b4848..0a1e03e 100644
--- a/cmds/incidentd/src/Section.h
+++ b/cmds/incidentd/src/Section.h
@@ -17,7 +17,7 @@
 #ifndef SECTIONS_H
 #define SECTIONS_H
 
-#include "FdBuffer.h"
+#include "Reporter.h"
 
 #include <stdarg.h>
 #include <utils/String8.h>
@@ -42,8 +42,18 @@
     virtual ~Section();
 
     virtual status_t Execute(ReportRequestSet* requests) const = 0;
+};
 
-    status_t WriteHeader(ReportRequestSet* requests, size_t size) const;
+/**
+ * Section that generates incident headers.
+ */
+class HeaderSection : public Section
+{
+public:
+    HeaderSection();
+    virtual ~HeaderSection();
+
+    virtual status_t Execute(ReportRequestSet* requests) const;
 };
 
 /**
diff --git a/cmds/incidentd/src/io_util.cpp b/cmds/incidentd/src/io_util.cpp
new file mode 100644
index 0000000..f043d36
--- /dev/null
+++ b/cmds/incidentd/src/io_util.cpp
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#include "io_util.h"
+
+#include <unistd.h>
+
+status_t write_all(int fd, uint8_t const* buf, size_t size)
+{
+    while (size > 0) {
+        ssize_t amt = ::write(fd, buf, size);
+        if (amt < 0) {
+            return -errno;
+        }
+        size -= amt;
+        buf += amt;
+    }
+    return NO_ERROR;
+}
+
+Fpipe::Fpipe() {}
+
+Fpipe::~Fpipe() { close(); }
+
+bool Fpipe::close() { return !(::close(mFds[0]) || ::close(mFds[1])); }
+
+bool Fpipe::init() { return pipe(mFds) != -1; }
+
+int Fpipe::readFd() const { return mFds[0]; }
+
+int Fpipe::writeFd() const { return mFds[1]; }
diff --git a/cmds/incidentd/src/io_util.h b/cmds/incidentd/src/io_util.h
new file mode 100644
index 0000000..320dd6c
--- /dev/null
+++ b/cmds/incidentd/src/io_util.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2017 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 IO_UTIL_H
+#define IO_UTIL_H
+
+#include <stdint.h>
+#include <utils/Errors.h>
+
+using namespace android;
+
+status_t write_all(int fd, uint8_t const* buf, size_t size);
+
+class Fpipe {
+public:
+    Fpipe();
+    ~Fpipe();
+
+    bool init();
+    bool close();
+    int readFd() const;
+    int writeFd() const;
+
+private:
+    int mFds[2];
+};
+
+#endif // IO_UTIL_H
\ No newline at end of file
diff --git a/cmds/incidentd/src/protobuf.cpp b/cmds/incidentd/src/protobuf.cpp
index b865339..05de831 100644
--- a/cmds/incidentd/src/protobuf.cpp
+++ b/cmds/incidentd/src/protobuf.cpp
@@ -16,8 +16,17 @@
 
 #include "protobuf.h"
 
+uint8_t read_wire_type(uint32_t varint)
+{
+    return (uint8_t) (varint & 0x07);
+}
 
-uint8_t* 
+uint32_t read_field_id(uint32_t varint)
+{
+    return varint >> 3;
+}
+
+uint8_t*
 write_raw_varint(uint8_t* buf, uint32_t val)
 {
     uint8_t* p = buf;
@@ -32,7 +41,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);
@@ -40,3 +49,24 @@
     return buf;
 }
 
+size_t
+write_raw_varint(vector<uint8_t> &buf, uint32_t val)
+{
+    size_t size = 0;
+    while (true) {
+        size++;
+        if ((val & ~0x7F) == 0) {
+            buf.push_back((uint8_t) val);
+            return size;
+        } else {
+            buf.push_back((uint8_t)((val & 0x7F) | 0x80));
+            val >>= 7;
+        }
+    }
+}
+
+size_t
+write_header(vector<uint8_t> &buf, uint32_t fieldId, uint8_t wireType)
+{
+    return write_raw_varint(buf, (fieldId << 3) | wireType);
+}
\ No newline at end of file
diff --git a/cmds/incidentd/src/protobuf.h b/cmds/incidentd/src/protobuf.h
index f196ddc..fb0d69d 100644
--- a/cmds/incidentd/src/protobuf.h
+++ b/cmds/incidentd/src/protobuf.h
@@ -18,6 +18,24 @@
 #define PROTOBUF_H
 
 #include <stdint.h>
+#include <vector>
+
+using namespace std;
+
+const uint8_t WIRE_TYPE_VARINT = 0;
+const uint8_t WIRE_TYPE_FIXED64 = 1;
+const uint8_t WIRE_TYPE_LENGTH_DELIMITED = 2;
+const uint8_t WIRE_TYPE_FIXED32 = 5;
+
+/**
+ * Read the wire type from varint, it is the smallest 3 bits.
+ */
+uint8_t read_wire_type(uint32_t varint);
+
+/**
+ * read field id from varint, it is varint >> 3;
+ */
+uint32_t read_field_id(uint32_t varint);
 
 /**
  * Write a varint into the buffer. Return the next position to write at.
@@ -32,6 +50,16 @@
  */
 uint8_t* write_length_delimited_tag_header(uint8_t* buf, uint32_t fieldId, size_t size);
 
+/**
+ * Write a varint into a vector. Return the size of the varint.
+ */
+size_t write_raw_varint(vector<uint8_t> &buf, uint32_t val);
+
+/**
+ * Write a protobuf header. Return the size of the header.
+ */
+size_t write_header(vector<uint8_t> &buf, uint32_t fieldId, uint8_t wireType);
+
 enum {
     // IncidentProto.header
     FIELD_ID_INCIDENT_HEADER = 1
diff --git a/cmds/incidentd/src/section_list.h b/cmds/incidentd/src/section_list.h
index 1abdb52..4d9efd7 100644
--- a/cmds/incidentd/src/section_list.h
+++ b/cmds/incidentd/src/section_list.h
@@ -17,6 +17,7 @@
 #ifndef SECTION_LIST_H
 #define SECTION_LIST_H
 
+#include "Privacy.h"
 #include "Section.h"
 
 /**
@@ -25,37 +26,6 @@
  */
 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
diff --git a/cmds/incidentd/tests/EncodedBuffer_test.cpp b/cmds/incidentd/tests/EncodedBuffer_test.cpp
new file mode 100644
index 0000000..c51520b
--- /dev/null
+++ b/cmds/incidentd/tests/EncodedBuffer_test.cpp
@@ -0,0 +1,207 @@
+// Copyright (C) 2017 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.
+
+#include "EncodedBuffer.h"
+
+#include <android-base/file.h>
+#include <android-base/test_utils.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <string.h>
+
+using namespace android;
+using namespace android::base;
+using namespace std;
+using ::testing::StrEq;
+using ::testing::Test;
+using ::testing::internal::CaptureStdout;
+using ::testing::internal::GetCapturedStdout;
+
+const uint8_t LOCAL = 0;
+const uint8_t EXPLICIT = 1;
+const uint8_t AUTOMATIC = 2;
+
+const uint8_t OTHER_TYPE = 1;
+const uint8_t STRING_TYPE = 9;
+const uint8_t MESSAGE_TYPE = 11;
+const string STRING_FIELD_0 = "\x02\viamtestdata";
+const string VARINT_FIELD_1 = "\x08\x96\x01"; // 150
+const string STRING_FIELD_2 = "\x12\vwhatthefuck";
+const string FIX64_FIELD_3 = "\x19\xff\xff\xff\xff\xff\xff\xff\xff"; // -1
+const string FIX32_FIELD_4 = "\x25\xff\xff\xff\xff"; // -1
+const string MESSAGE_FIELD_5 = "\x2a\x10" + VARINT_FIELD_1 + STRING_FIELD_2;
+
+class EncodedBufferTest : public Test {
+public:
+    virtual void SetUp() override {
+        ASSERT_NE(tf.fd, -1);
+    }
+
+    void writeToFdBuffer(string str) {
+        ASSERT_TRUE(WriteStringToFile(str, tf.path, false));
+        ASSERT_EQ(NO_ERROR, buffer.read(tf.fd, 10000));
+    }
+
+    void assertBuffer(EncodedBuffer& buf, string expected) {
+        ASSERT_EQ(buf.size(), expected.size());
+        CaptureStdout();
+        ASSERT_EQ(buf.flush(STDOUT_FILENO), NO_ERROR);
+        ASSERT_THAT(GetCapturedStdout(), StrEq(expected));
+    }
+
+    void assertStrip(uint8_t dest, string expected, Privacy* policy) {
+        PrivacySpec spec(dest);
+        EncodedBuffer encodedBuf(buffer, policy);
+        ASSERT_EQ(encodedBuf.strip(spec), NO_ERROR);
+        assertBuffer(encodedBuf, expected);
+    }
+
+    void assertStripByFields(uint8_t dest, string expected, int size, Privacy* privacy, ...) {
+        Privacy* list[size+1];
+        list[0] = privacy;
+        va_list args;
+        va_start(args, privacy);
+        for (int i=1; i<size; i++) {
+            Privacy* p = va_arg(args, Privacy*);
+            list[i] = p;
+        }
+        va_end(args);
+        list[size] = NULL;
+        assertStrip(dest, expected, new Privacy(300, const_cast<const Privacy**>(list)));
+    }
+
+    FdBuffer buffer;
+private:
+    TemporaryFile tf;
+};
+
+TEST_F(EncodedBufferTest, NullFieldPolicy) {
+    writeToFdBuffer(STRING_FIELD_0);
+    assertStrip(EXPLICIT, STRING_FIELD_0, new Privacy(300, NULL));
+}
+
+TEST_F(EncodedBufferTest, StripSpecNotAllowed) {
+    writeToFdBuffer(STRING_FIELD_0);
+    assertStripByFields(AUTOMATIC, "", 1, new Privacy(0, STRING_TYPE, EXPLICIT));
+}
+
+TEST_F(EncodedBufferTest, StripVarintField) {
+    writeToFdBuffer(VARINT_FIELD_1);
+    assertStripByFields(EXPLICIT, "", 1, new Privacy(1, OTHER_TYPE, LOCAL));
+}
+
+TEST_F(EncodedBufferTest, StripLengthDelimitedField_String) {
+    writeToFdBuffer(STRING_FIELD_2);
+    assertStripByFields(EXPLICIT, "", 1, new Privacy(2, STRING_TYPE, LOCAL));
+}
+
+TEST_F(EncodedBufferTest, StripFixed64Field) {
+    writeToFdBuffer(FIX64_FIELD_3);
+    assertStripByFields(EXPLICIT, "", 1, new Privacy(3, OTHER_TYPE, LOCAL));
+}
+
+TEST_F(EncodedBufferTest, StripFixed32Field) {
+    writeToFdBuffer(FIX32_FIELD_4);
+    assertStripByFields(EXPLICIT, "", 1, new Privacy(4, OTHER_TYPE, LOCAL));
+}
+
+TEST_F(EncodedBufferTest, StripLengthDelimitedField_Message) {
+    writeToFdBuffer(MESSAGE_FIELD_5);
+    assertStripByFields(EXPLICIT, "", 1, new Privacy(5, MESSAGE_TYPE, LOCAL));
+}
+
+TEST_F(EncodedBufferTest, NoStripVarintField) {
+    writeToFdBuffer(VARINT_FIELD_1);
+    assertStripByFields(EXPLICIT, VARINT_FIELD_1, 1, new Privacy(1, OTHER_TYPE, AUTOMATIC));
+}
+
+TEST_F(EncodedBufferTest, NoStripLengthDelimitedField_String) {
+    writeToFdBuffer(STRING_FIELD_2);
+    assertStripByFields(EXPLICIT, STRING_FIELD_2, 1, new Privacy(2, STRING_TYPE, AUTOMATIC));
+}
+
+TEST_F(EncodedBufferTest, NoStripFixed64Field) {
+    writeToFdBuffer(FIX64_FIELD_3);
+    assertStripByFields(EXPLICIT, FIX64_FIELD_3, 1, new Privacy(3, OTHER_TYPE, AUTOMATIC));
+}
+
+TEST_F(EncodedBufferTest, NoStripFixed32Field) {
+    writeToFdBuffer(FIX32_FIELD_4);
+    assertStripByFields(EXPLICIT, FIX32_FIELD_4, 1, new Privacy(4, OTHER_TYPE, AUTOMATIC));
+}
+
+TEST_F(EncodedBufferTest, NoStripLengthDelimitedField_Message) {
+    writeToFdBuffer(MESSAGE_FIELD_5);
+    assertStripByFields(EXPLICIT, MESSAGE_FIELD_5, 1, new Privacy(5, MESSAGE_TYPE, AUTOMATIC));
+}
+
+TEST_F(EncodedBufferTest, StripVarintAndString) {
+    writeToFdBuffer(STRING_FIELD_0 + VARINT_FIELD_1 + STRING_FIELD_2
+            + FIX64_FIELD_3 + FIX32_FIELD_4);
+    string expected = STRING_FIELD_0 + FIX64_FIELD_3 + FIX32_FIELD_4;
+    assertStripByFields(EXPLICIT, expected, 2,
+            new Privacy(1, OTHER_TYPE, LOCAL), new Privacy(2, STRING_TYPE, LOCAL));
+}
+
+TEST_F(EncodedBufferTest, StripVarintAndFixed64) {
+    writeToFdBuffer(STRING_FIELD_0 + VARINT_FIELD_1 + STRING_FIELD_2
+            + FIX64_FIELD_3 + FIX32_FIELD_4);
+    string expected = STRING_FIELD_0 + STRING_FIELD_2 + FIX32_FIELD_4;
+    assertStripByFields(EXPLICIT, expected, 2,
+            new Privacy(1, OTHER_TYPE, LOCAL), new Privacy(3, OTHER_TYPE, LOCAL));
+}
+
+TEST_F(EncodedBufferTest, StripVarintInNestedMessage) {
+    writeToFdBuffer(STRING_FIELD_0 + MESSAGE_FIELD_5);
+    const Privacy* list[] = { new Privacy(1, OTHER_TYPE, LOCAL), NULL };
+    string expected = STRING_FIELD_0 + "\x2a\xd" + STRING_FIELD_2;
+    assertStripByFields(EXPLICIT, expected, 1, new Privacy(5, list));
+}
+
+TEST_F(EncodedBufferTest, StripFix64AndVarintInNestedMessage) {
+    writeToFdBuffer(STRING_FIELD_0 + FIX64_FIELD_3 + MESSAGE_FIELD_5);
+    const Privacy* list[] = { new Privacy(1, OTHER_TYPE, LOCAL), NULL };
+    string expected = STRING_FIELD_0 + "\x2a\xd" + STRING_FIELD_2;
+    assertStripByFields(EXPLICIT, expected, 2, new Privacy(3, OTHER_TYPE, LOCAL), new Privacy(5, list));
+}
+
+TEST_F(EncodedBufferTest, ClearAndStrip) {
+    string data = STRING_FIELD_0 + VARINT_FIELD_1;
+    writeToFdBuffer(data);
+    const Privacy* list[] = { new Privacy(1, OTHER_TYPE, LOCAL), NULL };
+    EncodedBuffer encodedBuf(buffer, new Privacy(300, list));
+    PrivacySpec spec1(EXPLICIT), spec2(LOCAL);
+
+    ASSERT_EQ(encodedBuf.strip(spec1), NO_ERROR);
+    assertBuffer(encodedBuf, STRING_FIELD_0);
+    ASSERT_EQ(encodedBuf.strip(spec2), NO_ERROR);
+    assertBuffer(encodedBuf, data);
+}
+
+TEST_F(EncodedBufferTest, BadDataInFdBuffer) {
+    writeToFdBuffer("iambaddata");
+    const Privacy* list[] = { new Privacy(4, OTHER_TYPE, AUTOMATIC), NULL };
+    EncodedBuffer encodedBuf(buffer, new Privacy(300, list));
+    PrivacySpec spec;
+    ASSERT_EQ(encodedBuf.strip(spec), BAD_VALUE);
+}
+
+TEST_F(EncodedBufferTest, BadDataInNestedMessage) {
+    writeToFdBuffer(STRING_FIELD_0 + MESSAGE_FIELD_5 + "aoeoe");
+    const Privacy* list[] = { new Privacy(1, OTHER_TYPE, LOCAL), NULL };
+    const Privacy* field5[] = { new Privacy(5, list), NULL };
+    EncodedBuffer encodedBuf(buffer, new Privacy(300, field5));
+    PrivacySpec spec;
+    ASSERT_EQ(encodedBuf.strip(spec), BAD_VALUE);
+}
diff --git a/cmds/incidentd/tests/FdBuffer_test.cpp b/cmds/incidentd/tests/FdBuffer_test.cpp
index 403a2ab..d1436b2 100644
--- a/cmds/incidentd/tests/FdBuffer_test.cpp
+++ b/cmds/incidentd/tests/FdBuffer_test.cpp
@@ -15,10 +15,11 @@
 #define LOG_TAG "incidentd"
 
 #include "FdBuffer.h"
+#include "io_util.h"
 
 #include <android-base/file.h>
 #include <android-base/test_utils.h>
-#include <gmock/gmock.h>
+#include <fcntl.h>
 #include <gtest/gtest.h>
 #include <signal.h>
 #include <string.h>
@@ -30,10 +31,7 @@
 
 using namespace android;
 using namespace android::base;
-using ::testing::StrEq;
 using ::testing::Test;
-using ::testing::internal::CaptureStdout;
-using ::testing::internal::GetCapturedStdout;
 
 class FdBufferTest : public Test {
 public:
@@ -50,12 +48,13 @@
     }
 
     void AssertBufferContent(const char* expected) {
-        ReportRequestSet requests;
-        requests.setMainFd(STDOUT_FILENO);
-
-        CaptureStdout();
-        ASSERT_EQ(NO_ERROR, buffer.write(&requests));
-        EXPECT_THAT(GetCapturedStdout(), StrEq(expected));
+        int i=0;
+        FdBuffer::iterator it = buffer.begin();
+        while (expected[i] != '\0') {
+            ASSERT_EQ(*it, expected[i++]);
+            it++;
+        }
+        ASSERT_EQ(it, buffer.end());
     }
 
     bool DoDataStream(int rFd, int wFd) {
@@ -99,6 +98,16 @@
     EXPECT_TRUE(it.outOfBound());
 }
 
+TEST_F(FdBufferTest, IteratorSnapshot) {
+    FdBuffer::iterator it = buffer.begin();
+    it += 4;
+    FdBuffer::iterator snapshot = it.snapshot();
+    it += 5;
+    EXPECT_TRUE(snapshot != it);
+    EXPECT_EQ(it - snapshot, 5);
+    EXPECT_EQ(snapshot - it, -5);
+}
+
 TEST_F(FdBufferTest, ReadAndIterate) {
     std::string testdata = "FdBuffer test string";
     ASSERT_TRUE(WriteStringToFile(testdata, tf.path, false));
@@ -227,7 +236,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);
+    int fd = open(testFile.c_str(), O_RDONLY | O_CLOEXEC);
     ASSERT_NE(fd, -1);
     int pid = fork();
     ASSERT_TRUE(pid != -1);
diff --git a/cmds/incidentd/tests/Reporter_test.cpp b/cmds/incidentd/tests/Reporter_test.cpp
index a774741..5d074bc 100644
--- a/cmds/incidentd/tests/Reporter_test.cpp
+++ b/cmds/incidentd/tests/Reporter_test.cpp
@@ -76,8 +76,7 @@
     };
 
 protected:
-    IBinder* onAsBinder() override { return nullptr; };
-
+    virtual IBinder* onAsBinder() override { return nullptr; };
 };
 
 class ReporterTest : public Test {
@@ -127,29 +126,7 @@
 
 TEST_F(ReporterTest, ReportRequestSetEmpty) {
     requests.setMainFd(STDOUT_FILENO);
-
-    CaptureStdout();
-    requests.write((uint8_t *) "abcdef", 6);
-    EXPECT_THAT(GetCapturedStdout(), StrEq("abcdef"));
-}
-
-TEST_F(ReporterTest, WriteToStreamFdAndMainFd) {
-    TemporaryFile tf;
-    IncidentReportArgs args;
-    sp<ReportRequest> r = new ReportRequest(args, l, tf.fd);
-
-    requests.add(r);
-    requests.setMainFd(STDOUT_FILENO);
-
-    const char* data = "abcdef";
-
-    CaptureStdout();
-    requests.write((uint8_t *) data, 6);
-    EXPECT_THAT(GetCapturedStdout(), StrEq(data));
-
-    string content;
-    ASSERT_TRUE(ReadFileToString(tf.path, &content));
-    EXPECT_THAT(content, StrEq(data));
+    ASSERT_EQ(requests.mainFd(), STDOUT_FILENO);
 }
 
 TEST_F(ReporterTest, RunReportEmpty) {
diff --git a/cmds/incidentd/tests/Section_test.cpp b/cmds/incidentd/tests/Section_test.cpp
index 93771ff..25b05b2 100644
--- a/cmds/incidentd/tests/Section_test.cpp
+++ b/cmds/incidentd/tests/Section_test.cpp
@@ -22,31 +22,80 @@
 #include <gtest/gtest.h>
 #include <string.h>
 
+const int TIMEOUT_PARSER = -1;
+const int NOOP_PARSER = 0;
+const int REVERSE_PARSER = 1;
+
 const int QUICK_TIMEOUT_MS = 100;
 
+const string VARINT_FIELD_1 = "\x08\x96\x01"; // 150
+const string STRING_FIELD_2 = "\x12\vwhatthefuck";
+const string FIX64_FIELD_3 = "\x19\xff\xff\xff\xff\xff\xff\xff\xff"; // -1
+
 using namespace android::base;
+using namespace android::binder;
 using namespace std;
 using ::testing::StrEq;
 using ::testing::internal::CaptureStdout;
 using ::testing::internal::GetCapturedStdout;
 
 // NOTICE: this test requires /system/bin/incident_helper is installed.
-TEST(SectionTest, WriteHeader) {
-    int id = 13; // expect output is 13 << 3 & 2 = 106 --> \x6a in ASCII
-    FileSection s(id, ""); // ignore the path, just used to test the header
+
+class SimpleListener : public IIncidentReportStatusListener
+{
+public:
+    SimpleListener() {};
+    virtual ~SimpleListener() {};
+
+    virtual Status onReportStarted() { return Status::ok(); };
+    virtual Status onReportSectionStatus(int /*section*/, int /*status*/) { return Status::ok(); };
+    virtual Status onReportFinished() { return Status::ok(); };
+    virtual Status onReportFailed() { return Status::ok(); };
+
+protected:
+    virtual IBinder* onAsBinder() override { return nullptr; };
+};
+
+TEST(SectionTest, HeaderSection) {
+    TemporaryFile output2;
+    HeaderSection hs;
     ReportRequestSet requests;
 
+    IncidentReportArgs args1, args2;
+    args1.addSection(1);
+    args1.addSection(2);
+    args2.setAll(true);
+
+    vector<int8_t> head1;
+    head1.push_back('a');
+    head1.push_back('x');
+    head1.push_back('e');
+
+    vector<int8_t> head2;
+    head2.push_back('p');
+    head2.push_back('u');
+    head2.push_back('p');
+
+    args1.addHeader(head1);
+    args1.addHeader(head2);
+    args2.addHeader(head2);
+
+    requests.add(new ReportRequest(args1, new SimpleListener(), -1));
+    requests.add(new ReportRequest(args2, new SimpleListener(), output2.fd));
     requests.setMainFd(STDOUT_FILENO);
 
+    string content;
     CaptureStdout();
-    ASSERT_EQ(NO_ERROR, s.WriteHeader(&requests, 300));
-    // According to protobuf encoding, 300 is "1010 1100 0000 0010" -> \xac \x02
-    EXPECT_THAT(GetCapturedStdout(), StrEq("\x6a\xac\x02"));
+    ASSERT_EQ(NO_ERROR, hs.Execute(&requests));
+    EXPECT_THAT(GetCapturedStdout(), StrEq("\n\x3" "axe\n\x03pup"));
+
+    EXPECT_TRUE(ReadFileToString(output2.path, &content));
+    EXPECT_THAT(content, StrEq("\n\x03pup"));
 }
 
 TEST(SectionTest, FileSection) {
     TemporaryFile tf;
-    FileSection fs(0, tf.path);
+    FileSection fs(REVERSE_PARSER, tf.path);
     ReportRequestSet requests;
 
     ASSERT_TRUE(tf.fd != -1);
@@ -58,13 +107,13 @@
     ASSERT_EQ(NO_ERROR, fs.Execute(&requests));
     // The input string is reversed in incident helper
     // The length is 11, in 128Varint it is "0000 1011" -> \v
-    EXPECT_THAT(GetCapturedStdout(), StrEq("\x02\vatadtsetmai"));
+    EXPECT_THAT(GetCapturedStdout(), StrEq("\xa\vatadtsetmai"));
 }
 
 TEST(SectionTest, FileSectionTimeout) {
     TemporaryFile tf;
     // id -1 is timeout parser
-    FileSection fs(-1, tf.path, QUICK_TIMEOUT_MS);
+    FileSection fs(TIMEOUT_PARSER, tf.path, QUICK_TIMEOUT_MS);
     ReportRequestSet requests;
     ASSERT_EQ(NO_ERROR, fs.Execute(&requests));
 }
@@ -84,36 +133,165 @@
 }
 
 TEST(SectionTest, CommandSectionEcho) {
-    CommandSection cs(0, "/system/bin/echo", "about", NULL);
+    CommandSection cs(REVERSE_PARSER, "/system/bin/echo", "about", NULL);
     ReportRequestSet requests;
     requests.setMainFd(STDOUT_FILENO);
     CaptureStdout();
     ASSERT_EQ(NO_ERROR, cs.Execute(&requests));
-    EXPECT_THAT(GetCapturedStdout(), StrEq("\x02\x06\ntuoba"));
+    EXPECT_THAT(GetCapturedStdout(), StrEq("\xa\x06\ntuoba"));
 }
 
 TEST(SectionTest, CommandSectionCommandTimeout) {
-    CommandSection cs(0, QUICK_TIMEOUT_MS, "/system/bin/yes", NULL);
+    CommandSection cs(NOOP_PARSER, QUICK_TIMEOUT_MS, "/system/bin/yes", NULL);
     ReportRequestSet requests;
     ASSERT_EQ(NO_ERROR, cs.Execute(&requests));
 }
 
 TEST(SectionTest, CommandSectionIncidentHelperTimeout) {
-    CommandSection cs(-1, QUICK_TIMEOUT_MS, "/system/bin/echo", "about", NULL);
+    CommandSection cs(TIMEOUT_PARSER, QUICK_TIMEOUT_MS, "/system/bin/echo", "about", NULL);
     ReportRequestSet requests;
     requests.setMainFd(STDOUT_FILENO);
     ASSERT_EQ(NO_ERROR, cs.Execute(&requests));
 }
 
 TEST(SectionTest, CommandSectionBadCommand) {
-    CommandSection cs(0, "echo", "about", NULL);
+    CommandSection cs(NOOP_PARSER, "echo", "about", NULL);
     ReportRequestSet requests;
     ASSERT_EQ(NAME_NOT_FOUND, cs.Execute(&requests));
 }
 
 TEST(SectionTest, CommandSectionBadCommandAndTimeout) {
-    CommandSection cs(-1, QUICK_TIMEOUT_MS, "nonexistcommand", "-opt", NULL);
+    CommandSection cs(TIMEOUT_PARSER, QUICK_TIMEOUT_MS, "nonexistcommand", "-opt", NULL);
     ReportRequestSet requests;
     // timeout will return first
     ASSERT_EQ(NO_ERROR, cs.Execute(&requests));
+}
+
+TEST(SectionTest, TestFilterPiiTaggedFields) {
+    TemporaryFile tf;
+    FileSection fs(NOOP_PARSER, tf.path);
+    ReportRequestSet requests;
+
+    ASSERT_TRUE(tf.fd != -1);
+    ASSERT_TRUE(WriteStringToFile(VARINT_FIELD_1 + STRING_FIELD_2 + FIX64_FIELD_3, tf.path, false));
+
+    requests.setMainFd(STDOUT_FILENO);
+
+    CaptureStdout();
+    ASSERT_EQ(NO_ERROR, fs.Execute(&requests));
+    EXPECT_THAT(GetCapturedStdout(), StrEq("\x02\r" + STRING_FIELD_2));
+}
+
+TEST(SectionTest, TestBadFdRequest) {
+    TemporaryFile input;
+    FileSection fs(NOOP_PARSER, input.path);
+    ReportRequestSet requests;
+    ASSERT_TRUE(WriteStringToFile(VARINT_FIELD_1 + STRING_FIELD_2 + FIX64_FIELD_3, input.path, false));
+
+    IncidentReportArgs args;
+    args.setAll(true);
+    args.setDest(0);
+    sp<ReportRequest> badFdRequest = new ReportRequest(args, new SimpleListener(), 1234567);
+    requests.add(badFdRequest);
+    requests.setMainFd(STDOUT_FILENO);
+
+    CaptureStdout();
+    ASSERT_EQ(NO_ERROR, fs.Execute(&requests));
+    EXPECT_THAT(GetCapturedStdout(), StrEq("\x02\r" + STRING_FIELD_2));
+    EXPECT_EQ(badFdRequest->err, -EBADF);
+}
+
+TEST(SectionTest, TestBadRequests) {
+    TemporaryFile input;
+    FileSection fs(NOOP_PARSER, input.path);
+    ReportRequestSet requests;
+    ASSERT_TRUE(WriteStringToFile(VARINT_FIELD_1 + STRING_FIELD_2 + FIX64_FIELD_3, input.path, false));
+
+    IncidentReportArgs args;
+    args.setAll(true);
+    args.setDest(0);
+    requests.add(new ReportRequest(args, new SimpleListener(), -1));
+    EXPECT_EQ(fs.Execute(&requests), -EBADF);
+}
+
+TEST(SectionTest, TestMultipleRequests) {
+    TemporaryFile input, output1, output2, output3;
+    FileSection fs(NOOP_PARSER, input.path);
+    ReportRequestSet requests;
+
+    ASSERT_TRUE(input.fd != -1);
+    ASSERT_TRUE(output1.fd != -1);
+    ASSERT_TRUE(output2.fd != -1);
+    ASSERT_TRUE(output3.fd != -1);
+    ASSERT_TRUE(WriteStringToFile(VARINT_FIELD_1 + STRING_FIELD_2 + FIX64_FIELD_3, input.path, false));
+
+    IncidentReportArgs args1, args2, args3;
+    args1.setAll(true);
+    args1.setDest(0); // LOCAL
+    args2.setAll(true); // default to explicit
+    sp<SimpleListener> l = new SimpleListener();
+    requests.add(new ReportRequest(args1, l, output1.fd));
+    requests.add(new ReportRequest(args2, l, output2.fd));
+    requests.add(new ReportRequest(args3, l, output3.fd));
+    requests.setMainFd(STDOUT_FILENO);
+
+    CaptureStdout();
+    ASSERT_EQ(NO_ERROR, fs.Execute(&requests));
+    EXPECT_THAT(GetCapturedStdout(), StrEq("\x02\r" + STRING_FIELD_2));
+
+    string content, expect;
+    expect = VARINT_FIELD_1 + STRING_FIELD_2 + FIX64_FIELD_3;
+    char c = (char) expect.size();
+    EXPECT_TRUE(ReadFileToString(output1.path, &content));
+    EXPECT_THAT(content, StrEq(string("\x02") + c + expect));
+
+    expect = STRING_FIELD_2 + FIX64_FIELD_3;
+    c = (char) expect.size();
+    EXPECT_TRUE(ReadFileToString(output2.path, &content));
+    EXPECT_THAT(content, StrEq(string("\x02") + c + expect));
+
+    // because args3 doesn't set section, so it should receive nothing
+    EXPECT_TRUE(ReadFileToString(output3.path, &content));
+    EXPECT_THAT(content, StrEq(""));
+}
+
+TEST(SectionTest, TestMultipleRequestsBySpec) {
+    TemporaryFile input, output1, output2, output3;
+    FileSection fs(NOOP_PARSER, input.path);
+    ReportRequestSet requests;
+
+    ASSERT_TRUE(input.fd != -1);
+    ASSERT_TRUE(output1.fd != -1);
+    ASSERT_TRUE(output2.fd != -1);
+    ASSERT_TRUE(output3.fd != -1);
+
+    ASSERT_TRUE(WriteStringToFile(VARINT_FIELD_1 + STRING_FIELD_2 + FIX64_FIELD_3, input.path, false));
+
+    IncidentReportArgs args1, args2, args3, args4;
+    args1.setAll(true);
+    args2.setAll(true);
+    args4.setAll(true);
+    sp<SimpleListener> l = new SimpleListener();
+    requests.add(new ReportRequest(args1, l, output1.fd));
+    requests.add(new ReportRequest(args2, l, output2.fd));
+    requests.add(new ReportRequest(args3, l, output3.fd));
+    requests.setMainFd(STDOUT_FILENO);
+
+    CaptureStdout();
+    ASSERT_EQ(NO_ERROR, fs.Execute(&requests));
+    EXPECT_THAT(GetCapturedStdout(), StrEq("\x02\r" + STRING_FIELD_2));
+
+    string content, expect;
+    expect = STRING_FIELD_2 + FIX64_FIELD_3;
+    char c = (char) expect.size();
+
+    // output1 and output2 are the same
+    EXPECT_TRUE(ReadFileToString(output1.path, &content));
+    EXPECT_THAT(content, StrEq(string("\x02") + c + expect));
+    EXPECT_TRUE(ReadFileToString(output2.path, &content));
+    EXPECT_THAT(content, StrEq(string("\x02") + c + expect));
+
+    // because args3 doesn't set section, so it should receive nothing
+    EXPECT_TRUE(ReadFileToString(output3.path, &content));
+    EXPECT_THAT(content, StrEq(""));
 }
\ No newline at end of file
diff --git a/cmds/incidentd/tests/section_list.cpp b/cmds/incidentd/tests/section_list.cpp
index f005335..3722c72 100644
--- a/cmds/incidentd/tests/section_list.cpp
+++ b/cmds/incidentd/tests/section_list.cpp
@@ -4,3 +4,18 @@
 const Section* SECTION_LIST[] = {
     NULL
 };
+
+const uint8_t LOCAL = 0;
+const uint8_t EXPLICIT = 1;
+const uint8_t AUTOMATIC = 2;
+
+const Privacy* list[] = {
+    new Privacy(1, 1, LOCAL),
+    new Privacy(2, AUTOMATIC, (const char**)NULL),
+    NULL };
+
+const Privacy* PRIVACY_POLICY_LIST[] = {
+    new Privacy(0, list),
+    new Privacy(1, 9, AUTOMATIC),
+    NULL
+};
\ No newline at end of file
diff --git a/core/java/android/os/IncidentReportArgs.java b/core/java/android/os/IncidentReportArgs.java
index abb3161..fd0ebcf 100644
--- a/core/java/android/os/IncidentReportArgs.java
+++ b/core/java/android/os/IncidentReportArgs.java
@@ -35,6 +35,7 @@
     private final IntArray mSections = new IntArray();
     private final ArrayList<byte[]> mHeaders = new ArrayList<byte[]>();
     private boolean mAll;
+    private int mDest;
 
     /**
      * Construct an incident report args with no fields.
@@ -69,6 +70,8 @@
         for (int i=0; i<N; i++) {
             out.writeByteArray(mHeaders.get(i));
         }
+
+        out.writeInt(mDest);
     }
 
     public void readFromParcel(Parcel in) {
@@ -85,6 +88,8 @@
         for (int i=0; i<N; i++) {
             mHeaders.add(in.createByteArray());
         }
+
+        mDest = in.readInt();
     }
 
     public static final Parcelable.Creator<IncidentReportArgs> CREATOR
@@ -118,7 +123,8 @@
         }
         sb.append(", ");
         sb.append(mHeaders.size());
-        sb.append(" headers)");
+        sb.append(" headers), ");
+        sb.append("Dest enum value: ").append(mDest);
         return sb.toString();
     }
 
@@ -133,6 +139,14 @@
     }
 
     /**
+     * Set this incident report privacy policy spec.
+     * @hide
+     */
+    public void setPrivacyPolicy(int dest) {
+        mDest = dest;
+    }
+
+    /**
      * Add this section to the incident report. Skip if the input is smaller than 2 since section
      * id are only valid for positive integer as Protobuf field id. Here 1 is reserved for Header.
      */
diff --git a/core/proto/android/os/incident.proto b/core/proto/android/os/incident.proto
index 5dfcd2a..aab4142 100644
--- a/core/proto/android/os/incident.proto
+++ b/core/proto/android/os/incident.proto
@@ -31,22 +31,12 @@
 import "frameworks/base/core/proto/android/service/power.proto";
 import "frameworks/base/core/proto/android/service/print.proto";
 import "frameworks/base/core/proto/android/providers/settings.proto";
+import "frameworks/base/core/proto/android/os/incidentheader.proto";
 import "frameworks/base/core/proto/android/os/kernelwake.proto";
 import "frameworks/base/core/proto/android/os/procrank.proto";
 
 package android.os;
 
-message IncidentHeaderProto {
-    enum Cause {
-        CAUSE_UNKNOWN = 0;
-        CAUSE_USER = 1;
-        CAUSE_ANR = 2;
-        CAUSE_CRASH = 3;
-    }
-
-    Cause cause = 1;
-}
-
 // privacy field options must not be set at this level because all
 // the sections are able to be controlled and configured by section ids.
 // Instead privacy field options need to be configured in each section proto message.
diff --git a/core/proto/android/os/incidentheader.proto b/core/proto/android/os/incidentheader.proto
new file mode 100644
index 0000000..55a0616
--- /dev/null
+++ b/core/proto/android/os/incidentheader.proto
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+syntax = "proto3";
+
+option java_multiple_files = true;
+option java_outer_classname = "IncidentHeaderProtoMetadata";
+
+package android.os;
+
+message IncidentHeaderProto {
+    enum Cause {
+        CAUSE_UNKNOWN = 0;
+        CAUSE_USER = 1;
+        CAUSE_ANR = 2;
+        CAUSE_CRASH = 3;
+    }
+
+    Cause cause = 1;
+}
+
diff --git a/libs/incident/include/android/os/IncidentReportArgs.h b/libs/incident/include/android/os/IncidentReportArgs.h
index 956ef6c..da80989 100644
--- a/libs/incident/include/android/os/IncidentReportArgs.h
+++ b/libs/incident/include/android/os/IncidentReportArgs.h
@@ -39,12 +39,13 @@
     virtual status_t readFromParcel(const Parcel* in);
 
     void setAll(bool all);
+    void setDest(int dest);
     void addSection(int section);
     void addHeader(const vector<int8_t>& header);
 
-    inline bool all() const { return mAll; };
+    inline bool all() const { return mAll; }
     bool containsSection(int section) const;
-
+    inline int dest() const { return mDest; }
     inline const set<int>& sections() const { return mSections; }
     inline const vector<vector<int8_t>>& headers() const { return mHeaders; }
 
@@ -54,6 +55,7 @@
     set<int> mSections;
     vector<vector<int8_t>> mHeaders;
     bool mAll;
+    int mDest;
 };
 
 }
diff --git a/libs/incident/src/IncidentReportArgs.cpp b/libs/incident/src/IncidentReportArgs.cpp
index f604909..e628722 100644
--- a/libs/incident/src/IncidentReportArgs.cpp
+++ b/libs/incident/src/IncidentReportArgs.cpp
@@ -25,14 +25,16 @@
 
 IncidentReportArgs::IncidentReportArgs()
     :mSections(),
-     mAll(false)
+     mAll(false),
+     mDest(-1)
 {
 }
 
 IncidentReportArgs::IncidentReportArgs(const IncidentReportArgs& that)
     :mSections(that.mSections),
      mHeaders(that.mHeaders),
-     mAll(that.mAll)
+     mAll(that.mAll),
+     mDest(that.mDest)
 {
 }
 
@@ -74,6 +76,11 @@
         }
     }
 
+    err = out->writeInt32(mDest);
+    if (err != NO_ERROR) {
+        return err;
+    }
+
     return NO_ERROR;
 }
 
@@ -120,6 +127,13 @@
         }
     }
 
+    int32_t dest;
+    err = in->readInt32(&dest);
+    if (err != NO_ERROR) {
+        return err;
+    }
+    mDest = dest;
+
     return OK;
 }
 
@@ -133,6 +147,12 @@
 }
 
 void
+IncidentReportArgs::setDest(int dest)
+{
+    mDest = dest;
+}
+
+void
 IncidentReportArgs::addSection(int section)
 {
     if (!mAll) {
diff --git a/tools/incident_section_gen/main.cpp b/tools/incident_section_gen/main.cpp
index 23aafb2..7966d88 100644
--- a/tools/incident_section_gen/main.cpp
+++ b/tools/incident_section_gen/main.cpp
@@ -135,27 +135,24 @@
                 if (generatePrivacyFlags(field->message_type(), field_name, msgNames) &&
                     isDefaultDest(field)) break;
 
-                printf("static Privacy %s = { %d, %d, %d, NULL, %s_LIST };\n", field_name, field->number(),
-                        (int) field->type(), p.dest(), field_name);
+                printf("Privacy %s(%d, %s_LIST);\n", field_name, field->number(), field_name);
                 hasDefaultFlags[i] = false;
                 break;
             case FieldDescriptor::TYPE_STRING:
                 if (isDefaultDest(field) && p.patterns_size() == 0) break;
 
-                printf("static const char* %s_patterns[] = {\n", field_name);
+                printf("const char* %s_patterns[] = {\n", field_name);
                 for (int i=0; i<p.patterns_size(); i++) {
                     // the generated string need to escape backslash as well, need to dup it here
                     printf("    \"%s\",\n", replaceAll(p.patterns(i), '\\', "\\\\").c_str());
                 }
                 printf("    NULL };\n");
-                printf("static Privacy %s = { %d, %d, %d, %s_patterns };\n", field_name, field->number(),
-                        (int) field->type(), p.dest(), field_name);
+                printf("Privacy %s(%d, %d, %s_patterns);\n", field_name, field->number(), p.dest(), field_name);
                 hasDefaultFlags[i] = false;
                 break;
             default:
                 if (isDefaultDest(field)) break;
-                printf("static Privacy %s = { %d, %d, %d };\n", field_name, field->number(),
-                        (int) field->type(), p.dest());
+                printf("Privacy %s(%d, %d, %d);\n", field_name, field->number(), (int) field->type(), p.dest());
                 hasDefaultFlags[i] = false;
         }
         // add the field name to message map, true means it has default flags
@@ -213,20 +210,6 @@
     printf("    NULL };\n");
     emptyline();
 
-    // generates DESTINATION enum values
-    EnumDescriptor const* destination = Destination_descriptor();
-    for (int i=0; i<destination->value_count(); i++) {
-        EnumValueDescriptor const* val = destination->value(i);
-        printf("const uint8_t %s = %d;\n", val->name().c_str(), val->number());
-    }
-    emptyline();
-    printf("const uint8_t DEST_DEFAULT_VALUE = %d;\n", PrivacyFlags::default_instance().dest());
-    emptyline();
-    // populates string type and message type values
-    printf("const uint8_t TYPE_STRING = %d;\n", (int) FieldDescriptor::TYPE_STRING);
-    printf("const uint8_t TYPE_MESSAGE = %d;\n", (int) FieldDescriptor::TYPE_MESSAGE);
-    emptyline();
-
     // generates PRIVACY_POLICY
     map<string, bool> messageNames;
     if (generatePrivacyFlags(descriptor, "PRIVACY_POLICY", messageNames)) {