incidentd can now handle multiple callers asking it for incident reports

Test: bit incident_test:* GtsIncidentManagerTestCases:*
Bug: 123543706
Change-Id: I9f671dd5d8b2ad139f952a23e575c2be16120459
diff --git a/cmds/incidentd/src/Section.cpp b/cmds/incidentd/src/Section.cpp
index 32ec1ba..1e8261e 100644
--- a/cmds/incidentd/src/Section.cpp
+++ b/cmds/incidentd/src/Section.cpp
@@ -27,6 +27,7 @@
 #include <android-base/file.h>
 #include <android-base/stringprintf.h>
 #include <android/util/protobuf.h>
+#include <android/util/ProtoOutputStream.h>
 #include <binder/IServiceManager.h>
 #include <debuggerd/client.h>
 #include <dumputils/dump_utils.h>
@@ -37,7 +38,6 @@
 
 #include "FdBuffer.h"
 #include "Privacy.h"
-#include "PrivacyBuffer.h"
 #include "frameworks/base/core/proto/android/os/backtrace.proto.h"
 #include "frameworks/base/core/proto/android/os/data.proto.h"
 #include "frameworks/base/core/proto/android/util/log.proto.h"
@@ -51,7 +51,6 @@
 using namespace android::util;
 
 // special section ids
-const int FIELD_ID_INCIDENT_HEADER = 1;
 const int FIELD_ID_INCIDENT_METADATA = 2;
 
 // incident section parameters
@@ -64,114 +63,6 @@
 }
 
 // ================================================================================
-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 WriteFully(fd, buf, p - buf) ? NO_ERROR : -errno;
-}
-
-static void write_section_stats(IncidentMetadata::SectionStats* stats, const FdBuffer& buffer) {
-    stats->set_dump_size_bytes(buffer.data().size());
-    stats->set_dump_duration_ms(buffer.durationMs());
-    stats->set_timed_out(buffer.timedOut());
-    stats->set_is_truncated(buffer.truncated());
-    stats->set_success(!buffer.timedOut() && !buffer.truncated());
-}
-
-// Reads data from FdBuffer and writes it to the requests file descriptor.
-static status_t write_report_requests(const int id, const FdBuffer& buffer,
-                                      ReportRequestSet* requests) {
-    status_t err = -EBADF;
-    EncodedBuffer::iterator data = buffer.data();
-    PrivacyBuffer privacyBuffer(get_privacy_of_section(id), data);
-    IncidentMetadata::SectionStats* stats = requests->sectionStats(id);
-    int nPassed = 0;
-
-    // The streaming ones, group requests by spec in order to save unnecessary strip operations
-    map<PrivacySpec, vector<sp<ReportRequest>>> requestsBySpec;
-    for (auto it = requests->begin(); it != requests->end(); it++) {
-        sp<ReportRequest> request = *it;
-        if (!request->ok() || !request->args.containsSection(id)) {
-            continue;  // skip invalid request
-        }
-        PrivacySpec spec = PrivacySpec::new_spec(request->args.dest());
-        requestsBySpec[spec].push_back(request);
-    }
-
-    for (auto mit = requestsBySpec.begin(); mit != requestsBySpec.end(); mit++) {
-        PrivacySpec spec = mit->first;
-        err = privacyBuffer.strip(spec);
-        if (err != NO_ERROR) {
-            // Privacy Buffer is corrupted, probably due to an ill-formatted proto. This is a
-            // non-fatal error. The whole report is still valid. So just log the failure.
-            ALOGW("Failed to strip section %d with spec %d: %s",
-                id, spec.dest, strerror(-err));
-            stats->set_success(false);
-            stats->set_error_msg("Failed to strip section: privacy buffer corrupted, probably "
-                "due to an ill-formatted proto");
-            nPassed++;
-            continue;
-        }
-
-        if (privacyBuffer.size() == 0) continue;
-
-        for (auto it = mit->second.begin(); it != mit->second.end(); it++) {
-            sp<ReportRequest> request = *it;
-            err = write_section_header(request->fd, id, privacyBuffer.size());
-            if (err != NO_ERROR) {
-                request->err = err;
-                continue;
-            }
-            err = privacyBuffer.flush(request->fd);
-            if (err != NO_ERROR) {
-                request->err = err;
-                continue;
-            }
-            nPassed++;
-            VLOG("Section %d flushed %zu bytes to fd %d with spec %d", id, privacyBuffer.size(),
-                 request->fd, spec.dest);
-        }
-        privacyBuffer.clear();
-    }
-
-    // The dropbox file
-    if (requests->mainFd() >= 0) {
-        PrivacySpec spec = PrivacySpec::new_spec(requests->mainDest());
-        err = privacyBuffer.strip(spec);
-        if (err != NO_ERROR) {
-            ALOGW("Failed to strip section %d with spec %d for dropbox: %s",
-                id, spec.dest, strerror(-err));
-            stats->set_success(false);
-            stats->set_error_msg("Failed to strip section: privacy buffer corrupted, probably "
-                "due to an ill-formatted proto");
-            nPassed++;
-            goto DONE;
-        }
-        if (privacyBuffer.size() == 0) goto DONE;
-
-        err = write_section_header(requests->mainFd(), id, privacyBuffer.size());
-        if (err != NO_ERROR) {
-            requests->setMainFd(-1);
-            goto DONE;
-        }
-        err = privacyBuffer.flush(requests->mainFd());
-        if (err != NO_ERROR) {
-            requests->setMainFd(-1);
-            goto DONE;
-        }
-        nPassed++;
-        VLOG("Section %d flushed %zu bytes to dropbox %d with spec %d", id, privacyBuffer.size(),
-             requests->mainFd(), spec.dest);
-        // Reports bytes of the section uploaded via dropbox after filtering.
-        requests->sectionStats(id)->set_report_size_bytes(privacyBuffer.size());
-    }
-
-DONE:
-    // only returns error if there is no fd to write to.
-    return nPassed > 0 ? NO_ERROR : err;
-}
-
-// ================================================================================
 Section::Section(int i, int64_t timeoutMs, bool userdebugAndEngOnly)
     : id(i),
       timeoutMs(timeoutMs),
