First checkin of incident reporting.

There are a few major pieces here:

incidentd
---------
This daemon (started by init) runs and accepts incoming requests to take
incident reports.  When prompted, it calls into various system services
and fills in an IncidentProto data structure, and then writes the report
into dropbox.

The next steps for incidentd:
    - Security review of SELinux policies. These will be a subset of
      the dumpstate permissions.  Until this is done, incidentd is
      not started at boot time.

incident
--------
This shell command calls into incidentd, and can initiate an incident
report and either capture the output or leave for dropbox.

incident_report
---------------
This host side tool can call adb shell with the correct parameters
and also format the incident report as text.  This formatting code
was left of the device on purpose.  Right now it's pretty small, but
as the number of fields increases, the metadata and code to do the
formatting will start to grow.

The incident_report command also contains a workaround to let it
work before incidentd is turned on by default.  Right now, it is
implemented to call adb shell dumpsys <service> --proto directly,
whereas in the future it will go through the full incidentd flow.

incident_section_gen
--------------------
A build-time tool that generates a stripped down set of information
about the fields that are available.

libincident
-----------
This library contains the code to connect to incidentd, and the
meta proto definitions that are used by the framework protos.
The basics are here now, but they are not fully fleshed out yet.
The privacy.proto file contains annotations that can go in the
proto file that we will later use to filter which fields are
uploaded, and which are used by local sources.  For example, a
device in a test lab is safe to upload much much more information
than a real user.  These will share the same mechanism, but the
user's output will be filtered according to these annotations.

frameworks/core/proto
---------------------
These .proto files contain the definitions of the system's
output.  There is one master android.os.IncidentProto file that
is the top level of an incident report, but some other services
(notification, fingerprint, batterystats, etc) will have others
that are used directly by the logging mechanism.

Other files which are shared by several of the services also go
here, such as ComponentName, Locale, Configuration, etc.  There
will be many more.

There is also a first iplementation of a dump method handling
--proto in the fingerprint service.

IncidentManager
---------------
The java API to trigger an incident report.

