summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cmds/incident_helper/IncidentHelper.cpp17
-rw-r--r--cmds/incident_helper/IncidentHelper.h11
-rw-r--r--cmds/incident_helper/main.cpp4
-rw-r--r--cmds/incidentd/Android.mk9
-rw-r--r--cmds/incidentd/src/EncodedBuffer.cpp195
-rw-r--r--cmds/incidentd/src/EncodedBuffer.h64
-rw-r--r--cmds/incidentd/src/FdBuffer.cpp20
-rw-r--r--cmds/incidentd/src/FdBuffer.h54
-rw-r--r--cmds/incidentd/src/Privacy.cpp100
-rw-r--r--cmds/incidentd/src/Privacy.h67
-rw-r--r--cmds/incidentd/src/Reporter.cpp49
-rw-r--r--cmds/incidentd/src/Reporter.h7
-rw-r--r--cmds/incidentd/src/Section.cpp98
-rw-r--r--cmds/incidentd/src/Section.h4
-rw-r--r--cmds/incidentd/src/io_util.cpp44
-rw-r--r--cmds/incidentd/src/io_util.h41
-rw-r--r--cmds/incidentd/src/protobuf.cpp34
-rw-r--r--cmds/incidentd/src/protobuf.h28
-rw-r--r--cmds/incidentd/src/section_list.h32
-rw-r--r--cmds/incidentd/tests/EncodedBuffer_test.cpp207
-rw-r--r--cmds/incidentd/tests/FdBuffer_test.cpp31
-rw-r--r--cmds/incidentd/tests/Reporter_test.cpp24
-rw-r--r--cmds/incidentd/tests/Section_test.cpp54
-rw-r--r--cmds/incidentd/tests/section_list.cpp15
-rw-r--r--tools/incident_section_gen/main.cpp25
25 files changed, 1005 insertions, 229 deletions
diff --git a/cmds/incident_helper/IncidentHelper.cpp b/cmds/incident_helper/IncidentHelper.cpp
index fba5e662b7c1..787d3a1557d6 100644
--- a/cmds/incident_helper/IncidentHelper.cpp
+++ b/cmds/incident_helper/IncidentHelper.cpp
@@ -61,6 +61,21 @@ SetTableField(::google::protobuf::Message* message, string field_name, string fi
}
// ================================================================================
+status_t NoopParser::Parse(const int in, const int out) const
+{
+ string content;
+ if (!ReadFdToString(in, &content)) {
+ fprintf(stderr, "[%s]Failed to read data from incidentd\n", this->name.string());
+ return -1;
+ }
+ if (!WriteStringToFd(content, out)) {
+ fprintf(stderr, "[%s]Failed to write data to incidentd\n", this->name.string());
+ return -1;
+ }
+ return NO_ERROR;
+}
+
+// ================================================================================
status_t ReverseParser::Parse(const int in, const int out) const
{
string content;
@@ -189,4 +204,4 @@ status_t ProcrankParser::Parse(const int in, const int out) const {
}
fprintf(stderr, "[%s]Proto size: %d bytes\n", this->name.string(), proto.ByteSize());
return NO_ERROR;
-} \ No newline at end of file
+}
diff --git a/cmds/incident_helper/IncidentHelper.h b/cmds/incident_helper/IncidentHelper.h
index f319c419fcd6..f6579a2d3736 100644
--- a/cmds/incident_helper/IncidentHelper.h
+++ b/cmds/incident_helper/IncidentHelper.h
@@ -36,6 +36,17 @@ public:
};
/**
+ * No op parser returns what it reads
+ */
+class NoopParser : public TextParserBase {
+public:
+ NoopParser() : TextParserBase(String8("NoopParser")) {};
+ ~NoopParser() {};
+
+ virtual status_t Parse(const int in, const int out) const;
+};
+
+/**
* This parser is used for testing only, results in timeout.
*/
class TimeoutParser : public TextParserBase {
diff --git a/cmds/incident_helper/main.cpp b/cmds/incident_helper/main.cpp
index 333344b8ce86..296d3001b7bb 100644
--- a/cmds/incident_helper/main.cpp
+++ b/cmds/incident_helper/main.cpp
@@ -41,9 +41,11 @@ static TextParserBase* selectParser(int section) {
case -1:
return new TimeoutParser();
case 0:
+ return new NoopParser();
+ case 1: // 1 is reserved for incident header so it won't be section id
return new ReverseParser();
/* ========================================================================= */
- // IDs larger than 0 are reserved in incident.proto
+ // IDs larger than 1 are section ids reserved in incident.proto
case 2000:
return new ProcrankParser();
case 2002:
diff --git a/cmds/incidentd/Android.mk b/cmds/incidentd/Android.mk
index 835a7b94507b..830bf9e66cde 100644
--- a/cmds/incidentd/Android.mk
+++ b/cmds/incidentd/Android.mk
@@ -23,10 +23,13 @@ include $(CLEAR_VARS)
LOCAL_MODULE := incidentd
LOCAL_SRC_FILES := \
+ src/EncodedBuffer.cpp \
src/FdBuffer.cpp \
src/IncidentService.cpp \
+ src/Privacy.cpp \
src/Reporter.cpp \
src/Section.cpp \
+ src/io_util.cpp \
src/main.cpp \
src/protobuf.cpp \
src/report_directory.cpp
@@ -69,7 +72,9 @@ LOCAL_GENERATED_SOURCES += $(GEN)
gen_src_dir:=
GEN:=
+ifeq ($(BUILD_WITH_INCIDENTD_RC), true)
LOCAL_INIT_RC := incidentd.rc
+endif
include $(BUILD_EXECUTABLE)
@@ -88,12 +93,16 @@ LOCAL_CFLAGS := -Werror -Wall -Wno-unused-variable -Wunused-parameter
LOCAL_C_INCLUDES += $(LOCAL_PATH)/src
LOCAL_SRC_FILES := \
+ src/EncodedBuffer.cpp \
src/FdBuffer.cpp \
+ src/Privacy.cpp \
src/Reporter.cpp \
src/Section.cpp \
+ src/io_util.cpp \
src/protobuf.cpp \
src/report_directory.cpp \
tests/section_list.cpp \
+ tests/EncodedBuffer_test.cpp \
tests/FdBuffer_test.cpp \
tests/Reporter_test.cpp \
tests/Section_test.cpp \
diff --git a/cmds/incidentd/src/EncodedBuffer.cpp b/cmds/incidentd/src/EncodedBuffer.cpp
new file mode 100644
index 000000000000..d1872f96b9df
--- /dev/null
+++ b/cmds/incidentd/src/EncodedBuffer.cpp
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "EncodedBuffer.h"
+#include "io_util.h"
+#include "protobuf.h"
+
+#include <deque>
+
+const size_t BUFFER_SIZE = 4 * 1024; // 4 KB
+
+/**
+ * Read varint from iterator, the iterator will point to next available byte.
+ * Return the number of bytes of the varint.
+ */
+static uint32_t
+read_raw_varint(FdBuffer::iterator& it)
+{
+ uint32_t val = 0;
+ int i = 0;
+ bool hasNext = true;
+ while (hasNext) {
+ hasNext = ((*it & 0x80) != 0);
+ val += (*it & 0x7F) << (7*i);
+ it++;
+ i++;
+ }
+ return val;
+}
+
+/**
+ * Write the field to buf based on the wire type, iterator will point to next field.
+ * If skip is set to true, no data will be written to buf. Return number of bytes written.
+ */
+static size_t
+write_field_or_skip(FdBuffer::iterator &iterator, vector<uint8_t> &buf, uint8_t wireType, bool skip)
+{
+ FdBuffer::iterator snapshot = iterator.snapshot();
+ size_t bytesToWrite = 0;
+ uint32_t varint = 0;
+ switch (wireType) {
+ case WIRE_TYPE_VARINT:
+ varint = read_raw_varint(iterator);
+ if(!skip) return write_raw_varint(buf, varint);
+ break;
+ case WIRE_TYPE_FIXED64:
+ bytesToWrite = 8;
+ break;
+ case WIRE_TYPE_LENGTH_DELIMITED:
+ bytesToWrite = read_raw_varint(iterator);
+ if(!skip) write_raw_varint(buf, bytesToWrite);
+ break;
+ case WIRE_TYPE_FIXED32:
+ bytesToWrite = 4;
+ break;
+ }
+ if (skip) {
+ iterator += bytesToWrite;
+ } else {
+ buf.reserve(bytesToWrite);
+ for (size_t i=0; i<bytesToWrite; i++) {
+ buf.push_back(*iterator);
+ iterator++;
+ }
+ }
+ return skip ? 0 : iterator - snapshot;
+}
+
+/**
+ * Strip next field based on its private policy and request spec, then stores data in buf.
+ * Return NO_ERROR if succeeds, otherwise BAD_VALUE is returned to indicate bad data in FdBuffer.
+ *
+ * The iterator must point to the head of a protobuf formatted field for successful operation.
+ * After exit with NO_ERROR, iterator points to the next protobuf field's head.
+ */
+static status_t
+stripField(FdBuffer::iterator &iterator, vector<uint8_t> &buf, const Privacy* parentPolicy, const PrivacySpec& spec)
+{
+ if (iterator.outOfBound() || parentPolicy == NULL) return BAD_VALUE;
+
+ uint32_t varint = read_raw_varint(iterator);
+ uint8_t wireType = read_wire_type(varint);
+ uint32_t fieldId = read_field_id(varint);
+ const Privacy* policy = parentPolicy->lookup(fieldId);
+
+ if (policy == NULL || !policy->IsMessageType() || !policy->HasChildren()) {
+ bool skip = !spec.CheckPremission(policy);
+ size_t amt = buf.size();
+ if (!skip) amt += write_header(buf, fieldId, wireType);
+ amt += write_field_or_skip(iterator, buf, wireType, skip); // point to head of next field
+ return buf.size() != amt ? BAD_VALUE : NO_ERROR;
+ }
+ // current field is message type and its sub-fields have extra privacy policies
+ deque<vector<uint8_t>> q;
+ uint32_t msgSize = read_raw_varint(iterator);
+ size_t finalSize = 0;
+ FdBuffer::iterator start = iterator.snapshot();
+ while ((iterator - start) != (int)msgSize) {
+ vector<uint8_t> v;
+ status_t err = stripField(iterator, v, policy, spec);
+ if (err != NO_ERROR) return err;
+ if (v.empty()) continue;
+ q.push_back(v);
+ finalSize += v.size();
+ }
+
+ write_header(buf, fieldId, wireType);
+ write_raw_varint(buf, finalSize);
+ buf.reserve(finalSize);
+ while (!q.empty()) {
+ vector<uint8_t> subField = q.front();
+ for (vector<uint8_t>::iterator it = subField.begin(); it != subField.end(); it++) {
+ buf.push_back(*it);
+ }
+ q.pop_front();
+ }
+ return NO_ERROR;
+}
+
+// ================================================================================
+EncodedBuffer::EncodedBuffer(const FdBuffer& buffer, const Privacy* policy)
+ : mFdBuffer(buffer),
+ mPolicy(policy),
+ mBuffers(),
+ mSize(0)
+{
+}
+
+EncodedBuffer::~EncodedBuffer()
+{
+}
+
+status_t
+EncodedBuffer::strip(const PrivacySpec& spec)
+{
+ // optimization when no strip happens
+ if (mPolicy == NULL || !mPolicy->HasChildren() || spec.RequireAll()) {
+ if (spec.CheckPremission(mPolicy)) mSize = mFdBuffer.size();
+ return NO_ERROR;
+ }
+
+ FdBuffer::iterator it = mFdBuffer.begin();
+ vector<uint8_t> field;
+ field.reserve(BUFFER_SIZE);
+
+ while (it != mFdBuffer.end()) {
+ status_t err = stripField(it, field, mPolicy, spec);
+ if (err != NO_ERROR) return err;
+ if (field.size() > BUFFER_SIZE) { // rotate to another chunk if buffer size exceeds
+ mBuffers.push_back(field);
+ mSize += field.size();
+ field.clear();
+ }
+ }
+ if (!field.empty()) {
+ mBuffers.push_back(field);
+ mSize += field.size();
+ }
+ return NO_ERROR;
+}
+
+void
+EncodedBuffer::clear()
+{
+ mSize = 0;
+ mBuffers.clear();
+}
+
+size_t
+EncodedBuffer::size() const { return mSize; }
+
+status_t
+EncodedBuffer::flush(int fd)
+{
+ if (size() == mFdBuffer.size()) return mFdBuffer.flush(fd);
+
+ for (vector<vector<uint8_t>>::iterator it = mBuffers.begin(); it != mBuffers.end(); it++) {
+ status_t err = write_all(fd, it->data(), it->size());
+ if (err != NO_ERROR) return err;
+ }
+ return NO_ERROR;
+} \ No newline at end of file
diff --git a/cmds/incidentd/src/EncodedBuffer.h b/cmds/incidentd/src/EncodedBuffer.h
new file mode 100644
index 000000000000..ea8603a585d7
--- /dev/null
+++ b/cmds/incidentd/src/EncodedBuffer.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ENCODED_BUFFER_H
+#define ENCODED_BUFFER_H
+
+#include "FdBuffer.h"
+#include "Privacy.h"
+
+#include <stdint.h>
+#include <vector>
+
+/**
+ * EncodedBuffer is constructed from FdBuffer which holds original protobuf formatted data and
+ * its privacy policy in its tagged proto message. The class strips PII-sensitive fields
+ * based on the request and holds stripped data in its buffer for output.
+ */
+class EncodedBuffer
+{
+public:
+ EncodedBuffer(const FdBuffer& buffer, const Privacy* policy);
+ ~EncodedBuffer();
+
+ /**
+ * Strip based on the request and hold data in its own buffer. Return NO_ERROR if strip succeeds.
+ */
+ status_t strip(const PrivacySpec& spec);
+
+ /**
+ * Clear encoded buffer so it can be reused by another request.
+ */
+ void clear();
+
+ /**
+ * Return the size of the stripped data.
+ */
+ size_t size() const;
+
+ /**
+ * Flush buffer to the given fd. NO_ERROR is returned if the flush succeeds.
+ */
+ status_t flush(int fd);
+
+private:
+ const FdBuffer& mFdBuffer;
+ const Privacy* mPolicy;
+ vector<vector<uint8_t>> mBuffers;
+ size_t mSize;
+};
+
+#endif // ENCODED_BUFFER_H \ No newline at end of file
diff --git a/cmds/incidentd/src/FdBuffer.cpp b/cmds/incidentd/src/FdBuffer.cpp
index 4d6a36cdba2e..23a611acd494 100644
--- a/cmds/incidentd/src/FdBuffer.cpp
+++ b/cmds/incidentd/src/FdBuffer.cpp
@@ -17,6 +17,7 @@
#define LOG_TAG "incidentd"
#include "FdBuffer.h"
+#include "io_util.h"
#include <cutils/log.h>
#include <utils/SystemClock.h>
@@ -239,25 +240,26 @@ FdBuffer::readProcessedDataInStream(int fd, int toFd, int fromFd, int64_t timeou
}
size_t
-FdBuffer::size()
+FdBuffer::size() const
{
if (mBuffers.empty()) return 0;
return ((mBuffers.size() - 1) * BUFFER_SIZE) + mCurrentWritten;
}
status_t
-FdBuffer::write(ReportRequestSet* reporter)
+FdBuffer::flush(int fd) const
{
- const int N = mBuffers.size() - 1;
- for (int i=0; i<N; i++) {
- reporter->write(mBuffers[i], BUFFER_SIZE);
+ size_t i=0;
+ status_t err = NO_ERROR;
+ for (i=0; i<mBuffers.size()-1; i++) {
+ err = write_all(fd, mBuffers[i], BUFFER_SIZE);
+ if (err != NO_ERROR) return err;
}
- reporter->write(mBuffers[N], mCurrentWritten);
- return NO_ERROR;
+ return write_all(fd, mBuffers[i], mCurrentWritten);
}
FdBuffer::iterator
-FdBuffer::end()
+FdBuffer::end() const
{
if (mBuffers.empty() || mCurrentWritten < 0) return begin();
if (mCurrentWritten == BUFFER_SIZE)
@@ -279,7 +281,7 @@ FdBuffer::iterator::operator+(size_t offset)
}
size_t
-FdBuffer::iterator::bytesRead()
+FdBuffer::iterator::bytesRead() const
{
return mIndex * BUFFER_SIZE + mOffset;
}
diff --git a/cmds/incidentd/src/FdBuffer.h b/cmds/incidentd/src/FdBuffer.h
index e9a53ffe513a..4c4823e5a4d8 100644
--- a/cmds/incidentd/src/FdBuffer.h
+++ b/cmds/incidentd/src/FdBuffer.h
@@ -17,8 +17,6 @@
#ifndef FD_BUFFER_H
#define FD_BUFFER_H
-#include "Reporter.h"
-
#include <utils/Errors.h>
#include <vector>
@@ -55,7 +53,7 @@ public:
/**
* Whether we timed out.
*/
- bool timedOut() { return mTimedOut; }
+ bool timedOut() const { return mTimedOut; }
/**
* If more than 4 MB is read, we truncate the data and return success.
@@ -65,23 +63,22 @@ public:
* happens, truncated() will return true so it can be marked. If the data is
* exactly 4 MB, truncated is still set. Sorry.
*/
- bool truncated() { return mTruncated; }
+ bool truncated() const { return mTruncated; }
/**
* How much data was read.
*/
- size_t size();
+ size_t size() const;
/**
- * [Deprecated] Write the data that we recorded to the fd given.
- * TODO: remove it once the iterator api is working
+ * Flush all the data to given file descriptor;
*/
- status_t write(ReportRequestSet* requests);
+ status_t flush(int fd) const;
/**
* How long the read took in milliseconds.
*/
- int64_t durationMs() { return mFinishTime - mStartTime; }
+ int64_t durationMs() const { return mFinishTime - mStartTime; }
/**
* Read data stored in FdBuffer
@@ -89,14 +86,10 @@ public:
class iterator;
friend class iterator;
class iterator : public std::iterator<std::random_access_iterator_tag, uint8_t> {
- private:
- FdBuffer& mFdBuffer;
- size_t mIndex;
- size_t mOffset;
public:
- explicit iterator(FdBuffer& buffer, ssize_t index, ssize_t offset)
+ iterator(const FdBuffer& buffer, ssize_t index, ssize_t offset)
: mFdBuffer(buffer), mIndex(index), mOffset(offset) {}
- iterator& operator=(iterator& other) { return other; }
+ 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; }
@@ -105,14 +98,22 @@ public:
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]; }
+ // return the snapshot of the current iterator
+ iterator snapshot() const { return iterator(mFdBuffer, mIndex, mOffset); }
+ // how many bytes are read
+ size_t bytesRead() const;
// random access could make the iterator out of bound
- size_t bytesRead();
- bool outOfBound() { return bytesRead() > mFdBuffer.size(); };
+ bool outOfBound() const { return bytesRead() > mFdBuffer.size(); }
+ private:
+ const FdBuffer& mFdBuffer;
+ size_t mIndex;
+ size_t mOffset;
};
- iterator begin() { return iterator(*this, 0, 0); }
- iterator end();
+ iterator begin() const { return iterator(*this, 0, 0); }
+ iterator end() const;
private:
vector<uint8_t*> mBuffers;
@@ -123,19 +124,4 @@ private:
bool mTruncated;
};
-class Fpipe {
-public:
- Fpipe() {}
- bool close() { return !(::close(mFds[0]) || ::close(mFds[1])); }
- ~Fpipe() { close(); }
-
- inline bool init() { return pipe(mFds) != -1; }
- inline int readFd() const { return mFds[0]; }
- inline int writeFd() const { return mFds[1]; }
-
-private:
- int mFds[2];
-};
-
-
#endif // FD_BUFFER_H
diff --git a/cmds/incidentd/src/Privacy.cpp b/cmds/incidentd/src/Privacy.cpp
new file mode 100644
index 000000000000..a790efa2f611
--- /dev/null
+++ b/cmds/incidentd/src/Privacy.cpp
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Privacy.h"
+
+// DESTINATION enum value
+const uint8_t DEST_LOCAL = 0;
+const uint8_t DEST_EXPLICIT = 1;
+const uint8_t DEST_AUTOMATIC = 2;
+
+// type of the field, identitical to protobuf definition
+const uint8_t TYPE_STRING = 9;
+const uint8_t TYPE_MESSAGE = 11;
+
+Privacy::Privacy(uint32_t field_id, uint8_t type, uint8_t dest)
+ : field_id(field_id),
+ type(type),
+ children(NULL),
+ dest(dest),
+ patterns(NULL)
+{
+}
+
+Privacy::Privacy(uint32_t field_id, const Privacy** children)
+ : field_id(field_id),
+ type(TYPE_MESSAGE),
+ children(children),
+ dest(DEST_DEFAULT_VALUE), // this will be ignored
+ patterns(NULL)
+{
+}
+
+Privacy::Privacy(uint32_t field_id, uint8_t dest, const char** patterns)
+ : field_id(field_id),
+ type(TYPE_STRING),
+ children(NULL),
+ dest(dest),
+ patterns(patterns)
+{
+}
+
+bool
+Privacy::IsMessageType() const { return type == TYPE_MESSAGE; }
+
+bool
+Privacy::IsStringType() const { return type == TYPE_STRING; }
+
+bool
+Privacy::HasChildren() const { return children != NULL && children[0] != NULL; }
+
+const Privacy*
+Privacy::lookup(uint32_t fieldId) const
+{
+ if (children == NULL) return NULL;
+ for (int i=0; children[i] != NULL; i++) {
+ if (children[i]->field_id == fieldId) return children[i];
+ // This assumes the list's field id is in ascending order and must be true.
+ if (children[i]->field_id > fieldId) return NULL;
+ }
+ return NULL;
+}
+
+static bool allowDest(const uint8_t dest, const uint8_t policy)
+{
+ switch (policy) {
+ case DEST_LOCAL:
+ return dest == DEST_LOCAL;
+ case DEST_EXPLICIT:
+ return dest == DEST_LOCAL || dest == DEST_EXPLICIT;
+ case DEST_AUTOMATIC:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool
+PrivacySpec::CheckPremission(const Privacy* privacy) const
+{
+ uint8_t policy = privacy == NULL ? DEST_DEFAULT_VALUE : privacy->dest;
+ return allowDest(dest, policy);
+}
+
+bool
+PrivacySpec::RequireAll() const { return dest == DEST_LOCAL; }
+
+PrivacySpec get_default_dropbox_spec() { return PrivacySpec(DEST_AUTOMATIC); } \ No newline at end of file
diff --git a/cmds/incidentd/src/Privacy.h b/cmds/incidentd/src/Privacy.h
new file mode 100644
index 000000000000..53b0325b15f4
--- /dev/null
+++ b/cmds/incidentd/src/Privacy.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef PRIVACY_H
+#define PRIVACY_H
+
+#include <stdint.h>
+
+// This is the default value of DEST enum
+const uint8_t DEST_DEFAULT_VALUE = 1;
+
+/*
+ * In order not to depend on libprotobuf-cpp-full nor libplatformprotos in incidentd,
+ * privacy options's data structure are explicitly redefined in this file.
+ */
+struct Privacy {
+ uint32_t field_id;
+ uint8_t type;
+ // ignore parent's privacy flags if children are set, NULL-terminated
+ const Privacy** children;
+
+ // the following fields are identitical to
+ // frameworks/base/libs/incident/proto/android/privacy.proto
+ uint8_t dest;
+ const char** patterns; // only set when type is string
+
+ Privacy(uint32_t field_id, uint8_t type, uint8_t dest); // generic constructor
+ Privacy(uint32_t field_id, const Privacy** children); // used for message type
+ Privacy(uint32_t field_id, uint8_t dest, const char** patterns); // used for string type
+
+ bool IsMessageType() const;
+ bool IsStringType() const;
+ bool HasChildren() const;
+ const Privacy* lookup(uint32_t fieldId) const;
+};
+
+/**
+ * PrivacySpec defines the request has what level of privacy authorization.
+ * For example, a device without user consent should only be able to upload AUTOMATIC fields.
+ */
+class PrivacySpec {
+public:
+ const uint8_t dest;
+
+ PrivacySpec() : dest(DEST_DEFAULT_VALUE) {}
+ PrivacySpec(uint8_t dest) : dest(dest) {}
+
+ bool CheckPremission(const Privacy* privacy) const;
+ bool RequireAll() const;
+};
+
+PrivacySpec get_default_dropbox_spec();
+
+#endif // PRIVACY_H
diff --git a/cmds/incidentd/src/Reporter.cpp b/cmds/incidentd/src/Reporter.cpp
index 4ffc11984224..722bd4fa8b87 100644
--- a/cmds/incidentd/src/Reporter.cpp
+++ b/cmds/incidentd/src/Reporter.cpp
@@ -17,6 +17,7 @@
#define LOG_TAG "incidentd"
#include "Reporter.h"
+#include "io_util.h"
#include "protobuf.h"
#include "report_directory.h"
@@ -38,20 +39,6 @@
static const char* INCIDENT_DIRECTORY = "/data/misc/incidents/";
// ================================================================================
-static status_t write_all(int fd, uint8_t const* buf, size_t size)
-{
- while (size > 0) {
- ssize_t amt = ::write(fd, buf, size);
- if (amt < 0) {
- return -errno;
- }
- size -= amt;
- buf += amt;
- }
- return NO_ERROR;
-}
-
-// ================================================================================
ReportRequest::ReportRequest(const IncidentReportArgs& a,
const sp<IIncidentReportStatusListener> &l, int f)
:args(a),
@@ -69,7 +56,6 @@ ReportRequest::~ReportRequest()
ReportRequestSet::ReportRequestSet()
:mRequests(),
mSections(),
- mWritableCount(0),
mMainFd(-1)
{
}
@@ -84,45 +70,12 @@ ReportRequestSet::add(const sp<ReportRequest>& request)
{
mRequests.push_back(request);
mSections.merge(request->args);
- mWritableCount++;
}
void
ReportRequestSet::setMainFd(int fd)
{
mMainFd = fd;
- mWritableCount++;
-}
-
-status_t
-ReportRequestSet::write(uint8_t const* buf, size_t size)
-{
- status_t err = EBADF;
-
- // The streaming ones
- int const N = mRequests.size();
- for (int i=N-1; i>=0; i--) {
- sp<ReportRequest> request = mRequests[i];
- if (request->fd >= 0 && request->err == NO_ERROR) {
- err = write_all(request->fd, buf, size);
- if (err != NO_ERROR) {
- request->err = err;
- mWritableCount--;
- }
- }
- }
-
- // The dropbox file
- if (mMainFd >= 0) {
- err = write_all(mMainFd, buf, size);
- if (err != NO_ERROR) {
- mMainFd = -1;
- mWritableCount--;
- }
- }
-
- // Return an error only when there are no FDs to write.
- return mWritableCount > 0 ? NO_ERROR : err;
}
bool
diff --git a/cmds/incidentd/src/Reporter.h b/cmds/incidentd/src/Reporter.h
index 509611c34d4b..b74cc4290306 100644
--- a/cmds/incidentd/src/Reporter.h
+++ b/cmds/incidentd/src/Reporter.h
@@ -52,21 +52,16 @@ public:
void add(const sp<ReportRequest>& request);
void setMainFd(int fd);
- // Write to all of the fds for the requests. If a write fails, it stops
- // writing to that fd and returns NO_ERROR. When we are out of fds to write
- // to it returns an error.
- status_t write(uint8_t const* buf, size_t size);
-
typedef vector<sp<ReportRequest>>::iterator iterator;
iterator begin() { return mRequests.begin(); }
iterator end() { return mRequests.end(); }
+ int mainFd() { return mMainFd; }
bool containsSection(int id);
private:
vector<sp<ReportRequest>> mRequests;
IncidentReportArgs mSections;
- int mWritableCount;
int mMainFd;
};
diff --git a/cmds/incidentd/src/Section.cpp b/cmds/incidentd/src/Section.cpp
index ac87fe3b1e40..a04804c8c770 100644
--- a/cmds/incidentd/src/Section.cpp
+++ b/cmds/incidentd/src/Section.cpp
@@ -16,8 +16,14 @@
#define LOG_TAG "incidentd"
+#include "EncodedBuffer.h"
+#include "FdBuffer.h"
+#include "Privacy.h"
#include "Section.h"
+
+#include "io_util.h"
#include "protobuf.h"
+#include "section_list.h"
#include <private/android_filesystem_config.h>
#include <binder/IServiceManager.h>
@@ -66,6 +72,7 @@ forkAndExecuteIncidentHelper(const int id, const char* name, Fpipe& p2cPipe, Fpi
return pid;
}
+// ================================================================================
static status_t killChild(pid_t pid) {
int status;
kill(pid, SIGKILL);
@@ -87,22 +94,79 @@ static status_t waitForChild(pid_t pid) {
}
// ================================================================================
-Section::Section(int i, const int64_t timeoutMs)
- :id(i), timeoutMs(timeoutMs)
+static const Privacy*
+GetPrivacyOfSection(int id)
{
+ if (id < 0) return NULL;
+ int i=0;
+ while (PRIVACY_POLICY_LIST[i] != NULL) {
+ const Privacy* p = PRIVACY_POLICY_LIST[i];
+ if (p->field_id == (uint32_t)id) return p;
+ if (p->field_id > (uint32_t)id) return NULL;
+ i++;
+ }
+ return NULL;
}
-Section::~Section()
+static status_t
+WriteToRequest(const int id, const int fd, EncodedBuffer& buffer, const PrivacySpec& spec)
{
+ if (fd < 0) return EBADF;
+ 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;
}
-status_t
-Section::WriteHeader(ReportRequestSet* requests, size_t size) const
+static status_t
+WriteToReportRequests(const int id, const FdBuffer& buffer, ReportRequestSet* requests)
+{
+ status_t err = EBADF;
+ EncodedBuffer encodedBuffer(buffer, GetPrivacyOfSection(id));
+ int writeable = 0;
+
+ // The streaming ones
+ 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++;
+ }
+ }
+
+ // The dropbox file
+ if (requests->mainFd() >= 0) {
+ err = WriteToRequest(id, requests->mainFd(), encodedBuffer, get_default_dropbox_spec());
+ if (err != NO_ERROR) {
+ requests->setMainFd(-1);
+ } else {
+ writeable++;
+ }
+ }
+ // only returns error if there is no fd to write to.
+ return writeable > 0 ? NO_ERROR : err;
+}
+
+// ================================================================================
+Section::Section(int i, const int64_t timeoutMs)
+ :id(i), timeoutMs(timeoutMs)
+{
+}
+
+Section::~Section()
{
- 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);
}
// ================================================================================
@@ -113,7 +177,9 @@ FileSection::FileSection(int id, const char* filename, const int64_t timeoutMs)
FileSection::~FileSection() {}
-status_t FileSection::Execute(ReportRequestSet* requests) const {
+status_t
+FileSection::Execute(ReportRequestSet* requests) const
+{
// read from mFilename first, make sure the file is available
// add O_CLOEXEC to make sure it is closed when exec incident helper
int fd = open(mFilename, O_RDONLY | O_CLOEXEC);
@@ -155,8 +221,7 @@ status_t FileSection::Execute(ReportRequestSet* requests) const {
ALOGD("FileSection '%s' wrote %zd bytes in %d ms", this->name.string(), buffer.size(),
(int)buffer.durationMs());
- WriteHeader(requests, buffer.size());
- status_t err = buffer.write(requests);
+ status_t err = WriteToReportRequests(this->id, buffer, requests);
if (err != NO_ERROR) {
ALOGW("FileSection '%s' failed writing: %s", this->name.string(), strerror(-err));
return err;
@@ -313,8 +378,7 @@ WorkerThreadSection::Execute(ReportRequestSet* requests) const
// Write the data that was collected
ALOGD("WorkerThreadSection '%s' wrote %zd bytes in %d ms", name.string(), buffer.size(),
(int)buffer.durationMs());
- WriteHeader(requests, buffer.size());
- err = buffer.write(requests);
+ err = WriteToReportRequests(this->id, buffer, requests);
if (err != NO_ERROR) {
ALOGW("WorkerThreadSection '%s' failed writing: '%s'", this->name.string(), strerror(-err));
return err;
@@ -324,7 +388,8 @@ WorkerThreadSection::Execute(ReportRequestSet* requests) const
}
// ================================================================================
-void CommandSection::init(const char* command, va_list args)
+void
+CommandSection::init(const char* command, va_list args)
{
va_list copied_args;
int numOfArgs = 0;
@@ -429,8 +494,7 @@ CommandSection::Execute(ReportRequestSet* requests) const
ALOGD("CommandSection '%s' wrote %zd bytes in %d ms", this->name.string(), buffer.size(),
(int)buffer.durationMs());
- WriteHeader(requests, buffer.size());
- status_t err = buffer.write(requests);
+ status_t err = WriteToReportRequests(this->id, buffer, requests);
if (err != NO_ERROR) {
ALOGW("CommandSection '%s' failed writing: %s", this->name.string(), strerror(-err));
return err;
diff --git a/cmds/incidentd/src/Section.h b/cmds/incidentd/src/Section.h
index 93b4848f5bd8..9cfd696c17f0 100644
--- a/cmds/incidentd/src/Section.h
+++ b/cmds/incidentd/src/Section.h
@@ -17,7 +17,7 @@
#ifndef SECTIONS_H
#define SECTIONS_H
-#include "FdBuffer.h"
+#include "Reporter.h"
#include <stdarg.h>
#include <utils/String8.h>
@@ -42,8 +42,6 @@ public:
virtual ~Section();
virtual status_t Execute(ReportRequestSet* requests) const = 0;
-
- status_t WriteHeader(ReportRequestSet* requests, size_t size) const;
};
/**
diff --git a/cmds/incidentd/src/io_util.cpp b/cmds/incidentd/src/io_util.cpp
new file mode 100644
index 000000000000..f043d367d982
--- /dev/null
+++ b/cmds/incidentd/src/io_util.cpp
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "io_util.h"
+
+#include <unistd.h>
+
+status_t write_all(int fd, uint8_t const* buf, size_t size)
+{
+ while (size > 0) {
+ ssize_t amt = ::write(fd, buf, size);
+ if (amt < 0) {
+ return -errno;
+ }
+ size -= amt;
+ buf += amt;
+ }
+ return NO_ERROR;
+}
+
+Fpipe::Fpipe() {}
+
+Fpipe::~Fpipe() { close(); }
+
+bool Fpipe::close() { return !(::close(mFds[0]) || ::close(mFds[1])); }
+
+bool Fpipe::init() { return pipe(mFds) != -1; }
+
+int Fpipe::readFd() const { return mFds[0]; }
+
+int Fpipe::writeFd() const { return mFds[1]; }
diff --git a/cmds/incidentd/src/io_util.h b/cmds/incidentd/src/io_util.h
new file mode 100644
index 000000000000..320dd6c386d2
--- /dev/null
+++ b/cmds/incidentd/src/io_util.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef IO_UTIL_H
+#define IO_UTIL_H
+
+#include <stdint.h>
+#include <utils/Errors.h>
+
+using namespace android;
+
+status_t write_all(int fd, uint8_t const* buf, size_t size);
+
+class Fpipe {
+public:
+ Fpipe();
+ ~Fpipe();
+
+ bool init();
+ bool close();
+ int readFd() const;
+ int writeFd() const;
+
+private:
+ int mFds[2];
+};
+
+#endif // IO_UTIL_H \ No newline at end of file
diff --git a/cmds/incidentd/src/protobuf.cpp b/cmds/incidentd/src/protobuf.cpp
index b865339a9b98..05de8314deb4 100644
--- a/cmds/incidentd/src/protobuf.cpp
+++ b/cmds/incidentd/src/protobuf.cpp
@@ -16,8 +16,17 @@
#include "protobuf.h"
+uint8_t read_wire_type(uint32_t varint)
+{
+ return (uint8_t) (varint & 0x07);
+}
+
+uint32_t read_field_id(uint32_t varint)
+{
+ return varint >> 3;
+}
-uint8_t*
+uint8_t*
write_raw_varint(uint8_t* buf, uint32_t val)
{
uint8_t* p = buf;
@@ -32,7 +41,7 @@ write_raw_varint(uint8_t* buf, uint32_t val)
}
}
-uint8_t*
+uint8_t*
write_length_delimited_tag_header(uint8_t* buf, uint32_t fieldId, size_t size)
{
buf = write_raw_varint(buf, (fieldId << 3) | 2);
@@ -40,3 +49,24 @@ write_length_delimited_tag_header(uint8_t* buf, uint32_t fieldId, size_t size)
return buf;
}
+size_t
+write_raw_varint(vector<uint8_t> &buf, uint32_t val)
+{
+ size_t size = 0;
+ while (true) {
+ size++;
+ if ((val & ~0x7F) == 0) {
+ buf.push_back((uint8_t) val);
+ return size;
+ } else {
+ buf.push_back((uint8_t)((val & 0x7F) | 0x80));
+ val >>= 7;
+ }
+ }
+}
+
+size_t
+write_header(vector<uint8_t> &buf, uint32_t fieldId, uint8_t wireType)
+{
+ return write_raw_varint(buf, (fieldId << 3) | wireType);
+} \ No newline at end of file
diff --git a/cmds/incidentd/src/protobuf.h b/cmds/incidentd/src/protobuf.h
index f196ddc11967..fb0d69dbf755 100644
--- a/cmds/incidentd/src/protobuf.h
+++ b/cmds/incidentd/src/protobuf.h
@@ -18,6 +18,24 @@
#define PROTOBUF_H
#include <stdint.h>
+#include <vector>
+
+using namespace std;
+
+const uint8_t WIRE_TYPE_VARINT = 0;
+const uint8_t WIRE_TYPE_FIXED64 = 1;
+const uint8_t WIRE_TYPE_LENGTH_DELIMITED = 2;
+const uint8_t WIRE_TYPE_FIXED32 = 5;
+
+/**
+ * Read the wire type from varint, it is the smallest 3 bits.
+ */
+uint8_t read_wire_type(uint32_t varint);
+
+/**
+ * read field id from varint, it is varint >> 3;
+ */
+uint32_t read_field_id(uint32_t varint);
/**
* Write a varint into the buffer. Return the next position to write at.
@@ -32,6 +50,16 @@ uint8_t* write_raw_varint(uint8_t* buf, uint32_t val);
*/
uint8_t* write_length_delimited_tag_header(uint8_t* buf, uint32_t fieldId, size_t size);
+/**
+ * Write a varint into a vector. Return the size of the varint.
+ */
+size_t write_raw_varint(vector<uint8_t> &buf, uint32_t val);
+
+/**
+ * Write a protobuf header. Return the size of the header.
+ */
+size_t write_header(vector<uint8_t> &buf, uint32_t fieldId, uint8_t wireType);
+
enum {
// IncidentProto.header
FIELD_ID_INCIDENT_HEADER = 1
diff --git a/cmds/incidentd/src/section_list.h b/cmds/incidentd/src/section_list.h
index 1abdb5284001..4d9efd760dee 100644
--- a/cmds/incidentd/src/section_list.h
+++ b/cmds/incidentd/src/section_list.h
@@ -17,6 +17,7 @@
#ifndef SECTION_LIST_H
#define SECTION_LIST_H
+#include "Privacy.h"
#include "Section.h"
/**
@@ -25,37 +26,6 @@
*/
extern const Section* SECTION_LIST[];
-/*
- * In order not to use libprotobuf-cpp-full nor libplatformprotos in incidentd
- * privacy options's data structure are explicityly redefined in this file.
- */
-
-// DESTINATION enum
-extern const uint8_t DEST_LOCAL;
-extern const uint8_t DEST_EXPLICIT;
-extern const uint8_t DEST_AUTOMATIC;
-
-// This is the default value of DEST enum
-// field with this value doesn't generate Privacy to save too much generated code
-extern const uint8_t DEST_DEFAULT_VALUE;
-
-// type of the field, identitical to protobuf definition
-extern const uint8_t TYPE_STRING;
-extern const uint8_t TYPE_MESSAGE;
-
-struct Privacy {
- int field_id;
- uint8_t type;
-
- // the following two fields are identitical to
- // frameworks/base/libs/incident/proto/android/privacy.proto
- uint8_t dest;
- const char** patterns;
-
- // ignore parent's privacy flags if children are set, NULL-terminated
- const Privacy** children;
-};
-
/**
* This is the mapping of section IDs to each section's privacy policy.
* The section IDs are guaranteed in ascending order
diff --git a/cmds/incidentd/tests/EncodedBuffer_test.cpp b/cmds/incidentd/tests/EncodedBuffer_test.cpp
new file mode 100644
index 000000000000..c51520b36a28
--- /dev/null
+++ b/cmds/incidentd/tests/EncodedBuffer_test.cpp
@@ -0,0 +1,207 @@
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "EncodedBuffer.h"
+
+#include <android-base/file.h>
+#include <android-base/test_utils.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <string.h>
+
+using namespace android;
+using namespace android::base;
+using namespace std;
+using ::testing::StrEq;
+using ::testing::Test;
+using ::testing::internal::CaptureStdout;
+using ::testing::internal::GetCapturedStdout;
+
+const uint8_t LOCAL = 0;
+const uint8_t EXPLICIT = 1;
+const uint8_t AUTOMATIC = 2;
+
+const uint8_t OTHER_TYPE = 1;
+const uint8_t STRING_TYPE = 9;
+const uint8_t MESSAGE_TYPE = 11;
+const string STRING_FIELD_0 = "\x02\viamtestdata";
+const string VARINT_FIELD_1 = "\x08\x96\x01"; // 150
+const string STRING_FIELD_2 = "\x12\vwhatthefuck";
+const string FIX64_FIELD_3 = "\x19\xff\xff\xff\xff\xff\xff\xff\xff"; // -1
+const string FIX32_FIELD_4 = "\x25\xff\xff\xff\xff"; // -1
+const string MESSAGE_FIELD_5 = "\x2a\x10" + VARINT_FIELD_1 + STRING_FIELD_2;
+
+class EncodedBufferTest : public Test {
+public:
+ virtual void SetUp() override {
+ ASSERT_NE(tf.fd, -1);
+ }
+
+ void writeToFdBuffer(string str) {
+ ASSERT_TRUE(WriteStringToFile(str, tf.path, false));
+ ASSERT_EQ(NO_ERROR, buffer.read(tf.fd, 10000));
+ }
+
+ void assertBuffer(EncodedBuffer& buf, string expected) {
+ ASSERT_EQ(buf.size(), expected.size());
+ CaptureStdout();
+ ASSERT_EQ(buf.flush(STDOUT_FILENO), NO_ERROR);
+ ASSERT_THAT(GetCapturedStdout(), StrEq(expected));
+ }
+
+ void assertStrip(uint8_t dest, string expected, Privacy* policy) {
+ PrivacySpec spec(dest);
+ EncodedBuffer encodedBuf(buffer, policy);
+ ASSERT_EQ(encodedBuf.strip(spec), NO_ERROR);
+ assertBuffer(encodedBuf, expected);
+ }
+
+ void assertStripByFields(uint8_t dest, string expected, int size, Privacy* privacy, ...) {
+ Privacy* list[size+1];
+ list[0] = privacy;
+ va_list args;
+ va_start(args, privacy);
+ for (int i=1; i<size; i++) {
+ Privacy* p = va_arg(args, Privacy*);
+ list[i] = p;
+ }
+ va_end(args);
+ list[size] = NULL;
+ assertStrip(dest, expected, new Privacy(300, const_cast<const Privacy**>(list)));
+ }
+
+ FdBuffer buffer;
+private:
+ TemporaryFile tf;
+};
+
+TEST_F(EncodedBufferTest, NullFieldPolicy) {
+ writeToFdBuffer(STRING_FIELD_0);
+ assertStrip(EXPLICIT, STRING_FIELD_0, new Privacy(300, NULL));
+}
+
+TEST_F(EncodedBufferTest, StripSpecNotAllowed) {
+ writeToFdBuffer(STRING_FIELD_0);
+ assertStripByFields(AUTOMATIC, "", 1, new Privacy(0, STRING_TYPE, EXPLICIT));
+}
+
+TEST_F(EncodedBufferTest, StripVarintField) {
+ writeToFdBuffer(VARINT_FIELD_1);
+ assertStripByFields(EXPLICIT, "", 1, new Privacy(1, OTHER_TYPE, LOCAL));
+}
+
+TEST_F(EncodedBufferTest, StripLengthDelimitedField_String) {
+ writeToFdBuffer(STRING_FIELD_2);
+ assertStripByFields(EXPLICIT, "", 1, new Privacy(2, STRING_TYPE, LOCAL));
+}
+
+TEST_F(EncodedBufferTest, StripFixed64Field) {
+ writeToFdBuffer(FIX64_FIELD_3);
+ assertStripByFields(EXPLICIT, "", 1, new Privacy(3, OTHER_TYPE, LOCAL));
+}
+
+TEST_F(EncodedBufferTest, StripFixed32Field) {
+ writeToFdBuffer(FIX32_FIELD_4);
+ assertStripByFields(EXPLICIT, "", 1, new Privacy(4, OTHER_TYPE, LOCAL));
+}
+
+TEST_F(EncodedBufferTest, StripLengthDelimitedField_Message) {
+ writeToFdBuffer(MESSAGE_FIELD_5);
+ assertStripByFields(EXPLICIT, "", 1, new Privacy(5, MESSAGE_TYPE, LOCAL));
+}
+
+TEST_F(EncodedBufferTest, NoStripVarintField) {
+ writeToFdBuffer(VARINT_FIELD_1);
+ assertStripByFields(EXPLICIT, VARINT_FIELD_1, 1, new Privacy(1, OTHER_TYPE, AUTOMATIC));
+}
+
+TEST_F(EncodedBufferTest, NoStripLengthDelimitedField_String) {
+ writeToFdBuffer(STRING_FIELD_2);
+ assertStripByFields(EXPLICIT, STRING_FIELD_2, 1, new Privacy(2, STRING_TYPE, AUTOMATIC));
+}
+
+TEST_F(EncodedBufferTest, NoStripFixed64Field) {
+ writeToFdBuffer(FIX64_FIELD_3);
+ assertStripByFields(EXPLICIT, FIX64_FIELD_3, 1, new Privacy(3, OTHER_TYPE, AUTOMATIC));
+}
+
+TEST_F(EncodedBufferTest, NoStripFixed32Field) {
+ writeToFdBuffer(FIX32_FIELD_4);
+ assertStripByFields(EXPLICIT, FIX32_FIELD_4, 1, new Privacy(4, OTHER_TYPE, AUTOMATIC));
+}
+
+TEST_F(EncodedBufferTest, NoStripLengthDelimitedField_Message) {
+ writeToFdBuffer(MESSAGE_FIELD_5);
+ assertStripByFields(EXPLICIT, MESSAGE_FIELD_5, 1, new Privacy(5, MESSAGE_TYPE, AUTOMATIC));
+}
+
+TEST_F(EncodedBufferTest, StripVarintAndString) {
+ writeToFdBuffer(STRING_FIELD_0 + VARINT_FIELD_1 + STRING_FIELD_2
+ + FIX64_FIELD_3 + FIX32_FIELD_4);
+ string expected = STRING_FIELD_0 + FIX64_FIELD_3 + FIX32_FIELD_4;
+ assertStripByFields(EXPLICIT, expected, 2,
+ new Privacy(1, OTHER_TYPE, LOCAL), new Privacy(2, STRING_TYPE, LOCAL));
+}
+
+TEST_F(EncodedBufferTest, StripVarintAndFixed64) {
+ writeToFdBuffer(STRING_FIELD_0 + VARINT_FIELD_1 + STRING_FIELD_2
+ + FIX64_FIELD_3 + FIX32_FIELD_4);
+ string expected = STRING_FIELD_0 + STRING_FIELD_2 + FIX32_FIELD_4;
+ assertStripByFields(EXPLICIT, expected, 2,
+ new Privacy(1, OTHER_TYPE, LOCAL), new Privacy(3, OTHER_TYPE, LOCAL));
+}
+
+TEST_F(EncodedBufferTest, StripVarintInNestedMessage) {
+ writeToFdBuffer(STRING_FIELD_0 + MESSAGE_FIELD_5);
+ const Privacy* list[] = { new Privacy(1, OTHER_TYPE, LOCAL), NULL };
+ string expected = STRING_FIELD_0 + "\x2a\xd" + STRING_FIELD_2;
+ assertStripByFields(EXPLICIT, expected, 1, new Privacy(5, list));
+}
+
+TEST_F(EncodedBufferTest, StripFix64AndVarintInNestedMessage) {
+ writeToFdBuffer(STRING_FIELD_0 + FIX64_FIELD_3 + MESSAGE_FIELD_5);
+ const Privacy* list[] = { new Privacy(1, OTHER_TYPE, LOCAL), NULL };
+ string expected = STRING_FIELD_0 + "\x2a\xd" + STRING_FIELD_2;
+ assertStripByFields(EXPLICIT, expected, 2, new Privacy(3, OTHER_TYPE, LOCAL), new Privacy(5, list));
+}
+
+TEST_F(EncodedBufferTest, ClearAndStrip) {
+ string data = STRING_FIELD_0 + VARINT_FIELD_1;
+ writeToFdBuffer(data);
+ const Privacy* list[] = { new Privacy(1, OTHER_TYPE, LOCAL), NULL };
+ EncodedBuffer encodedBuf(buffer, new Privacy(300, list));
+ PrivacySpec spec1(EXPLICIT), spec2(LOCAL);
+
+ ASSERT_EQ(encodedBuf.strip(spec1), NO_ERROR);
+ assertBuffer(encodedBuf, STRING_FIELD_0);
+ ASSERT_EQ(encodedBuf.strip(spec2), NO_ERROR);
+ assertBuffer(encodedBuf, data);
+}
+
+TEST_F(EncodedBufferTest, BadDataInFdBuffer) {
+ writeToFdBuffer("iambaddata");
+ const Privacy* list[] = { new Privacy(4, OTHER_TYPE, AUTOMATIC), NULL };
+ EncodedBuffer encodedBuf(buffer, new Privacy(300, list));
+ PrivacySpec spec;
+ ASSERT_EQ(encodedBuf.strip(spec), BAD_VALUE);
+}
+
+TEST_F(EncodedBufferTest, BadDataInNestedMessage) {
+ writeToFdBuffer(STRING_FIELD_0 + MESSAGE_FIELD_5 + "aoeoe");
+ const Privacy* list[] = { new Privacy(1, OTHER_TYPE, LOCAL), NULL };
+ const Privacy* field5[] = { new Privacy(5, list), NULL };
+ EncodedBuffer encodedBuf(buffer, new Privacy(300, field5));
+ PrivacySpec spec;
+ ASSERT_EQ(encodedBuf.strip(spec), BAD_VALUE);
+}
diff --git a/cmds/incidentd/tests/FdBuffer_test.cpp b/cmds/incidentd/tests/FdBuffer_test.cpp
index 403a2abf670a..d1436b2cc36f 100644
--- a/cmds/incidentd/tests/FdBuffer_test.cpp
+++ b/cmds/incidentd/tests/FdBuffer_test.cpp
@@ -15,10 +15,11 @@
#define LOG_TAG "incidentd"
#include "FdBuffer.h"
+#include "io_util.h"
#include <android-base/file.h>
#include <android-base/test_utils.h>
-#include <gmock/gmock.h>
+#include <fcntl.h>
#include <gtest/gtest.h>
#include <signal.h>
#include <string.h>
@@ -30,10 +31,7 @@ const std::string HEAD = "[OK]";
using namespace android;
using namespace android::base;
-using ::testing::StrEq;
using ::testing::Test;
-using ::testing::internal::CaptureStdout;
-using ::testing::internal::GetCapturedStdout;
class FdBufferTest : public Test {
public:
@@ -50,12 +48,13 @@ public:
}
void AssertBufferContent(const char* expected) {
- ReportRequestSet requests;
- requests.setMainFd(STDOUT_FILENO);
-
- CaptureStdout();
- ASSERT_EQ(NO_ERROR, buffer.write(&requests));
- EXPECT_THAT(GetCapturedStdout(), StrEq(expected));
+ int i=0;
+ FdBuffer::iterator it = buffer.begin();
+ while (expected[i] != '\0') {
+ ASSERT_EQ(*it, expected[i++]);
+ it++;
+ }
+ ASSERT_EQ(it, buffer.end());
}
bool DoDataStream(int rFd, int wFd) {
@@ -99,6 +98,16 @@ TEST_F(FdBufferTest, IterateEmpty) {
EXPECT_TRUE(it.outOfBound());
}
+TEST_F(FdBufferTest, IteratorSnapshot) {
+ FdBuffer::iterator it = buffer.begin();
+ it += 4;
+ FdBuffer::iterator snapshot = it.snapshot();
+ it += 5;
+ EXPECT_TRUE(snapshot != it);
+ EXPECT_EQ(it - snapshot, 5);
+ EXPECT_EQ(snapshot - it, -5);
+}
+
TEST_F(FdBufferTest, ReadAndIterate) {
std::string testdata = "FdBuffer test string";
ASSERT_TRUE(WriteStringToFile(testdata, tf.path, false));
@@ -227,7 +236,7 @@ TEST_F(FdBufferTest, ReadInStreamEmpty) {
TEST_F(FdBufferTest, ReadInStreamMoreThan4MB) {
const std::string testFile = kTestDataPath + "morethan4MB.txt";
size_t fourMB = (size_t) 4 * 1024 * 1024;
- int fd = open(testFile.c_str(), O_RDONLY);
+ int fd = open(testFile.c_str(), O_RDONLY | O_CLOEXEC);
ASSERT_NE(fd, -1);
int pid = fork();
ASSERT_TRUE(pid != -1);
diff --git a/cmds/incidentd/tests/Reporter_test.cpp b/cmds/incidentd/tests/Reporter_test.cpp
index a77474199d27..3c1b44b6a515 100644
--- a/cmds/incidentd/tests/Reporter_test.cpp
+++ b/cmds/incidentd/tests/Reporter_test.cpp
@@ -127,29 +127,7 @@ TEST_F(ReporterTest, IncidentReportArgs) {
TEST_F(ReporterTest, ReportRequestSetEmpty) {
requests.setMainFd(STDOUT_FILENO);
-
- CaptureStdout();
- requests.write((uint8_t *) "abcdef", 6);
- EXPECT_THAT(GetCapturedStdout(), StrEq("abcdef"));
-}
-
-TEST_F(ReporterTest, WriteToStreamFdAndMainFd) {
- TemporaryFile tf;
- IncidentReportArgs args;
- sp<ReportRequest> r = new ReportRequest(args, l, tf.fd);
-
- requests.add(r);
- requests.setMainFd(STDOUT_FILENO);
-
- const char* data = "abcdef";
-
- CaptureStdout();
- requests.write((uint8_t *) data, 6);
- EXPECT_THAT(GetCapturedStdout(), StrEq(data));
-
- string content;
- ASSERT_TRUE(ReadFileToString(tf.path, &content));
- EXPECT_THAT(content, StrEq(data));
+ ASSERT_EQ(requests.mainFd(), STDOUT_FILENO);
}
TEST_F(ReporterTest, RunReportEmpty) {
diff --git a/cmds/incidentd/tests/Section_test.cpp b/cmds/incidentd/tests/Section_test.cpp
index 93771ff30b64..ab0f05498232 100644
--- a/cmds/incidentd/tests/Section_test.cpp
+++ b/cmds/incidentd/tests/Section_test.cpp
@@ -22,8 +22,16 @@
#include <gtest/gtest.h>
#include <string.h>
+const int TIMEOUT_PARSER = -1;
+const int NOOP_PARSER = 0;
+const int REVERSE_PARSER = 1;
+
const int QUICK_TIMEOUT_MS = 100;
+const string VARINT_FIELD_1 = "\x08\x96\x01"; // 150
+const string STRING_FIELD_2 = "\x12\vwhatthefuck";
+const string FIX64_FIELD_3 = "\x19\xff\xff\xff\xff\xff\xff\xff\xff"; // -1
+
using namespace android::base;
using namespace std;
using ::testing::StrEq;
@@ -31,22 +39,9 @@ using ::testing::internal::CaptureStdout;
using ::testing::internal::GetCapturedStdout;
// NOTICE: this test requires /system/bin/incident_helper is installed.
-TEST(SectionTest, WriteHeader) {
- int id = 13; // expect output is 13 << 3 & 2 = 106 --> \x6a in ASCII
- FileSection s(id, ""); // ignore the path, just used to test the header
- ReportRequestSet requests;
-
- requests.setMainFd(STDOUT_FILENO);
-
- CaptureStdout();
- ASSERT_EQ(NO_ERROR, s.WriteHeader(&requests, 300));
- // According to protobuf encoding, 300 is "1010 1100 0000 0010" -> \xac \x02
- EXPECT_THAT(GetCapturedStdout(), StrEq("\x6a\xac\x02"));
-}
-
TEST(SectionTest, FileSection) {
TemporaryFile tf;
- FileSection fs(0, tf.path);
+ FileSection fs(REVERSE_PARSER, tf.path);
ReportRequestSet requests;
ASSERT_TRUE(tf.fd != -1);
@@ -58,13 +53,13 @@ TEST(SectionTest, FileSection) {
ASSERT_EQ(NO_ERROR, fs.Execute(&requests));
// The input string is reversed in incident helper
// The length is 11, in 128Varint it is "0000 1011" -> \v
- EXPECT_THAT(GetCapturedStdout(), StrEq("\x02\vatadtsetmai"));
+ EXPECT_THAT(GetCapturedStdout(), StrEq("\xa\vatadtsetmai"));
}
TEST(SectionTest, FileSectionTimeout) {
TemporaryFile tf;
// id -1 is timeout parser
- FileSection fs(-1, tf.path, QUICK_TIMEOUT_MS);
+ FileSection fs(TIMEOUT_PARSER, tf.path, QUICK_TIMEOUT_MS);
ReportRequestSet requests;
ASSERT_EQ(NO_ERROR, fs.Execute(&requests));
}
@@ -84,36 +79,51 @@ TEST(SectionTest, CommandSectionConstructor) {
}
TEST(SectionTest, CommandSectionEcho) {
- CommandSection cs(0, "/system/bin/echo", "about", NULL);
+ CommandSection cs(REVERSE_PARSER, "/system/bin/echo", "about", NULL);
ReportRequestSet requests;
requests.setMainFd(STDOUT_FILENO);
CaptureStdout();
ASSERT_EQ(NO_ERROR, cs.Execute(&requests));
- EXPECT_THAT(GetCapturedStdout(), StrEq("\x02\x06\ntuoba"));
+ EXPECT_THAT(GetCapturedStdout(), StrEq("\xa\x06\ntuoba"));
}
TEST(SectionTest, CommandSectionCommandTimeout) {
- CommandSection cs(0, QUICK_TIMEOUT_MS, "/system/bin/yes", NULL);
+ CommandSection cs(NOOP_PARSER, QUICK_TIMEOUT_MS, "/system/bin/yes", NULL);
ReportRequestSet requests;
ASSERT_EQ(NO_ERROR, cs.Execute(&requests));
}
TEST(SectionTest, CommandSectionIncidentHelperTimeout) {
- CommandSection cs(-1, QUICK_TIMEOUT_MS, "/system/bin/echo", "about", NULL);
+ CommandSection cs(TIMEOUT_PARSER, QUICK_TIMEOUT_MS, "/system/bin/echo", "about", NULL);
ReportRequestSet requests;
requests.setMainFd(STDOUT_FILENO);
ASSERT_EQ(NO_ERROR, cs.Execute(&requests));
}
TEST(SectionTest, CommandSectionBadCommand) {
- CommandSection cs(0, "echo", "about", NULL);
+ CommandSection cs(NOOP_PARSER, "echo", "about", NULL);
ReportRequestSet requests;
ASSERT_EQ(NAME_NOT_FOUND, cs.Execute(&requests));
}
TEST(SectionTest, CommandSectionBadCommandAndTimeout) {
- CommandSection cs(-1, QUICK_TIMEOUT_MS, "nonexistcommand", "-opt", NULL);
+ CommandSection cs(TIMEOUT_PARSER, QUICK_TIMEOUT_MS, "nonexistcommand", "-opt", NULL);
ReportRequestSet requests;
// timeout will return first
ASSERT_EQ(NO_ERROR, cs.Execute(&requests));
+}
+
+TEST(SectionTest, TestFilterPiiTaggedFields) {
+ TemporaryFile tf;
+ FileSection fs(NOOP_PARSER, tf.path);
+ ReportRequestSet requests;
+
+ ASSERT_TRUE(tf.fd != -1);
+ ASSERT_TRUE(WriteStringToFile(VARINT_FIELD_1 + STRING_FIELD_2 + FIX64_FIELD_3, tf.path, false));
+
+ requests.setMainFd(STDOUT_FILENO);
+
+ CaptureStdout();
+ ASSERT_EQ(NO_ERROR, fs.Execute(&requests));
+ EXPECT_THAT(GetCapturedStdout(), StrEq("\x02\r" + STRING_FIELD_2));
} \ No newline at end of file
diff --git a/cmds/incidentd/tests/section_list.cpp b/cmds/incidentd/tests/section_list.cpp
index f0053355bd24..3722c7244ed7 100644
--- a/cmds/incidentd/tests/section_list.cpp
+++ b/cmds/incidentd/tests/section_list.cpp
@@ -4,3 +4,18 @@
const Section* SECTION_LIST[] = {
NULL
};
+
+const uint8_t LOCAL = 0;
+const uint8_t EXPLICIT = 1;
+const uint8_t AUTOMATIC = 2;
+
+const Privacy* list[] = {
+ new Privacy(1, 1, LOCAL),
+ new Privacy(2, AUTOMATIC, (const char**)NULL),
+ NULL };
+
+const Privacy* PRIVACY_POLICY_LIST[] = {
+ new Privacy(0, list),
+ new Privacy(1, 9, AUTOMATIC),
+ NULL
+}; \ No newline at end of file
diff --git a/tools/incident_section_gen/main.cpp b/tools/incident_section_gen/main.cpp
index 23aafb2626b8..7966d886d5a0 100644
--- a/tools/incident_section_gen/main.cpp
+++ b/tools/incident_section_gen/main.cpp
@@ -135,27 +135,24 @@ static bool generatePrivacyFlags(const Descriptor* descriptor, const char* alias
if (generatePrivacyFlags(field->message_type(), field_name, msgNames) &&
isDefaultDest(field)) break;
- printf("static Privacy %s = { %d, %d, %d, NULL, %s_LIST };\n", field_name, field->number(),
- (int) field->type(), p.dest(), field_name);
+ printf("Privacy %s(%d, %s_LIST);\n", field_name, field->number(), field_name);
hasDefaultFlags[i] = false;
break;
case FieldDescriptor::TYPE_STRING:
if (isDefaultDest(field) && p.patterns_size() == 0) break;
- printf("static const char* %s_patterns[] = {\n", field_name);
+ printf("const char* %s_patterns[] = {\n", field_name);
for (int i=0; i<p.patterns_size(); i++) {
// the generated string need to escape backslash as well, need to dup it here
printf(" \"%s\",\n", replaceAll(p.patterns(i), '\\', "\\\\").c_str());
}
printf(" NULL };\n");
- printf("static Privacy %s = { %d, %d, %d, %s_patterns };\n", field_name, field->number(),
- (int) field->type(), p.dest(), field_name);
+ printf("Privacy %s(%d, %d, %s_patterns);\n", field_name, field->number(), p.dest(), field_name);
hasDefaultFlags[i] = false;
break;
default:
if (isDefaultDest(field)) break;
- printf("static Privacy %s = { %d, %d, %d };\n", field_name, field->number(),
- (int) field->type(), p.dest());
+ printf("Privacy %s(%d, %d, %d);\n", field_name, field->number(), (int) field->type(), p.dest());
hasDefaultFlags[i] = false;
}
// add the field name to message map, true means it has default flags
@@ -213,20 +210,6 @@ static bool generateSectionListCpp(Descriptor const* descriptor) {
printf(" NULL };\n");
emptyline();
- // generates DESTINATION enum values
- EnumDescriptor const* destination = Destination_descriptor();
- for (int i=0; i<destination->value_count(); i++) {
- EnumValueDescriptor const* val = destination->value(i);
- printf("const uint8_t %s = %d;\n", val->name().c_str(), val->number());
- }
- emptyline();
- printf("const uint8_t DEST_DEFAULT_VALUE = %d;\n", PrivacyFlags::default_instance().dest());
- emptyline();
- // populates string type and message type values
- printf("const uint8_t TYPE_STRING = %d;\n", (int) FieldDescriptor::TYPE_STRING);
- printf("const uint8_t TYPE_MESSAGE = %d;\n", (int) FieldDescriptor::TYPE_MESSAGE);
- emptyline();
-
// generates PRIVACY_POLICY
map<string, bool> messageNames;
if (generatePrivacyFlags(descriptor, "PRIVACY_POLICY", messageNames)) {