@@ -180,86 +71,6 @@
 Section::~Section() {}
 
 // ================================================================================
-HeaderSection::HeaderSection() : Section(FIELD_ID_INCIDENT_HEADER, 0) {}
-
-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<uint8_t>>& headers = request->args.headers();
-
-        for (vector<vector<uint8_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, id, buf->size());
-            WriteFully(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;
-}
-// ================================================================================
-MetadataSection::MetadataSection() : Section(FIELD_ID_INCIDENT_METADATA, 0) {}
-
-MetadataSection::~MetadataSection() {}
-
-status_t MetadataSection::Execute(ReportRequestSet* requests) const {
-    ProtoOutputStream proto;
-    IncidentMetadata metadata = requests->metadata();
-    proto.write(FIELD_TYPE_ENUM | IncidentMetadata::kDestFieldNumber, metadata.dest());
-    proto.write(FIELD_TYPE_INT32 | IncidentMetadata::kRequestSizeFieldNumber,
-                metadata.request_size());
-    proto.write(FIELD_TYPE_BOOL | IncidentMetadata::kUseDropboxFieldNumber, metadata.use_dropbox());
-    for (auto iter = requests->allSectionStats().begin(); iter != requests->allSectionStats().end();
-         iter++) {
-        IncidentMetadata::SectionStats stats = iter->second;
-        uint64_t token = proto.start(FIELD_TYPE_MESSAGE | IncidentMetadata::kSectionsFieldNumber);
-        proto.write(FIELD_TYPE_INT32 | IncidentMetadata::SectionStats::kIdFieldNumber, stats.id());
-        proto.write(FIELD_TYPE_BOOL | IncidentMetadata::SectionStats::kSuccessFieldNumber,
-                    stats.success());
-        proto.write(FIELD_TYPE_INT32 | IncidentMetadata::SectionStats::kReportSizeBytesFieldNumber,
-                    stats.report_size_bytes());
-        proto.write(FIELD_TYPE_INT64 | IncidentMetadata::SectionStats::kExecDurationMsFieldNumber,
-                    stats.exec_duration_ms());
-        proto.write(FIELD_TYPE_INT32 | IncidentMetadata::SectionStats::kDumpSizeBytesFieldNumber,
-                    stats.dump_size_bytes());
-        proto.write(FIELD_TYPE_INT64 | IncidentMetadata::SectionStats::kDumpDurationMsFieldNumber,
-                    stats.dump_duration_ms());
-        proto.write(FIELD_TYPE_BOOL | IncidentMetadata::SectionStats::kTimedOutFieldNumber,
-                    stats.timed_out());
-        proto.write(FIELD_TYPE_BOOL | IncidentMetadata::SectionStats::kIsTruncatedFieldNumber,
-                    stats.is_truncated());
-        proto.write(FIELD_TYPE_STRING | IncidentMetadata::SectionStats::kErrorMsgFieldNumber,
-                    stats.error_msg());
-        proto.end(token);
-    }
-
-    for (ReportRequestSet::iterator it = requests->begin(); it != requests->end(); it++) {
-        const sp<ReportRequest> request = *it;
-        if (request->fd < 0 || request->err != NO_ERROR) {
-            continue;
-        }
-        write_section_header(request->fd, id, proto.size());
-        if (!proto.flush(request->fd)) {
-            ALOGW("Failed to write metadata to fd %d", request->fd);
-            // we don't fail if we can't write to a single request's fd.
-        }
-    }
-    if (requests->mainFd() >= 0) {
-        write_section_header(requests->mainFd(), id, proto.size());
-        if (!proto.flush(requests->mainFd())) {
-            ALOGW("Failed to write metadata to dropbox fd %d", requests->mainFd());
-            return -1;
-        }
-    }
-    return NO_ERROR;
-}
-// ================================================================================
 static inline bool isSysfs(const char* filename) { return strncmp(filename, "/sys/", 5) == 0; }
 
 FileSection::FileSection(int id, const char* filename, const int64_t timeoutMs)