Test: Not written yet
Change-Id: I59568b115ac7fcf73af70c946c95752bf33ae67f
diff --git a/cmds/incidentd/Android.mk b/cmds/incidentd/Android.mk
new file mode 100644
index 0000000..bacf672
--- /dev/null
+++ b/cmds/incidentd/Android.mk
@@ -0,0 +1,56 @@
+# Copyright (C) 2016 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.
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := incidentd
+
+LOCAL_SRC_FILES := \
+        src/FdBuffer.cpp \
+        src/IncidentService.cpp \
+        src/Reporter.cpp \
+        src/Section.cpp \
+        src/main.cpp \
+        src/protobuf.cpp \
+        src/report_directory.cpp \
+        src/section_list.cpp
+
+LOCAL_CFLAGS += \
+        -Wall -Werror -Wno-missing-field-initializers -Wno-unused-variable -Wunused-parameter
+
+ifeq (debug,)
+    LOCAL_CFLAGS += \
+            -g -O0
+else
+    # optimize for size (protobuf glop can get big)
+    LOCAL_CFLAGS += \
+            -Os
+endif
+
+LOCAL_SHARED_LIBRARIES := \
+        libbase \
+        libbinder \
+        libcutils \
+        libincident \
+        liblog \
+        libselinux \
+        libservices \
+        libutils
+
+ifeq (BUILD_WITH_INCIDENTD_RC,true)
+LOCAL_INIT_RC := incidentd.rc
+endif
+
+include $(BUILD_EXECUTABLE)
diff --git a/cmds/incidentd/incidentd.rc b/cmds/incidentd/incidentd.rc
new file mode 100644
index 0000000..d11e3cf
--- /dev/null
+++ b/cmds/incidentd/incidentd.rc
@@ -0,0 +1,16 @@
+# Copyright (C) 2016 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.
+
+#service incidentd /system/bin/incidentd
+#    class main
diff --git a/cmds/incidentd/src/FdBuffer.cpp b/cmds/incidentd/src/FdBuffer.cpp
new file mode 100644
index 0000000..527d7ee
--- /dev/null
+++ b/cmds/incidentd/src/FdBuffer.cpp
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "incidentd"
+
+#include "FdBuffer.h"
+
+#include <cutils/log.h>
+#include <utils/SystemClock.h>
+
+#include <fcntl.h>
+#include <poll.h>
+#include <unistd.h>
+
+const ssize_t BUFFER_SIZE = 16 * 1024;
+const ssize_t MAX_BUFFER_COUNT = 256; // 4 MB max
+
+
+FdBuffer::FdBuffer()
+    :mBuffers(),
+     mStartTime(-1),
+     mFinishTime(-1),
+     mCurrentWritten(-1),
+     mTimedOut(false),
+     mTruncated(false)
+{
+}
+
+FdBuffer::~FdBuffer()
+{
+    const int N = mBuffers.size();
+    for (int i=0; i<N; i++) {
+        uint8_t* buf = mBuffers[i];
+        free(buf);
+    }
+}
+
+status_t
+FdBuffer::read(int fd, int64_t timeout)
+{
+    struct pollfd pfds = {
+        .fd = fd,
+        .events = POLLIN
+    };
+    mStartTime = uptimeMillis();
+
+    fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK);
+
+    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 + timeout) - uptimeMillis();
+        if (remainingTime <= 0) {
+            mTimedOut = true;
+            break;
+        }
+
+        int count = poll(&pfds, 1, remainingTime);
+        if (count == 0) {
+            mTimedOut = true;
+            break;
+        } else if (count < 0) {
+            return -errno;
+        } else {
+            if ((pfds.revents & POLLERR) != 0) {
+                return errno != 0 ? -errno : UNKNOWN_ERROR;
+            } else {
+                ssize_t amt = ::read(fd, buf + mCurrentWritten, BUFFER_SIZE - mCurrentWritten);
+                if (amt < 0) {
+                    if (errno == EAGAIN || errno == EWOULDBLOCK) {
+                        continue;
+                    } else {
+                        return -errno;
+                    }
+                } else if (amt == 0) {
+                    break;
+                }
+                mCurrentWritten += amt;
+            }
+        }
+    }
+
+    mFinishTime = uptimeMillis();
+    return NO_ERROR;
+}
+
+size_t
+FdBuffer::size()
+{
+    return ((mBuffers.size() - 1) * BUFFER_SIZE) + mCurrentWritten;
+}
+
+status_t
+FdBuffer::write(ReportRequestSet* reporter)
+{
+    const int N = mBuffers.size() - 1;
+    for (int i=0; i<N; i++) {
+        reporter->write(mBuffers[i], BUFFER_SIZE);
+    }
+    reporter->write(mBuffers[N], mCurrentWritten);
+    return NO_ERROR;
+}
+
+
diff --git a/cmds/incidentd/src/FdBuffer.h b/cmds/incidentd/src/FdBuffer.h
new file mode 100644
index 0000000..e12374f
--- /dev/null
+++ b/cmds/incidentd/src/FdBuffer.h
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2016 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 FD_BUFFER_H
+#define FD_BUFFER_H
+
+#include "Reporter.h"
+
+#include <utils/Errors.h>
+
+#include <set>
+#include <vector>
+
+using namespace android;
+using namespace std;
+
+/**
+ * Reads a file into a buffer, and then writes that data to an FdSet.
+ */
+class FdBuffer
+{
+public:
+    FdBuffer();
+    ~FdBuffer();
+
+    /**
+     * Read the data until the timeout is hit or we hit eof.
+     * Returns NO_ERROR if there were no errors or if we timed out.
+     * Will mark the file O_NONBLOCK.
+     */
+    status_t read(int fd, int64_t timeoutMs);
+
+    /**
+     * Whether we timed out.
+     */
+    bool timedOut() { return mTimedOut; }
+
+    /**
+     * If more than 4 MB is read, we truncate the data and return success.
+     * Downstream tools must handle truncated incident reports as best as possible
+     * anyway because they could be cut off for a lot of reasons and it's best
+     * to get as much useful information out of the system as possible. If this
+     * 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; }
+
+    /**
+     * How much data was read.
+     */
+    size_t size();
+
+    /**
+     * Write the data that we recorded to the fd given.
+     */
+    status_t write(ReportRequestSet* requests);
+
+    /**
+     * How long the read took in milliseconds.
+     */
+    int64_t durationMs() { return mFinishTime - mStartTime; }
+
+private:
+    vector<uint8_t*> mBuffers;
+    int64_t mStartTime;
+    int64_t mFinishTime;
+    ssize_t mCurrentWritten;
+    bool mTimedOut;
+    bool mTruncated;
+};
+
+
+#endif // FD_BUFFER_H
diff --git a/cmds/incidentd/src/IncidentService.cpp b/cmds/incidentd/src/IncidentService.cpp
new file mode 100644
index 0000000..7c6789e
--- /dev/null
+++ b/cmds/incidentd/src/IncidentService.cpp
@@ -0,0 +1,244 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "incidentd"
+
+#include "IncidentService.h"
+
+#include "Reporter.h"
+
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+#include <cutils/log.h>
+#include <private/android_filesystem_config.h>
+#include <utils/Looper.h>
+
+#include <unistd.h>
+
+using namespace android;
+
+enum {
+    WHAT_RUN_REPORT = 1,
+    WHAT_SEND_BACKLOG_TO_DROPBOX = 2
+};
+
+//#define DEFAULT_BACKLOG_DELAY_NS (1000000000LL * 60 * 5)
+#define DEFAULT_BACKLOG_DELAY_NS (1000000000LL)
+
+// ================================================================================
+String16 const DUMP_PERMISSION("android.permission.DUMP");
+String16 const USAGE_STATS_PERMISSION("android.permission.PACKAGE_USAGE_STATS");
+
+static Status
+checkIncidentPermissions()
+{
+    if (!checkCallingPermission(DUMP_PERMISSION)) {
+        ALOGW("Calling pid %d and uid %d does not have permission: android.permission.DUMP",
+                IPCThreadState::self()->getCallingPid(), IPCThreadState::self()->getCallingUid());
+        return Status::fromExceptionCode(Status::EX_SECURITY,
+                "Calling process does not have permission: android.permission.DUMP");
+    }
+    if (!checkCallingPermission(USAGE_STATS_PERMISSION)) {
+        ALOGW("Calling pid %d and uid %d does not have permission: android.permission.USAGE_STATS",
+                IPCThreadState::self()->getCallingPid(), IPCThreadState::self()->getCallingUid());
+        return Status::fromExceptionCode(Status::EX_SECURITY,
+                "Calling process does not have permission: android.permission.USAGE_STATS");
+    }
+    return Status::ok();
+}
+
+
+// ================================================================================
+ReportRequestQueue::ReportRequestQueue()
+{
+}
+
+ReportRequestQueue::~ReportRequestQueue()
+{
+}
+
+void
+ReportRequestQueue::addRequest(const sp<ReportRequest>& request) 
+{
+    unique_lock<mutex> lock(mLock);
+    mQueue.push_back(request);
+}
+
+sp<ReportRequest>
+ReportRequestQueue::getNextRequest()
+{
+    unique_lock<mutex> lock(mLock);
+    if (mQueue.empty()) {
+        return NULL;
+    } else {
+        sp<ReportRequest> front(mQueue.front());
+        mQueue.pop_front();
+        return front;
+    }
+}
+
+
+// ================================================================================
+ReportHandler::ReportHandler(const sp<Looper>& handlerLooper, const sp<ReportRequestQueue>& queue)
+    :mBacklogDelay(DEFAULT_BACKLOG_DELAY_NS),
+     mHandlerLooper(handlerLooper),
+     mQueue(queue)
+{
+}
+
+ReportHandler::~ReportHandler()
+{
+}
+
+void
+ReportHandler::handleMessage(const Message& message)
+{
+    switch (message.what) {
+        case WHAT_RUN_REPORT:
+            run_report();
+            break;
+        case WHAT_SEND_BACKLOG_TO_DROPBOX:
+            send_backlog_to_dropbox();
+            break;
+    }
+}
+
+void
+ReportHandler::scheduleRunReport(const sp<ReportRequest>& request)
+{
+    mQueue->addRequest(request);
+    mHandlerLooper->removeMessages(this, WHAT_RUN_REPORT);
+    mHandlerLooper->sendMessage(this, Message(WHAT_RUN_REPORT));
+}
+
+void
+ReportHandler::scheduleSendBacklogToDropbox()
+{
+    unique_lock<mutex> lock(mLock);
+    mBacklogDelay = DEFAULT_BACKLOG_DELAY_NS;
+    schedule_send_backlog_to_dropbox_locked();
+}
+
+void
+ReportHandler::schedule_send_backlog_to_dropbox_locked()
+{
+    mHandlerLooper->removeMessages(this, WHAT_SEND_BACKLOG_TO_DROPBOX);
+    mHandlerLooper->sendMessageDelayed(mBacklogDelay, this,
+            Message(WHAT_SEND_BACKLOG_TO_DROPBOX));
+}
+
+void
+ReportHandler::run_report()
+{
+    sp<Reporter> reporter = new Reporter();
+
+    // Merge all of the requests into one that has all of the
+    // requested fields.
+    while (true) {
+        sp<ReportRequest> request = mQueue->getNextRequest();
+        if (request == NULL) {
+            break;
+        }
+        reporter->batch.add(request);
+        reporter->args.merge(request->args);
+    }
+
+    // Take the report, which might take a while. More requests might queue
+    // up while we're doing this, and we'll handle them in their next batch.
+    // TODO: We should further rate-limit the reports to no more than N per time-period.
+    Reporter::run_report_status_t reportStatus = reporter->runReport();
+    if (reportStatus == Reporter::REPORT_NEEDS_DROPBOX) {
+        unique_lock<mutex> lock(mLock);
+        schedule_send_backlog_to_dropbox_locked();
+    }
+}
+
+void
+ReportHandler::send_backlog_to_dropbox()
+{
+    if (Reporter::upload_backlog() == Reporter::REPORT_NEEDS_DROPBOX) {
+        // There was a failure. Exponential backoff.
+        unique_lock<mutex> lock(mLock);
+        mBacklogDelay *= 2;
+        ALOGI("Error sending to dropbox. Trying again in %lld minutes",
+                (mBacklogDelay / (1000000000LL * 60)));
+        schedule_send_backlog_to_dropbox_locked();
+    } else {
+        mBacklogDelay = DEFAULT_BACKLOG_DELAY_NS;
+    }
+}
+
+// ================================================================================
+IncidentService::IncidentService(const sp<Looper>& handlerLooper)
+    :mQueue(new ReportRequestQueue())
+{
+    mHandler = new ReportHandler(handlerLooper, mQueue);
+}
+
+IncidentService::~IncidentService()
+{
+}
+
+Status
+IncidentService::reportIncident(const IncidentReportArgs& args)
+{
+    ALOGI("reportIncident");
+
+    Status status = checkIncidentPermissions();
+    if (!status.isOk()) {
+        return status;
+    }
+
+    mHandler->scheduleRunReport(new ReportRequest(args, NULL, -1));
+
+    return Status::ok();
+}
+
+Status
+IncidentService::reportIncidentToStream(const IncidentReportArgs& args,
+            const sp<IIncidentReportStatusListener>& listener, const unique_fd& stream)
+{
+    ALOGI("reportIncidentToStream");
+
+    Status status = checkIncidentPermissions();
+    if (!status.isOk()) {
+        return status;
+    }
+
+    int fd = dup(stream.get());
+    if (fd < 0) {
+        return Status::fromStatusT(-errno);
+    }
+
+    mHandler->scheduleRunReport(new ReportRequest(args, listener, fd));
+
+    return Status::ok();
+}
+
+Status
+IncidentService::systemRunning()
+{
+    if (IPCThreadState::self()->getCallingUid() != AID_SYSTEM) {
+        return Status::fromExceptionCode(Status::EX_SECURITY,
+                "Only system uid can call systemRunning");
+    }
+    
+    // When system_server is up and running, schedule the dropbox task to run.
+    mHandler->scheduleSendBacklogToDropbox();
+
+    return Status::ok();
+}
+
diff --git a/cmds/incidentd/src/IncidentService.h b/cmds/incidentd/src/IncidentService.h
new file mode 100644
index 0000000..d6f33df
--- /dev/null
+++ b/cmds/incidentd/src/IncidentService.h
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2016 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 INCIDENT_SERVICE_H
+#define INCIDENT_SERVICE_H
+
+#include "Reporter.h"
+
+#include <android/os/BnIncidentManager.h>
+#include <utils/Looper.h>
+
+#include <deque>
+#include <mutex>
+
+using namespace android;
+using namespace android::base;
+using namespace android::binder;
+using namespace android::os;
+using namespace std;
+
+// ================================================================================
+class ReportRequestQueue : public virtual RefBase
+{
+public:
+    ReportRequestQueue();
+    virtual ~ReportRequestQueue();
+
+    void addRequest(const sp<ReportRequest>& request);
+    sp<ReportRequest> getNextRequest();
+
+private:
+    mutex mLock;
+    deque<sp<ReportRequest> > mQueue;
+};
+
+
+// ================================================================================
+class ReportHandler : public MessageHandler
+{
+public:
+    ReportHandler(const sp<Looper>& handlerLooper, const sp<ReportRequestQueue>& queue);
+    virtual ~ReportHandler();
+
+    virtual void handleMessage(const Message& message);
+
+    /**
+     * Adds a ReportRequest to the queue.
+     */
+    void scheduleRunReport(const sp<ReportRequest>& request);
+
+    /**
+     * Resets mBacklogDelay to the default and schedules sending
+     * the messages to dropbox.
+     */
+    void scheduleSendBacklogToDropbox();
+
+private:
+    mutex mLock;
+    nsecs_t mBacklogDelay;
+    sp<Looper> mHandlerLooper;
+    sp<ReportRequestQueue> mQueue;
+
+    /**
+     * Runs all of the reports that have been queued.
+     */
+    void run_report();
+
+    /**
+     * Schedules a dropbox task mBacklogDelay nanoseconds from now.
+     */
+    void schedule_send_backlog_to_dropbox_locked();
+
+    /**
+     * Sends the backlog to the dropbox service.
+     */
+    void send_backlog_to_dropbox();
+};
+
+
+// ================================================================================
+class IncidentService : public BnIncidentManager {
+public:
+    IncidentService(const sp<Looper>& handlerLooper);
+    virtual ~IncidentService();
+
+    virtual Status reportIncident(const IncidentReportArgs& args);
+
+    virtual Status reportIncidentToStream(const IncidentReportArgs& args,
+            const sp<IIncidentReportStatusListener>& listener, const unique_fd& stream);
+
+    virtual Status systemRunning();
+
+private:
+    sp<ReportRequestQueue> mQueue;
+    sp<ReportHandler> mHandler;
+};
+
+
+#endif // INCIDENT_SERVICE_H
diff --git a/cmds/incidentd/src/Reporter.cpp b/cmds/incidentd/src/Reporter.cpp
new file mode 100644
index 0000000..1ecb291
--- /dev/null
+++ b/cmds/incidentd/src/Reporter.cpp
@@ -0,0 +1,352 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "incidentd"
+
+#include "Reporter.h"
+#include "protobuf.h"
+
+#include "report_directory.h"
+#include "section_list.h"
+
+#include <private/android_filesystem_config.h>
+#include <android/os/DropBoxManager.h>
+#include <utils/SystemClock.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <errno.h>
+
+/**
+ * The directory where the incident reports are stored.
+ */
+static const String8 INCIDENT_DIRECTORY("/data/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),
+     listener(l),
+     fd(f),
+     err(NO_ERROR)
+{
+}
+
+ReportRequest::~ReportRequest()
+{
+}
+
+// ================================================================================
+ReportRequestSet::ReportRequestSet()
+    :mRequests(),
+     mWritableCount(0),
+     mMainFd(-1)
+{
+}
+
+ReportRequestSet::~ReportRequestSet()
+{
+}
+
+void
+ReportRequestSet::add(const sp<ReportRequest>& request)
+{
+    mRequests.push_back(request);
+    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;
+}
+
+
+// ================================================================================
+Reporter::Reporter()
+    :args(),
+     batch()
+{
+    char buf[100];
+
+    // TODO: Make the max size smaller for user builds.
+    mMaxSize = 100 * 1024 * 1024;
+    mMaxCount = 100;
+
+    // There can't be two at the same time because it's on one thread.
+    mStartTime = time(NULL);
+    strftime(buf, sizeof(buf), "/incident-%Y%m%d-%H%M%S", localtime(&mStartTime));
+    mFilename = INCIDENT_DIRECTORY + buf;
+}
+
+Reporter::~Reporter()
+{
+}
+
+Reporter::run_report_status_t
+Reporter::runReport()
+{
+
+    status_t err = NO_ERROR;
+    bool needMainFd = false;
+    int mainFd = -1;
+
+    // See if we need the main file
+    for (ReportRequestSet::iterator it=batch.begin(); it!=batch.end(); it++) {
+        if ((*it)->fd < 0 && mainFd < 0) {
+            needMainFd = true;
+            break;
+        }
+    }
+    if (needMainFd) {
+        // Create the directory
+        err = create_directory(INCIDENT_DIRECTORY);
+        if (err != NO_ERROR) {
+            goto done;
+        }
+
+        // If there are too many files in the directory (for whatever reason),
+        // delete the oldest ones until it's under the limit. Doing this first
+        // does mean that we can go over, so the max size is not a hard limit.
+        clean_directory(INCIDENT_DIRECTORY, mMaxSize, mMaxCount);
+
+        // Open the file.
+        err = create_file(&mainFd);
+        if (err != NO_ERROR) {
+            goto done;
+        }
+
+        // Add to the set
+        batch.setMainFd(mainFd);
+    }
+
+    // Tell everyone that we're starting.
+    for (ReportRequestSet::iterator it=batch.begin(); it!=batch.end(); it++) {
+        if ((*it)->listener != NULL) {
+            (*it)->listener->onReportStarted();
+        }
+    }
+
+    // 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.
+        }
+    }
+
+    // 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.
+    for (const Section** section=SECTION_LIST; *section; section++) {
+        const int id = (*section)->id;
+        ALOGD("Taking incident report section %d '%s'", id, (*section)->name.string());
+
+        if (this->args.containsSection(id)) {
+            // Notify listener of starting
+            for (ReportRequestSet::iterator it=batch.begin(); it!=batch.end(); it++) {
+                if ((*it)->listener != NULL && (*it)->args.containsSection(id)) {
+                    (*it)->listener->onReportSectionStatus(id,
+                            IIncidentReportStatusListener::STATUS_STARTING);
+                }
+            }
+
+            // Execute - go get the data and write it into the file descriptors.
+            err = (*section)->Execute(&batch);
+            if (err != NO_ERROR) {
+                ALOGW("Incident section %s (%d) failed. Stopping report.",
+                        (*section)->name.string(), id);
+                goto done;
+            }
+
+            // Notify listener of starting
+            for (ReportRequestSet::iterator it=batch.begin(); it!=batch.end(); it++) {
+                if ((*it)->listener != NULL && (*it)->args.containsSection(id)) {
+                    (*it)->listener->onReportSectionStatus(id,
+                            IIncidentReportStatusListener::STATUS_FINISHED);
+                }
+            }
+        }
+    }
+
+done:
+    // Close the file.
+    if (mainFd >= 0) {
+        close(mainFd);
+    }
+
+    // Tell everyone that we're done.
+    for (ReportRequestSet::iterator it=batch.begin(); it!=batch.end(); it++) {
+        if ((*it)->listener != NULL) {
+            if (err == NO_ERROR) {
+                (*it)->listener->onReportFinished();
+            } else {
+                (*it)->listener->onReportFailed();
+            }
+        }
+    }
+
+    // Put the report into dropbox.
+    if (needMainFd && err == NO_ERROR) {
+        sp<DropBoxManager> dropbox = new DropBoxManager();
+        Status status = dropbox->addFile(String16("incident"), mFilename, 0);
+        ALOGD("Incident report done. dropbox status=%s\n", status.toString8().string());
+        if (!status.isOk()) {
+            return REPORT_NEEDS_DROPBOX;
+        }
+
+        // If the status was ok, delete the file. If not, leave it around until the next
+        // boot or the next checkin. If the directory gets too big older files will
+        // be rotated out.
+        unlink(mFilename.c_str());
+    }
+
+    return REPORT_FINISHED;
+}
+
+/**
+ * Create our output file and set the access permissions to -rw-rw----
+ */
+status_t
+Reporter::create_file(int* fd)
+{
+    const char* filename = mFilename.c_str();
+
+    *fd = open(filename, O_CREAT | O_TRUNC | O_RDWR, 0660);
+    if (*fd < 0) {
+        ALOGE("Couldn't open incident file: %s (%s)", filename, strerror(errno));
+        return -errno;
+    }
+
+    // Override umask. Not super critical. If it fails go on with life.
+    chmod(filename, 0660);
+
+    if (chown(filename, AID_SYSTEM, AID_SYSTEM)) {
+        ALOGE("Unable to change ownership of incident file %s: %s\n", filename, strerror(errno));
+        status_t err = -errno;
+        unlink(mFilename.c_str());
+        return err;
+    }
+
+    return NO_ERROR;
+}
+
+// ================================================================================
+Reporter::run_report_status_t
+Reporter::upload_backlog()
+{
+    DIR* dir;
+    struct dirent* entry;
+    struct stat st;
+
+    if ((dir = opendir(INCIDENT_DIRECTORY.string())) == NULL) {
+        ALOGE("Couldn't open incident directory: %s", INCIDENT_DIRECTORY.string());
+        return REPORT_NEEDS_DROPBOX;
+    }
+
+    String8 dirbase(INCIDENT_DIRECTORY + "/");
+    sp<DropBoxManager> dropbox = new DropBoxManager();
+
+    // Enumerate, count and add up size
+    while ((entry = readdir(dir)) != NULL) {
+        if (entry->d_name[0] == '.') {
+            continue;
+        }
+        String8 filename = dirbase + entry->d_name;
+        if (stat(filename.string(), &st) != 0) {
+            ALOGE("Unable to stat file %s", filename.string());
+            continue;
+        }
+        if (!S_ISREG(st.st_mode)) {
+            continue;
+        }
+
+        Status status = dropbox->addFile(String16("incident"), filename.string(), 0);
+        ALOGD("Incident report done. dropbox status=%s\n", status.toString8().string());
+        if (!status.isOk()) {
+            return REPORT_NEEDS_DROPBOX;
+        }
+
+        // If the status was ok, delete the file. If not, leave it around until the next
+        // boot or the next checkin. If the directory gets too big older files will
+        // be rotated out.
+        unlink(filename.string());
+    }
+
+    closedir(dir);
+
+    return REPORT_FINISHED;
+}
+
diff --git a/cmds/incidentd/src/Reporter.h b/cmds/incidentd/src/Reporter.h
new file mode 100644
index 0000000..5b86561
--- /dev/null
+++ b/cmds/incidentd/src/Reporter.h
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2016 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 REPORTER_H
+#define REPORTER_H
+
+#include <android/os/IIncidentReportStatusListener.h>
+#include <android/os/IncidentReportArgs.h>
+
+#include <string>
+#include <vector>
+
+#include <time.h>
+
+using namespace android;
+using namespace android::os;
+using namespace std;
+
+// ================================================================================
+struct ReportRequest : public virtual RefBase
+{
+    IncidentReportArgs args;
+    sp<IIncidentReportStatusListener> listener;
+    int fd;
+    status_t err;
+
+    ReportRequest(const IncidentReportArgs& args,
+            const sp<IIncidentReportStatusListener> &listener, int fd);
+    virtual ~ReportRequest();
+};
+
+// ================================================================================
+class ReportRequestSet
+{
+public:
+    ReportRequestSet();
+    ~ReportRequestSet();
+
+    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(); }
+
+private:
+    vector<sp<ReportRequest>> mRequests;
+    int mWritableCount;
+    int mMainFd;
+};
+
+// ================================================================================
+class Reporter : public virtual RefBase
+{
+public:
+    enum run_report_status_t {
+        REPORT_FINISHED = 0,
+        REPORT_NEEDS_DROPBOX = 1
+    };
+
+    IncidentReportArgs args;
+    ReportRequestSet batch;
+
+    Reporter();
+    virtual ~Reporter();
+
+    // Run the report as described in the batch and args parameters.
+    run_report_status_t runReport();
+
+    static run_report_status_t upload_backlog();
+
+private:
+    string mFilename;
+    off_t mMaxSize;
+    size_t mMaxCount;
+    time_t mStartTime;
+
+    status_t create_file(int* fd);
+};
+
+
+#endif // REPORTER_H
diff --git a/cmds/incidentd/src/Section.cpp b/cmds/incidentd/src/Section.cpp
new file mode 100644
index 0000000..fac299e
--- /dev/null
+++ b/cmds/incidentd/src/Section.cpp
@@ -0,0 +1,292 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "incidentd"
+
+#include "Section.h"
+#include "protobuf.h"
+
+#include <binder/IServiceManager.h>
+#include <mutex>
+
+using namespace std;
+
+const int64_t REMOTE_CALL_TIMEOUT_MS = 10 * 1000; // 10 seconds
+
+// ================================================================================
+Section::Section(int i)
+    :id(i)
+{
+}
+
+Section::~Section()
+{
+}
+
+status_t
+Section::WriteHeader(ReportRequestSet* requests, size_t size) const
+{
+    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);
+}
+
+// ================================================================================
+struct WorkerThreadData : public virtual RefBase
+{
+    const WorkerThreadSection* section;
+    int fds[2];
+
+    // Lock protects these fields
+    mutex lock;
+    bool workerDone;
+    status_t workerError;
+
+    WorkerThreadData(const WorkerThreadSection* section);
+    virtual ~WorkerThreadData();
+
+    int readFd() { return fds[0]; }
+    int writeFd() { return fds[1]; }
+};
+
+WorkerThreadData::WorkerThreadData(const WorkerThreadSection* sec)
+    :section(sec),
+     workerDone(false),
+     workerError(NO_ERROR)
+{
+    fds[0] = -1;
+    fds[1] = -1;
+}
+
+WorkerThreadData::~WorkerThreadData()
+{
+}
+
+// ================================================================================
+WorkerThreadSection::WorkerThreadSection(int id)
+    :Section(id)
+{
+}
+
+WorkerThreadSection::~WorkerThreadSection()
+{
+}
+
+static void*
+worker_thread_func(void* cookie)
+{
+    WorkerThreadData* data = (WorkerThreadData*)cookie;
+    status_t err = data->section->BlockingCall(data->writeFd());
+
+    {
+        unique_lock<mutex> lock(data->lock);
+        data->workerDone = true;
+        data->workerError = err;
+    }
+
+    close(data->writeFd());
+    data->decStrong(data->section);
+    // data might be gone now. don't use it after this point in this thread.
+    return NULL;
+}
+
+status_t
+WorkerThreadSection::Execute(ReportRequestSet* requests) const
+{
+    status_t err = NO_ERROR;
+    pthread_t thread;
+    pthread_attr_t attr;
+    bool timedOut = false;
+    FdBuffer buffer;
+
+    // Data shared between this thread and the worker thread.
+    sp<WorkerThreadData> data = new WorkerThreadData(this);
+
+    // Create the pipe
+    err = pipe(data->fds);
+    if (err != 0) {
+        return -errno;
+    }
+
+    // The worker thread needs a reference and we can't let the count go to zero
+    // if that thread is slow to start.
+    data->incStrong(this);
+
+    // Create the thread
+    err = pthread_attr_init(&attr);
+    if (err != 0) {
+        return -err;
+    }
+    // TODO: Do we need to tweak thread priority?
+    err = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+    if (err != 0) {
+        pthread_attr_destroy(&attr);
+        return -err;
+    }
+    err = pthread_create(&thread, &attr, worker_thread_func, (void*)data.get());
+    if (err != 0) {
+        pthread_attr_destroy(&attr);
+        return -err;
+    }
+    pthread_attr_destroy(&attr);
+
+    // Loop reading until either the timeout or the worker side is done (i.e. eof).
+    err = buffer.read(data->readFd(), REMOTE_CALL_TIMEOUT_MS);
+    if (err != NO_ERROR) {
+        // TODO: Log this error into the incident report.
+        ALOGW("WorkerThreadSection '%s' reader failed with error '%s'", this->name.string(),
+                strerror(-err));
+    }
+
+    // Done with the read fd. The worker thread closes the write one so
+    // we never race and get here first.
+    close(data->readFd());
+
+    // If the worker side is finished, then return its error (which may overwrite
+    // our possible error -- but it's more interesting anyway).  If not, then we timed out.
+    {
+        unique_lock<mutex> lock(data->lock);
+        if (!data->workerDone) {
+            // We timed out
+            timedOut = true;
+        } else {
+            if (data->workerError != NO_ERROR) {
+                err = data->workerError;
+                // TODO: Log this error into the incident report.
+                ALOGW("WorkerThreadSection '%s' worker failed with error '%s'", this->name.string(),
+                        strerror(-err));
+            }
+        }
+    }
+
+    if (timedOut || buffer.timedOut()) {
+        ALOGW("WorkerThreadSection '%s' timed out", this->name.string());
+        return NO_ERROR;
+    }
+
+    if (buffer.truncated()) {
+        // TODO: Log this into the incident report.
+    }
+
+    // TODO: There was an error with the command or buffering. Report that.  For now
+    // just exit with a log messasge.
+    if (err != NO_ERROR) {
+        ALOGW("WorkerThreadSection '%s' failed with error '%s'", this->name.string(),
+                strerror(-err));
+        return NO_ERROR;
+    }
+
+    // Write the data that was collected
+    ALOGD("section '%s' wrote %zd bytes in %d ms", name.string(), buffer.size(),
+            (int)buffer.durationMs());
+    WriteHeader(requests, buffer.size());
+    err = buffer.write(requests);
+    if (err != NO_ERROR) {
+        ALOGW("WorkerThreadSection '%s' failed writing: '%s'", this->name.string(), strerror(-err));
+        return err;
+    }
+
+    return NO_ERROR;
+}
+
+// ================================================================================
+CommandSection::CommandSection(int id, const char* first, ...)
+    :Section(id)
+{
+    va_list args;
+    int count = 0;
+
+    va_start(args, first);
+    while (va_arg(args, const char*) != NULL) {
+        count++;
+    }
+    va_end(args);
+
+    mCommand = (const char**)malloc(sizeof(const char*) * count);
+
+    mCommand[0] = first;
+    name = first;
+    name += " ";
+    va_start(args, first);
+    for (int i=0; i<count; i++) {
+        const char* arg = va_arg(args, const char*); 
+        mCommand[i+1] = arg;
+        if (arg != NULL) {
+            name += va_arg(args, const char*);
+            name += " ";
+        }
+    }
+    va_end(args);
+}
+
+CommandSection::~CommandSection()
+{
+}
+
+status_t
+CommandSection::Execute(ReportRequestSet* /*requests*/) const
+{
+    return NO_ERROR;
+}
+
+// ================================================================================
+DumpsysSection::DumpsysSection(int id, const char* service, ...)
+    :WorkerThreadSection(id),
+     mService(service)
+{
+    name = "dumpsys ";
+    name += service;
+
+    va_list args;
+    va_start(args, service);
+    while (true) {
+        const char* arg = va_arg(args, const char*); 
+        if (arg == NULL) {
+            break;
+        }
+        mArgs.add(String16(arg));
+        name += " ";
+        name += arg;
+    }
+    va_end(args);
+}
+
+DumpsysSection::~DumpsysSection()
+{
+}
+
+status_t
+DumpsysSection::BlockingCall(int pipeWriteFd) const
+{
+    // 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.
+        // TODO: have a meta record inside the report that would log this
+        // failure inside the report, because the fact that we can't find
+        // the service is good data in and of itself. This is running in
+        // another thread so lock that carefully...
+        ALOGW("DumpsysSection: Can't lookup service: %s", String8(mService).string());
+        return NO_ERROR;
+    }
+
+    service->dump(pipeWriteFd, mArgs);
+
+    return NO_ERROR;
+}
diff --git a/cmds/incidentd/src/Section.h b/cmds/incidentd/src/Section.h
new file mode 100644
index 0000000..35740e9
--- /dev/null
+++ b/cmds/incidentd/src/Section.h
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2016 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 SECTIONS_H
+#define SECTIONS_H
+
+#include "FdBuffer.h"
+
+#include <utils/String8.h>
+#include <utils/String16.h>
+#include <utils/Vector.h>
+
+using namespace android;
+
+/**
+ * Base class for sections
+ */
+class Section
+{
+public:
+    int id;
+    String8 name;
+
+    Section(int id);
+    virtual ~Section();
+
+    virtual status_t Execute(ReportRequestSet* requests) const = 0;
+
+    status_t WriteHeader(ReportRequestSet* requests, size_t size) const;
+};
+
+/**
+ * Section that reads in a file.
+ */
+class FileSection : public Section
+{
+public:
+    FileSection(int id, const char* filename);
+    virtual ~FileSection();
+
+    virtual status_t Execute(ReportRequestSet* requests) const;
+
+private:
+    const char* mFilename;
+};
+
+/**
+ * Base class for sections that call a command that might need a timeout.
+ */
+class WorkerThreadSection : public Section
+{
+public:
+    WorkerThreadSection(int id);
+    virtual ~WorkerThreadSection();
+
+    virtual status_t Execute(ReportRequestSet* requests) const;
+
+    virtual status_t BlockingCall(int pipeWriteFd) const = 0;
+};
+
+/**
+ * Section that forks and execs a command, and puts stdout as the section.
+ */
+class CommandSection : public Section
+{
+public:
+    CommandSection(int id, const char* first, ...);
+    virtual ~CommandSection();
+
+    virtual status_t Execute(ReportRequestSet* requests) const;
+
+private:
+    const char** mCommand;
+};
+
+/**
+ * Section that calls dumpsys on a system service.
+ */
+class DumpsysSection : public WorkerThreadSection
+{
+public:
+    DumpsysSection(int id, const char* service, ...);
+    virtual ~DumpsysSection();
+
+    virtual status_t BlockingCall(int pipeWriteFd) const;
+
+private:
+    String16 mService;
+    Vector<String16> mArgs;
+};
+
+#endif // SECTIONS_H
+
diff --git a/cmds/incidentd/src/main.cpp b/cmds/incidentd/src/main.cpp
new file mode 100644
index 0000000..3a7511d
--- /dev/null
+++ b/cmds/incidentd/src/main.cpp
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "incidentd"
+
+#include "IncidentService.h"
+
+#include <binder/IInterface.h>
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+#include <binder/ProcessState.h>
+#include <binder/Status.h>
+#include <cutils/log.h>
+#include <utils/Looper.h>
+#include <utils/StrongPointer.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+using namespace android;
+
+// ================================================================================
+int
+main(int /*argc*/, char** /*argv*/)
+{
+    // Set up the looper
+    sp<Looper> looper(Looper::prepare(0 /* opts */));
+
+    // Set up the binder
+    sp<ProcessState> ps(ProcessState::self());
+    ps->setThreadPoolMaxThreadCount(1); // everything is oneway, let it queue and save ram
+    ps->startThreadPool();
+    ps->giveThreadPoolName();
+    IPCThreadState::self()->disableBackgroundScheduling(true);
+
+    // Create the service
+    android::sp<IncidentService> service = new IncidentService(looper);
+    if (defaultServiceManager()->addService(String16("incident"), service) != 0) {
+        ALOGE("Failed to add service");
+        return -1;
+    }
+
+    // Loop forever -- the reports run on this thread in a handler, and the
+    // binder calls remain responsive in their pool of one thread.
+    while (true) {
+        looper->pollAll(-1 /* timeoutMillis */);
+    }
+    ALOGW("incidentd escaped from its loop.");
+
+    return 1;
+}
diff --git a/cmds/incidentd/src/protobuf.cpp b/cmds/incidentd/src/protobuf.cpp
new file mode 100644
index 0000000..cb864fd
--- /dev/null
+++ b/cmds/incidentd/src/protobuf.cpp
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2016 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 "protobuf.h"
+
+uint8_t*
+write_raw_varint(uint8_t* buf, uint32_t val)
+{
+    uint8_t* p = buf;
+    while (true) {
+        if ((val & ~0x7F) == 0) {
+            *p++ = (uint8_t)val;
+            return p;
+        } else {
+            *p++ = (uint8_t)((val & 0x7F) | 0x80);
+            val >>= 7;
+        }
+    }
+}
+
+uint8_t*
+write_length_delimited_tag_header(uint8_t* buf, uint32_t fieldId, size_t size)
+{
+    buf = write_raw_varint(buf, (fieldId << 3) | 2);
+    buf = write_raw_varint(buf, size);
+    return buf;
+}
+
diff --git a/cmds/incidentd/src/protobuf.h b/cmds/incidentd/src/protobuf.h
new file mode 100644
index 0000000..a243998
--- /dev/null
+++ b/cmds/incidentd/src/protobuf.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2016 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 PROTOBUF_H
+#define PROTOBUF_H
+
+#include <stdint.h>
+
+/**
+ * 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
+ */
+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.
+ */
+uint8_t* write_length_delimited_tag_header(uint8_t* buf, uint32_t fieldId, size_t size);
+
+enum {
+    // IncidentProto.header
+    FIELD_ID_INCIDENT_HEADER = 1
+};
+
+#endif // PROTOBUF_H
+
diff --git a/cmds/incidentd/src/report_directory.cpp b/cmds/incidentd/src/report_directory.cpp
new file mode 100644
index 0000000..f60b8ac
--- /dev/null
+++ b/cmds/incidentd/src/report_directory.cpp
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "incidentd"
+
+#include "report_directory.h"
+
+#include <cutils/log.h>
+#include <private/android_filesystem_config.h>
+#include <utils/String8.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <libgen.h>
+#include <unistd.h>
+
+#include <vector>
+
+using namespace android;
+using namespace std;
+
+status_t
+create_directory(const char* directory)
+{
+    struct stat st;
+    status_t err = NO_ERROR;
+    char* dir = strdup(directory);
+
+    // Skip first slash
+    char* d = dir + 1;
+
+    // Create directories, assigning them to the system user
+    bool last = false;
+    while (!last) {
+        d = strchr(d, '/');
+        if (d != NULL) {
+            *d = '\0';
+        } else {
+            last = true;
+        }
+        if (stat(dir, &st) == 0) {
+            if (!S_ISDIR(st.st_mode)) {
+                err = ALREADY_EXISTS;
+                goto done;
+            }
+        } else {
+            if (mkdir(dir, 0770)) {
+                ALOGE("No incident reports today. "
+                        "Unable to create incident report dir %s: %s", dir,
+                        strerror(errno));
+                err = -errno;
+                goto done;
+            }
+            if (chmod(dir, 0770)) {
+                ALOGE("No incident reports today. "
+                        "Unable to set permissions for incident report dir %s: %s", dir,
+                        strerror(errno));
+                err = -errno;
+                goto done;
+            }
+            if (chown(dir, AID_SYSTEM, AID_SYSTEM)) {
+                ALOGE("No incident reports today. Unable to change ownership of dir %s: %s\n",
+                        dir, strerror(errno));
+                err = -errno;
+                goto done;
+            }
+        }
+        if (!last) {
+            *d++ = '/';
+        }
+    }
+
+    // Ensure that the final directory is owned by the system with 0770. If it isn't
+    // we won't write into it.
+    if (stat(directory, &st) != 0) {
+        ALOGE("No incident reports today. Can't stat: %s", directory);
+        err = -errno;
+        goto done;
+    }
+    if ((st.st_mode & 0777) != 0770) {
+        ALOGE("No incident reports today. Mode is %0o on report directory %s",
+                st.st_mode, directory);
+        err = BAD_VALUE;
+        goto done;
+    }
+    if (st.st_uid != AID_SYSTEM || st.st_gid != AID_SYSTEM) {
+        ALOGE("No incident reports today. Owner is %d and group is %d on report directory %s",
+                st.st_uid, st.st_gid, directory);
+        err = BAD_VALUE;
+        goto done;
+    }
+
+done:
+    free(dir);
+    return err;
+}
+
+static bool
+stat_mtime_cmp(const pair<String8,struct stat>& a, const pair<String8,struct stat>& b)
+{
+    return a.second.st_mtime < b.second.st_mtime;
+}
+
+void
+clean_directory(const char* directory, off_t maxSize, size_t maxCount)
+{
+    DIR* dir;
+    struct dirent* entry;
+    struct stat st;
+
+    vector<pair<String8,struct stat>> files;
+
+    if ((dir = opendir(directory)) == NULL) {
+        ALOGE("Couldn't open incident directory: %s", directory);
+        return;
+    }
+
+    String8 dirbase(String8(directory) + "/");
+
+    off_t totalSize = 0;
+    size_t totalCount = 0;
+
+    // Enumerate, count and add up size
+    while ((entry = readdir(dir)) != NULL) {
+        if (entry->d_name[0] == '.') {
+            continue;
+        }
+        String8 filename = dirbase + entry->d_name;
+        if (stat(filename.string(), &st) != 0) {
+            ALOGE("Unable to stat file %s", filename.string());
+            continue;
+        }
+        if (!S_ISREG(st.st_mode)) {
+            continue;
+        }
+        files.push_back(pair<String8,struct stat>(filename, st));
+
+        totalSize += st.st_size;
+        totalCount++;
+    }
+
+    closedir(dir);
+
+    // Count or size is less than max, then we're done.
+    if (totalSize < maxSize && totalCount < maxCount) {
+        return;
+    }
+
+    // Oldest files first.
+    sort(files.begin(), files.end(), stat_mtime_cmp);
+
+    // Remove files until we're under our limits.
+    for (vector<pair<String8,struct stat>>::iterator it = files.begin();
+            it != files.end() && totalSize >= maxSize && totalCount >= maxCount; it++) {
+        remove(it->first.string());
+        totalSize -= it->second.st_size;
+        totalCount--;
+    }
+}
diff --git a/cmds/incidentd/src/report_directory.h b/cmds/incidentd/src/report_directory.h
new file mode 100644
index 0000000..bed4f86
--- /dev/null
+++ b/cmds/incidentd/src/report_directory.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2016 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 DIRECTORY_CLEANER_H
+#define DIRECTORY_CLEANER_H
+
+#include <utils/Errors.h>
+
+#include <sys/types.h>
+
+using namespace android;
+
+status_t create_directory(const char* directory);
+void clean_directory(const char* directory, off_t maxSize, size_t maxCount);
+
+#endif // DIRECTORY_CLEANER_H
diff --git a/cmds/incidentd/src/section_list.cpp b/cmds/incidentd/src/section_list.cpp
new file mode 100644
index 0000000..b6112ed
--- /dev/null
+++ b/cmds/incidentd/src/section_list.cpp
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2016 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 "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),
+    NULL
+};
+
diff --git a/cmds/incidentd/src/section_list.h b/cmds/incidentd/src/section_list.h
new file mode 100644
index 0000000..c977519
--- /dev/null
+++ b/cmds/incidentd/src/section_list.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2016 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 SECTION_LIST_H
+#define SECTION_LIST_H
+
+#include "Section.h"
+
+/**
+ * This is the mapping of section IDs to the commands that are run to get those commands.
+ */
+extern const Section* SECTION_LIST[];
+
+#endif // SECTION_LIST_H
+