Implement Pii Stripper Part 3
The incident request args sets privacy spec. Strip action is optimized
to run once for each type of spec and ready for flush multiple times.
Incident command is updated to take -p option to specify privacy spec.
Bug: 64687253
Test: unit tests written, manually run incident command to test as well
Change-Id: I6753df117f76dc1a5f4d2152baa3fbbf56b490e4
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/incidentd/src/EncodedBuffer.cpp b/cmds/incidentd/src/EncodedBuffer.cpp
index d1872f9..3d20548 100644
--- a/cmds/incidentd/src/EncodedBuffer.cpp
+++ b/cmds/incidentd/src/EncodedBuffer.cpp
@@ -70,7 +70,6 @@
if (skip) {
iterator += bytesToWrite;
} else {
- buf.reserve(bytesToWrite);
for (size_t i=0; i<bytesToWrite; i++) {
buf.push_back(*iterator);
iterator++;
@@ -192,4 +191,5 @@
if (err != NO_ERROR) return err;
}
return NO_ERROR;
-}
\ No newline at end of file
+}
+
diff --git a/cmds/incidentd/src/FdBuffer.cpp b/cmds/incidentd/src/FdBuffer.cpp
index 23a611a..bb399b5 100644
--- a/cmds/incidentd/src/FdBuffer.cpp
+++ b/cmds/incidentd/src/FdBuffer.cpp
@@ -259,6 +259,12 @@
}
FdBuffer::iterator
+FdBuffer::begin() const
+{
+ return iterator(*this, 0, 0);
+}
+
+FdBuffer::iterator
FdBuffer::end() const
{
if (mBuffers.empty() || mCurrentWritten < 0) return begin();
@@ -268,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)
{
@@ -280,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() 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 4c4823e..dfe39c6 100644
--- a/cmds/incidentd/src/FdBuffer.h
+++ b/cmds/incidentd/src/FdBuffer.h
@@ -87,32 +87,29 @@
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)
- : mFdBuffer(buffer), mIndex(index), mOffset(offset) {}
- iterator& operator=(iterator& other) const { 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); }
- int operator-(iterator other) const { return (int)bytesRead() - (int)other.bytesRead(); }
- reference operator*() const { return mFdBuffer.mBuffers[mIndex][mOffset]; }
+ 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 { return iterator(mFdBuffer, mIndex, mOffset); }
+ iterator snapshot() const;
// how many bytes are read
size_t bytesRead() const;
// random access could make the iterator out of bound
- bool outOfBound() const { return bytesRead() > mFdBuffer.size(); }
+ bool outOfBound() const;
private:
const FdBuffer& mFdBuffer;
size_t mIndex;
size_t mOffset;
};
- iterator begin() const { return iterator(*this, 0, 0); }
+ iterator begin() const;
iterator end() const;
private:
diff --git a/cmds/incidentd/src/Privacy.cpp b/cmds/incidentd/src/Privacy.cpp
index a790efa..dbab548 100644
--- a/cmds/incidentd/src/Privacy.cpp
+++ b/cmds/incidentd/src/Privacy.cpp
@@ -88,6 +88,12 @@
}
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;
@@ -97,4 +103,9 @@
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
index 53b0325..c56ba9b8 100644
--- a/cmds/incidentd/src/Privacy.h
+++ b/cmds/incidentd/src/Privacy.h
@@ -58,10 +58,13 @@
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/Section.cpp b/cmds/incidentd/src/Section.cpp
index a04804c..08959263 100644
--- a/cmds/incidentd/src/Section.cpp
+++ b/cmds/incidentd/src/Section.cpp
@@ -27,6 +27,7 @@
#include <private/android_filesystem_config.h>
#include <binder/IServiceManager.h>
+#include <map>
#include <mutex>
#include <wait.h>
#include <unistd.h>
@@ -109,20 +110,16 @@
}
static status_t
-WriteToRequest(const int id, const int fd, EncodedBuffer& buffer, const PrivacySpec& spec)
+WriteToRequest(const int id, const int fd, EncodedBuffer& buffer)
{
- if (fd < 0) return EBADF;
+ if (buffer.size() == 0) return NO_ERROR;
+
status_t err = NO_ERROR;
uint8_t buf[20];
-
- buffer.clear(); // clear before strip
- err = buffer.strip(spec); // TODO: don't have to strip again if the spec is the same.
- if (err != NO_ERROR || buffer.size() == 0) return err;
uint8_t *p = write_length_delimited_tag_header(buf, id, buffer.size());
err = write_all(fd, buf, p-buf);
if (err == NO_ERROR) {
err = buffer.flush(fd);
- ALOGD("Section %d flushed %zu bytes to fd %d with spec %d", id, buffer.size(), fd, spec.dest);
}
return err;
}
@@ -130,25 +127,46 @@
static status_t
WriteToReportRequests(const int id, const FdBuffer& buffer, ReportRequestSet* requests)
{
- status_t err = EBADF;
+ status_t err = -EBADF;
EncodedBuffer encodedBuffer(buffer, GetPrivacyOfSection(id));
int writeable = 0;
- // The streaming ones
+ // 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;
- PrivacySpec spec; // TODO: this should be derived from each request.
- err = WriteToRequest(id, request->fd, encodedBuffer, spec);
- if (err != NO_ERROR) {
- request->err = err;
- } else {
- writeable++;
+ if (!request->args.containsSection(id) || request->fd < 0 || request->err != NO_ERROR) {
+ 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 = WriteToRequest(id, request->fd, encodedBuffer);
+ if (err != NO_ERROR) {
+ request->err = err;
+ } else {
+ 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 = WriteToRequest(id, requests->mainFd(), encodedBuffer, get_default_dropbox_spec());
+ err = encodedBuffer.strip(get_default_dropbox_spec());
+ if (err != NO_ERROR) return err; // the buffer data is corrupted.
+
+ err = WriteToRequest(id, requests->mainFd(), encodedBuffer);
if (err != NO_ERROR) {
requests->setMainFd(-1);
} else {
diff --git a/cmds/incidentd/tests/Reporter_test.cpp b/cmds/incidentd/tests/Reporter_test.cpp
index 3c1b44b..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 {
diff --git a/cmds/incidentd/tests/Section_test.cpp b/cmds/incidentd/tests/Section_test.cpp
index ab0f054..f2aee8f 100644
--- a/cmds/incidentd/tests/Section_test.cpp
+++ b/cmds/incidentd/tests/Section_test.cpp
@@ -33,11 +33,27 @@
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;
+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; };
+};
+
// NOTICE: this test requires /system/bin/incident_helper is installed.
TEST(SectionTest, FileSection) {
TemporaryFile tf;
@@ -126,4 +142,118 @@
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/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/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) {