@@ -271,7 +82,7 @@
 
 FileSection::~FileSection() {}
 
-status_t FileSection::Execute(ReportRequestSet* requests) const {
+status_t FileSection::Execute(ReportWriter* writer) const {
     // read from mFilename first, make sure the file is available
     // add O_CLOEXEC to make sure it is closed when exec incident helper
     unique_fd fd(open(mFilename, O_RDONLY | O_CLOEXEC));
@@ -301,7 +112,7 @@
     status_t readStatus = buffer.readProcessedDataInStream(fd.get(), std::move(p2cPipe.writeFd()),
                                                            std::move(c2pPipe.readFd()),
                                                            this->timeoutMs, mIsSysfs);
-    write_section_stats(requests->sectionStats(this->id), buffer);
+    writer->setSectionStats(buffer);
     if (readStatus != NO_ERROR || buffer.timedOut()) {
         ALOGW("[%s] failed to read data from incident helper: %s, timedout: %s",
               this->name.string(), strerror(-readStatus), buffer.timedOut() ? "true" : "false");
@@ -315,7 +126,7 @@
         return ihStatus;
     }
 
-    return write_report_requests(this->id, buffer, requests);
+    return writer->writeSection(buffer);
 }
 // ================================================================================
 GZipSection::GZipSection(int id, const char* filename, ...) : Section(id) {
@@ -332,7 +143,7 @@
 
 GZipSection::~GZipSection() { free(mFilenames); }
 
-status_t GZipSection::Execute(ReportRequestSet* requests) const {
+status_t GZipSection::Execute(ReportWriter* writer) const {
     // Reads the files in order, use the first available one.
     int index = 0;
     unique_fd fd;
@@ -366,7 +177,7 @@
 
     // construct Fdbuffer to output GZippedfileProto, the reason to do this instead of using
     // ProtoOutputStream is to avoid allocation of another buffer inside ProtoOutputStream.
-    EncodedBuffer* internalBuffer = buffer.getInternalBuffer();
+    sp<EncodedBuffer> internalBuffer = buffer.data();
     internalBuffer->writeHeader((uint32_t)GZippedFileProto::FILENAME, WIRE_TYPE_LENGTH_DELIMITED);
     size_t fileLen = strlen(mFilenames[index]);
     internalBuffer->writeRawVarint32(fileLen);
@@ -383,7 +194,7 @@
     status_t readStatus = buffer.readProcessedDataInStream(
             fd.get(), std::move(p2cPipe.writeFd()), std::move(c2pPipe.readFd()), this->timeoutMs,
             isSysfs(mFilenames[index]));
-    write_section_stats(requests->sectionStats(this->id), buffer);
+    writer->setSectionStats(buffer);
     if (readStatus != NO_ERROR || buffer.timedOut()) {
         ALOGW("[%s] failed to read data from gzip: %s, timedout: %s", this->name.string(),
               strerror(-readStatus), buffer.timedOut() ? "true" : "false");
@@ -402,7 +213,7 @@
     internalBuffer->writeRawVarint32(dataSize);
     internalBuffer->copy(dataBeginAt, dataSize);
 
-    return write_report_requests(this->id, buffer, requests);
+    return writer->writeSection(buffer);
 }
 
 // ================================================================================
@@ -458,13 +269,12 @@
     return NULL;
 }
 
-status_t WorkerThreadSection::Execute(ReportRequestSet* requests) const {
+status_t WorkerThreadSection::Execute(ReportWriter* writer) const {
     status_t err = NO_ERROR;
     pthread_t thread;
     pthread_attr_t attr;
     bool workerDone = false;
     FdBuffer buffer;
-    IncidentMetadata::SectionStats* stats = requests->sectionStats(this->id);
 
     // Data shared between this thread and the worker thread.
     sp<WorkerThreadData> data = new WorkerThreadData(this);
@@ -474,10 +284,6 @@
         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) {
@@ -489,12 +295,17 @@
         pthread_attr_destroy(&attr);
         return -err;
     }
+
+    // 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);
+
     err = pthread_create(&thread, &attr, worker_thread_func, (void*)data.get());
+    pthread_attr_destroy(&attr);
     if (err != 0) {
-        pthread_attr_destroy(&attr);
+        data->decStrong(this);
         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->pipe.readFd().get(), this->timeoutMs);
@@ -517,18 +328,17 @@
         workerDone = data->workerDone;
     }
 
-    write_section_stats(stats, buffer);
+    writer->setSectionStats(buffer);
     if (err != NO_ERROR) {
         char errMsg[128];
         snprintf(errMsg, 128, "[%s] failed with error '%s'",
             this->name.string(), strerror(-err));
-        stats->set_success(false);
-        stats->set_error_msg(errMsg);
+        writer->error(this, err, "WorkerThreadSection failed.");
         return NO_ERROR;
     }
     if (buffer.truncated()) {
         ALOGW("[%s] too large, truncating", this->name.string());
-        // Do not write a truncated section. It won't pass through the PrivacyBuffer.
+        // Do not write a truncated section. It won't pass through the PrivacyFilter.
         return NO_ERROR;
     }
     if (!workerDone || buffer.timedOut()) {
@@ -537,7 +347,7 @@
     }
 
     // Write the data that was collected
-    return write_report_requests(this->id, buffer, requests);
+    return writer->writeSection(buffer);
 }
 
 // ================================================================================
@@ -568,7 +378,7 @@
 
 CommandSection::~CommandSection() { free(mCommand); }
 
-status_t CommandSection::Execute(ReportRequestSet* requests) const {
+status_t CommandSection::Execute(ReportWriter* writer) const {
     FdBuffer buffer;
     Fpipe cmdPipe;
     Fpipe ihPipe;
@@ -591,7 +401,7 @@
 
     cmdPipe.writeFd().reset();
     status_t readStatus = buffer.read(ihPipe.readFd().get(), this->timeoutMs);
-    write_section_stats(requests->sectionStats(this->id), buffer);
+    writer->setSectionStats(buffer);
     if (readStatus != NO_ERROR || buffer.timedOut()) {
         ALOGW("[%s] failed to read data from incident helper: %s, timedout: %s",
               this->name.string(), strerror(-readStatus), buffer.timedOut() ? "true" : "false");
@@ -605,13 +415,13 @@
     status_t cmdStatus = wait_child(cmdPid);
     status_t ihStatus = wait_child(ihPid);
     if (cmdStatus != NO_ERROR || ihStatus != NO_ERROR) {
-        ALOGW("[%s] abnormal child processes, return status: command: %s, incident "
-              "helper: %s",
+        ALOGW("[%s] abnormal child processes, return status: command: %s, incident helper: %s",
               this->name.string(), strerror(-cmdStatus), strerror(-ihStatus));
-        return cmdStatus != NO_ERROR ? cmdStatus : ihStatus;
+        // Not a fatal error.
+        return NO_ERROR;
     }
 
-    return write_report_requests(this->id, buffer, requests);
+    return writer->writeSection(buffer);
 }
 
 // ================================================================================
@@ -842,7 +652,8 @@
         const std::string link_name = android::base::StringPrintf("/proc/%d/exe", pid);
         std::string exe;
         if (!android::base::Readlink(link_name, &exe)) {
-            ALOGE("Can't read '%s': %s\n", link_name.c_str(), strerror(errno));
+            ALOGE("Section %s: Can't read '%s': %s\n", name.string(),
+                    link_name.c_str(), strerror(errno));
             continue;
         }
 
@@ -913,10 +724,10 @@
         }
 
         auto dump = std::make_unique<char[]>(buffer.size());
-        auto iterator = buffer.data();
+        sp<ProtoReader> reader = buffer.data()->read();
         int i = 0;
-        while (iterator.hasNext()) {
-            dump[i] = iterator.next();
+        while (reader->hasNext()) {
+            dump[i] = reader->next();
             i++;
         }
         uint64_t token = proto.start(android::os::BackTraceProto::TRACES);