Implement c++ native lib for streaming proto, part 1
Extract protobuf class out and creates EncodedBuffer class
which holds protobuf data.
Next step is to create a ProtoOutputStream and let incident helper
adapt the change as well.
please see frameworks/base/core/java/android/util/proto
Bug: 65641021
Test: unit tested
Change-Id: I0dd343b2e62d60f091c8f857fae3452ec8da6b96
diff --git a/cmds/incidentd/Android.mk b/cmds/incidentd/Android.mk
index 830bf9e..cb5fd02 100644
--- a/cmds/incidentd/Android.mk
+++ b/cmds/incidentd/Android.mk
@@ -23,7 +23,7 @@
LOCAL_MODULE := incidentd
LOCAL_SRC_FILES := \
- src/EncodedBuffer.cpp \
+ src/PrivacyBuffer.cpp \
src/FdBuffer.cpp \
src/IncidentService.cpp \
src/Privacy.cpp \
@@ -31,7 +31,6 @@
src/Section.cpp \
src/io_util.cpp \
src/main.cpp \
- src/protobuf.cpp \
src/report_directory.cpp
LOCAL_CFLAGS += \
@@ -54,6 +53,7 @@
libcutils \
libincident \
liblog \
+ libprotoutil \
libselinux \
libservices \
libutils
@@ -93,16 +93,15 @@
LOCAL_C_INCLUDES += $(LOCAL_PATH)/src
LOCAL_SRC_FILES := \
- src/EncodedBuffer.cpp \
+ src/PrivacyBuffer.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/PrivacyBuffer_test.cpp \
tests/FdBuffer_test.cpp \
tests/Reporter_test.cpp \
tests/Section_test.cpp \
@@ -116,6 +115,7 @@
libcutils \
libincident \
liblog \
+ libprotoutil \
libselinux \
libservices \
libutils \
diff --git a/cmds/incidentd/src/EncodedBuffer.cpp b/cmds/incidentd/src/EncodedBuffer.cpp
deleted file mode 100644
index e8f2c11..0000000
--- a/cmds/incidentd/src/EncodedBuffer.cpp
+++ /dev/null
@@ -1,195 +0,0 @@
-/*
- * 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* iter, vector<uint8_t>* buf, uint8_t wireType, bool skip)
-{
- FdBuffer::iterator snapshot = iter->snapshot();
- size_t bytesToWrite = 0;
- uint32_t varint = 0;
- switch (wireType) {
- case WIRE_TYPE_VARINT:
- varint = read_raw_varint(iter);
- 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(iter);
- if(!skip) write_raw_varint(buf, bytesToWrite);
- break;
- case WIRE_TYPE_FIXED32:
- bytesToWrite = 4;
- break;
- }
- if (skip) {
- *iter += bytesToWrite;
- } else {
- for (size_t i=0; i<bytesToWrite; i++) {
- buf->push_back(**iter);
- (*iter)++;
- }
- }
- return skip ? 0 : *iter - 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* iter, vector<uint8_t>* buf, const Privacy* parentPolicy, const PrivacySpec& spec)
-{
- if (iter->outOfBound() || parentPolicy == NULL) return BAD_VALUE;
-
- uint32_t varint = read_raw_varint(iter);
- 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(iter, 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(iter);
- size_t finalSize = 0;
- FdBuffer::iterator start = iter->snapshot();
- while ((*iter - start) != (int)msgSize) {
- vector<uint8_t> v;
- status_t err = stripField(iter, &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); // reserve the size of the field
- while (!q.empty()) {
- vector<uint8_t> subField = q.front();
- for (vector<uint8_t>::iterator it = subField.begin(); it != subField.end(); it++) {
- buf->push_back(*it);
- }
- q.pop_front();
- }
- return NO_ERROR;
-}
-
-// ================================================================================
-EncodedBuffer::EncodedBuffer(const FdBuffer& buffer, const Privacy* policy)
- : mFdBuffer(buffer),
- mPolicy(policy),
- mBuffers(),
- mSize(0)
-{
-}
-
-EncodedBuffer::~EncodedBuffer()
-{
-}
-
-status_t
-EncodedBuffer::strip(const PrivacySpec& spec)
-{
- // optimization when no strip happens
- if (mPolicy == NULL || !mPolicy->HasChildren() || spec.RequireAll()) {
- if (spec.CheckPremission(mPolicy)) mSize = mFdBuffer.size();
- return NO_ERROR;
- }
-
- FdBuffer::iterator it = mFdBuffer.begin();
- vector<uint8_t> field;
- field.reserve(BUFFER_SIZE);
-
- while (it != mFdBuffer.end()) {
- status_t err = stripField(&it, &field, mPolicy, spec);
- if (err != NO_ERROR) return err;
- if (field.size() > BUFFER_SIZE) { // rotate to another chunk if buffer size exceeds
- mBuffers.push_back(field);
- mSize += field.size();
- field.clear();
- }
- }
- if (!field.empty()) {
- mBuffers.push_back(field);
- mSize += field.size();
- }
- return NO_ERROR;
-}
-
-void
-EncodedBuffer::clear()
-{
- mSize = 0;
- mBuffers.clear();
-}
-
-size_t
-EncodedBuffer::size() const { return mSize; }
-
-status_t
-EncodedBuffer::flush(int fd)
-{
- if (size() == mFdBuffer.size()) return mFdBuffer.flush(fd);
-
- for (vector<vector<uint8_t>>::iterator it = mBuffers.begin(); it != mBuffers.end(); it++) {
- status_t err = write_all(fd, it->data(), it->size());
- if (err != NO_ERROR) return err;
- }
- return NO_ERROR;
-}
-
diff --git a/cmds/incidentd/src/FdBuffer.cpp b/cmds/incidentd/src/FdBuffer.cpp
index bb399b5..b7633a4 100644
--- a/cmds/incidentd/src/FdBuffer.cpp
+++ b/cmds/incidentd/src/FdBuffer.cpp
@@ -17,7 +17,6 @@
#define LOG_TAG "incidentd"
#include "FdBuffer.h"
-#include "io_util.h"
#include <cutils/log.h>
#include <utils/SystemClock.h>
@@ -31,10 +30,9 @@
const ssize_t MAX_BUFFER_COUNT = 256; // 4 MB max
FdBuffer::FdBuffer()
- :mBuffers(),
+ :mBuffer(BUFFER_SIZE),
mStartTime(-1),
mFinishTime(-1),
- mCurrentWritten(-1),
mTimedOut(false),
mTruncated(false)
{
@@ -42,11 +40,6 @@
FdBuffer::~FdBuffer()
{
- const int N = mBuffers.size();
- for (int i=0; i<N; i++) {
- uint8_t* buf = mBuffers[i];
- free(buf);
- }
}
status_t
@@ -60,20 +53,12 @@
fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK);
- uint8_t* buf = NULL;
while (true) {
- if (mCurrentWritten >= BUFFER_SIZE || mCurrentWritten < 0) {
- if (mBuffers.size() == MAX_BUFFER_COUNT) {
- mTruncated = true;
- break;
- }
- buf = (uint8_t*)malloc(BUFFER_SIZE);
- if (buf == NULL) {
- return NO_MEMORY;
- }
- mBuffers.push_back(buf);
- mCurrentWritten = 0;
+ if (mBuffer.size() >= MAX_BUFFER_COUNT * BUFFER_SIZE) {
+ mTruncated = true;
+ break;
}
+ if (mBuffer.writeBuffer() == NULL) return NO_MEMORY;
int64_t remainingTime = (mStartTime + timeout) - uptimeMillis();
if (remainingTime <= 0) {
@@ -91,7 +76,7 @@
if ((pfds.revents & POLLERR) != 0) {
return errno != 0 ? -errno : UNKNOWN_ERROR;
} else {
- ssize_t amt = ::read(fd, buf + mCurrentWritten, BUFFER_SIZE - mCurrentWritten);
+ ssize_t amt = ::read(fd, mBuffer.writeBuffer(), mBuffer.currentToWrite());
if (amt < 0) {
if (errno == EAGAIN || errno == EWOULDBLOCK) {
continue;
@@ -101,11 +86,10 @@
} else if (amt == 0) {
break;
}
- mCurrentWritten += amt;
+ mBuffer.wp()->move(amt);
}
}
}
-
mFinishTime = uptimeMillis();
return NO_ERROR;
}
@@ -132,20 +116,12 @@
int rpos = 0, wpos = 0;
// This is the buffer used to store processed data
- uint8_t* buf = NULL;
while (true) {
- if (mCurrentWritten >= BUFFER_SIZE || mCurrentWritten < 0) {
- if (mBuffers.size() == MAX_BUFFER_COUNT) {
- mTruncated = true;
- break;
- }
- buf = (uint8_t*)malloc(BUFFER_SIZE);
- if (buf == NULL) {
- return NO_MEMORY;
- }
- mBuffers.push_back(buf);
- mCurrentWritten = 0;
+ if (mBuffer.size() >= MAX_BUFFER_COUNT * BUFFER_SIZE) {
+ mTruncated = true;
+ break;
}
+ if (mBuffer.writeBuffer() == NULL) return NO_MEMORY;
int64_t remainingTime = (mStartTime + timeoutMs) - uptimeMillis();
if (remainingTime <= 0) {
@@ -223,7 +199,7 @@
}
// read from parsing process
- ssize_t amt = ::read(fromFd, buf + mCurrentWritten, BUFFER_SIZE - mCurrentWritten);
+ ssize_t amt = ::read(fromFd, mBuffer.writeBuffer(), mBuffer.currentToWrite());
if (amt < 0) {
if (!(errno == EAGAIN || errno == EWOULDBLOCK)) {
return -errno;
@@ -231,7 +207,7 @@
} else if (amt == 0) {
break;
} else {
- mCurrentWritten += amt;
+ mBuffer.wp()->move(amt);
}
}
@@ -242,105 +218,11 @@
size_t
FdBuffer::size() const
{
- if (mBuffers.empty()) return 0;
- return ((mBuffers.size() - 1) * BUFFER_SIZE) + mCurrentWritten;
+ return mBuffer.size();
}
-status_t
-FdBuffer::flush(int fd) const
+EncodedBuffer::iterator
+FdBuffer::data() const
{
- 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;
- }
- return write_all(fd, mBuffers[i], mCurrentWritten);
-}
-
-FdBuffer::iterator
-FdBuffer::begin() const
-{
- return iterator(*this, 0, 0);
-}
-
-FdBuffer::iterator
-FdBuffer::end() const
-{
- if (mBuffers.empty() || mCurrentWritten < 0) return begin();
- if (mCurrentWritten == BUFFER_SIZE)
- // FdBuffer doesn't allocate another buf since no more bytes to read.
- return FdBuffer::iterator(*this, mBuffers.size(), 0);
- 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)
-{
- size_t newOffset = mOffset + offset;
- while (newOffset >= BUFFER_SIZE) {
- mIndex++;
- newOffset -= BUFFER_SIZE;
- }
- mOffset = newOffset;
- 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();
+ return mBuffer.begin();
}
diff --git a/cmds/incidentd/src/FdBuffer.h b/cmds/incidentd/src/FdBuffer.h
index dfe39c6..8857ae7 100644
--- a/cmds/incidentd/src/FdBuffer.h
+++ b/cmds/incidentd/src/FdBuffer.h
@@ -17,11 +17,11 @@
#ifndef FD_BUFFER_H
#define FD_BUFFER_H
+#include <android/util/EncodedBuffer.h>
#include <utils/Errors.h>
-#include <vector>
-
using namespace android;
+using namespace android::util;
using namespace std;
/**
@@ -71,52 +71,19 @@
size_t size() const;
/**
- * Flush all the data to given file descriptor;
- */
- status_t flush(int fd) const;
-
- /**
* How long the read took in milliseconds.
*/
int64_t durationMs() const { return mFinishTime - mStartTime; }
/**
- * Read data stored in FdBuffer
+ * Reader API for data stored in FdBuffer
*/
- class iterator;
- friend class iterator;
- class iterator : public std::iterator<std::random_access_iterator_tag, uint8_t> {
- public:
- iterator(const FdBuffer& buffer, ssize_t index, ssize_t offset);
- iterator& operator=(iterator& other) const;
- iterator& operator+(size_t offset);
- iterator& operator+=(size_t offset);
- iterator& operator++();
- iterator operator++(int);
- bool operator==(iterator other) const;
- bool operator!=(iterator other) const;
- int operator-(iterator other) const;
- reference operator*() const;
-
- // return the snapshot of the current iterator
- iterator snapshot() const;
- // how many bytes are read
- size_t bytesRead() const;
- // random access could make the iterator out of bound
- bool outOfBound() const;
- private:
- const FdBuffer& mFdBuffer;
- size_t mIndex;
- size_t mOffset;
- };
- iterator begin() const;
- iterator end() const;
+ EncodedBuffer::iterator data() const;
private:
- vector<uint8_t*> mBuffers;
+ EncodedBuffer mBuffer;
int64_t mStartTime;
int64_t mFinishTime;
- ssize_t mCurrentWritten;
bool mTimedOut;
bool mTruncated;
};
diff --git a/cmds/incidentd/src/PrivacyBuffer.cpp b/cmds/incidentd/src/PrivacyBuffer.cpp
new file mode 100644
index 0000000..07a064cf
--- /dev/null
+++ b/cmds/incidentd/src/PrivacyBuffer.cpp
@@ -0,0 +1,168 @@
+/*
+ * 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 "PrivacyBuffer.h"
+#include "io_util.h"
+
+#include <android/util/protobuf.h>
+#include <deque>
+
+using namespace android::util;
+
+/**
+ * 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(EncodedBuffer::iterator* iter, EncodedBuffer* buf, uint8_t wireType, bool skip)
+{
+ EncodedBuffer::Pointer snapshot = iter->rp()->copy();
+ size_t bytesToWrite = 0;
+ uint32_t varint = 0;
+ switch (wireType) {
+ case WIRE_TYPE_VARINT:
+ varint = iter->readRawVarint();
+ if(!skip) return buf->writeRawVarint(varint);
+ break;
+ case WIRE_TYPE_FIXED64:
+ bytesToWrite = 8;
+ break;
+ case WIRE_TYPE_LENGTH_DELIMITED:
+ bytesToWrite = iter->readRawVarint();
+ if(!skip) buf->writeRawVarint(bytesToWrite);
+ break;
+ case WIRE_TYPE_FIXED32:
+ bytesToWrite = 4;
+ break;
+ }
+ if (skip) {
+ iter->rp()->move(bytesToWrite);
+ } else {
+ for (size_t i=0; i<bytesToWrite; i++) {
+ *buf->writeBuffer() = iter->next();
+ buf->wp()->move();
+ }
+ }
+ return skip ? 0 : iter->rp()->pos() - snapshot.pos();
+}
+
+/**
+ * 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(EncodedBuffer::iterator* iter, EncodedBuffer* buf, const Privacy* parentPolicy, const PrivacySpec& spec)
+{
+ if (!iter->hasNext() || parentPolicy == NULL) return BAD_VALUE;
+ uint32_t varint = iter->readRawVarint();
+ 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 += buf->writeHeader(fieldId, wireType);
+ amt += write_field_or_skip(iter, 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<EncodedBuffer*> q;
+ uint32_t msgSize = iter->readRawVarint();
+ size_t finalSize = 0;
+ EncodedBuffer::Pointer start = iter->rp()->copy();
+ while (iter->rp()->pos() - start.pos() != msgSize) {
+ EncodedBuffer* v = new EncodedBuffer();
+ status_t err = stripField(iter, v, policy, spec);
+ if (err != NO_ERROR) return err;
+ if (v->size() == 0) continue;
+ q.push_back(v);
+ finalSize += v->size();
+ }
+
+ buf->writeHeader(fieldId, wireType);
+ buf->writeRawVarint(finalSize);
+ while (!q.empty()) {
+ EncodedBuffer* subField = q.front();
+ EncodedBuffer::iterator it = subField->begin();
+ while (it.hasNext()) {
+ *buf->writeBuffer() = it.next();
+ buf->wp()->move();
+ }
+ q.pop_front();
+ delete subField;
+ }
+ return NO_ERROR;
+}
+
+// ================================================================================
+PrivacyBuffer::PrivacyBuffer(const Privacy* policy, EncodedBuffer::iterator& data)
+ :mPolicy(policy),
+ mData(data),
+ mBuffer(0),
+ mSize(0)
+{
+}
+
+PrivacyBuffer::~PrivacyBuffer()
+{
+}
+
+status_t
+PrivacyBuffer::strip(const PrivacySpec& spec)
+{
+ // optimization when no strip happens
+ if (mPolicy == NULL || !mPolicy->HasChildren() || spec.RequireAll()) {
+ if (spec.CheckPremission(mPolicy)) mSize = mData.size();
+ return NO_ERROR;
+ }
+ while (mData.hasNext()) {
+ status_t err = stripField(&mData, &mBuffer, mPolicy, spec);
+ if (err != NO_ERROR) return err;
+ }
+ if (mData.bytesRead() != mData.size()) return BAD_VALUE;
+ mSize = mBuffer.size();
+ mData.rp()->rewind(); // rewind the read pointer back to beginning after the strip.
+ return NO_ERROR;
+}
+
+void
+PrivacyBuffer::clear()
+{
+ mSize = 0;
+ mBuffer.wp()->rewind();
+}
+
+size_t
+PrivacyBuffer::size() const { return mSize; }
+
+status_t
+PrivacyBuffer::flush(int fd)
+{
+ status_t err = NO_ERROR;
+ EncodedBuffer::iterator iter = size() == mData.size() ? mData : mBuffer.begin();
+ while (iter.readBuffer() != NULL) {
+ err = write_all(fd, iter.readBuffer(), iter.currentToRead());
+ iter.rp()->move(iter.currentToRead());
+ if (err != NO_ERROR) return err;
+ }
+ return NO_ERROR;
+}
diff --git a/cmds/incidentd/src/EncodedBuffer.h b/cmds/incidentd/src/PrivacyBuffer.h
similarity index 69%
rename from cmds/incidentd/src/EncodedBuffer.h
rename to cmds/incidentd/src/PrivacyBuffer.h
index ea8603a..720b38e 100644
--- a/cmds/incidentd/src/EncodedBuffer.h
+++ b/cmds/incidentd/src/PrivacyBuffer.h
@@ -14,25 +14,27 @@
* limitations under the License.
*/
-#ifndef ENCODED_BUFFER_H
-#define ENCODED_BUFFER_H
+#ifndef PRIVACY_BUFFER_H
+#define PRIVACY_BUFFER_H
-#include "FdBuffer.h"
#include "Privacy.h"
+#include <android/util/EncodedBuffer.h>
#include <stdint.h>
-#include <vector>
+#include <utils/Errors.h>
+
+using namespace android;
+using namespace android::util;
/**
- * 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.
+ * PrivacyBuffer holds the original protobuf data and strips PII-sensitive fields
+ * based on the request and holds stripped data in its own buffer for output.
*/
-class EncodedBuffer
+class PrivacyBuffer
{
public:
- EncodedBuffer(const FdBuffer& buffer, const Privacy* policy);
- ~EncodedBuffer();
+ PrivacyBuffer(const Privacy* policy, EncodedBuffer::iterator& data);
+ ~PrivacyBuffer();
/**
* Strip based on the request and hold data in its own buffer. Return NO_ERROR if strip succeeds.
@@ -55,10 +57,11 @@
status_t flush(int fd);
private:
- const FdBuffer& mFdBuffer;
const Privacy* mPolicy;
- vector<vector<uint8_t>> mBuffers;
+ EncodedBuffer::iterator& mData;
+
+ EncodedBuffer mBuffer;
size_t mSize;
};
-#endif // ENCODED_BUFFER_H
\ No newline at end of file
+#endif // PRIVACY_BUFFER_H
\ No newline at end of file
diff --git a/cmds/incidentd/src/Section.cpp b/cmds/incidentd/src/Section.cpp
index 166fef0..892bcca 100644
--- a/cmds/incidentd/src/Section.cpp
+++ b/cmds/incidentd/src/Section.cpp
@@ -16,15 +16,15 @@
#define LOG_TAG "incidentd"
-#include "EncodedBuffer.h"
#include "FdBuffer.h"
#include "Privacy.h"
+#include "PrivacyBuffer.h"
#include "Section.h"
#include "io_util.h"
-#include "protobuf.h"
#include "section_list.h"
+#include <android/util/protobuf.h>
#include <private/android_filesystem_config.h>
#include <binder/IServiceManager.h>
#include <map>
@@ -32,8 +32,13 @@
#include <wait.h>
#include <unistd.h>
+using namespace android::util;
using namespace std;
+// special section ids
+const int FIELD_ID_INCIDENT_HEADER = 1;
+
+// incident section parameters
const int WAIT_MAX = 5;
const struct timespec WAIT_INTERVAL_NS = {0, 200 * 1000 * 1000};
const char* INCIDENT_HELPER = "/system/bin/incident_helper";
@@ -127,7 +132,8 @@
write_report_requests(const int id, const FdBuffer& buffer, ReportRequestSet* requests)
{
status_t err = -EBADF;
- EncodedBuffer encodedBuffer(buffer, get_privacy_of_section(id));
+ EncodedBuffer::iterator data = buffer.data();
+ PrivacyBuffer privacyBuffer(get_privacy_of_section(id), data);
int writeable = 0;
// The streaming ones, group requests by spec in order to save unnecessary strip operations
@@ -143,34 +149,34 @@
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;
+ err = privacyBuffer.strip(spec);
+ if (err != NO_ERROR) return err; // it means the privacyBuffer data is corrupted.
+ if (privacyBuffer.size() == 0) continue;
for (vector<sp<ReportRequest>>::iterator it = mit->second.begin(); it != mit->second.end(); it++) {
sp<ReportRequest> request = *it;
- err = write_section_header(request->fd, id, encodedBuffer.size());
+ err = write_section_header(request->fd, id, privacyBuffer.size());
if (err != NO_ERROR) { request->err = err; continue; }
- err = encodedBuffer.flush(request->fd);
+ err = privacyBuffer.flush(request->fd);
if (err != NO_ERROR) { request->err = err; continue; }
writeable++;
- ALOGD("Section %d flushed %zu bytes to fd %d with spec %d", id, encodedBuffer.size(), request->fd, spec.dest);
+ ALOGD("Section %d flushed %zu bytes to fd %d with spec %d", id, privacyBuffer.size(), request->fd, spec.dest);
}
- encodedBuffer.clear();
+ privacyBuffer.clear();
}
// The dropbox file
if (requests->mainFd() >= 0) {
- err = encodedBuffer.strip(get_default_dropbox_spec());
+ err = privacyBuffer.strip(get_default_dropbox_spec());
if (err != NO_ERROR) return err; // the buffer data is corrupted.
- if (encodedBuffer.size() == 0) goto DONE;
+ if (privacyBuffer.size() == 0) goto DONE;
- err = write_section_header(requests->mainFd(), id, encodedBuffer.size());
+ err = write_section_header(requests->mainFd(), id, privacyBuffer.size());
if (err != NO_ERROR) { requests->setMainFd(-1); goto DONE; }
- err = encodedBuffer.flush(requests->mainFd());
+ err = privacyBuffer.flush(requests->mainFd());
if (err != NO_ERROR) { requests->setMainFd(-1); goto DONE; }
writeable++;
- ALOGD("Section %d flushed %zu bytes to dropbox %d", id, encodedBuffer.size(), requests->mainFd());
+ ALOGD("Section %d flushed %zu bytes to dropbox %d", id, privacyBuffer.size(), requests->mainFd());
}
DONE:
diff --git a/cmds/incidentd/tests/FdBuffer_test.cpp b/cmds/incidentd/tests/FdBuffer_test.cpp
index d1436b2..2afa778 100644
--- a/cmds/incidentd/tests/FdBuffer_test.cpp
+++ b/cmds/incidentd/tests/FdBuffer_test.cpp
@@ -49,12 +49,11 @@
void AssertBufferContent(const char* expected) {
int i=0;
- FdBuffer::iterator it = buffer.begin();
- while (expected[i] != '\0') {
- ASSERT_EQ(*it, expected[i++]);
- it++;
+ EncodedBuffer::iterator it = buffer.data();
+ while (it.hasNext()) {
+ ASSERT_EQ(it.next(), expected[i++]);
}
- ASSERT_EQ(it, buffer.end());
+ EXPECT_EQ(expected[i], '\0');
}
bool DoDataStream(int rFd, int wFd) {
@@ -92,20 +91,8 @@
}
TEST_F(FdBufferTest, IterateEmpty) {
- FdBuffer::iterator it = buffer.begin();
- EXPECT_EQ(it, buffer.end());
- it += 1;
- 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);
+ EncodedBuffer::iterator it = buffer.data();
+ EXPECT_FALSE(it.hasNext());
}
TEST_F(FdBufferTest, ReadAndIterate) {
@@ -114,15 +101,15 @@
ASSERT_EQ(NO_ERROR, buffer.read(tf.fd, READ_TIMEOUT));
int i=0;
- for (FdBuffer::iterator it = buffer.begin(); it != buffer.end(); ++it) {
- EXPECT_EQ(*it, (uint8_t)testdata[i++]);
+ EncodedBuffer::iterator it = buffer.data();
+ while (it.hasNext()) {
+ EXPECT_EQ(it.next(), (uint8_t)testdata[i++]);
}
- FdBuffer::iterator it = buffer.begin();
- it += buffer.size();
- EXPECT_EQ(it, buffer.end());
+ it.rp()->rewind();
+ it.rp()->move(buffer.size());
EXPECT_EQ(it.bytesRead(), testdata.size());
- EXPECT_FALSE(it.outOfBound());
+ EXPECT_FALSE(it.hasNext());
}
TEST_F(FdBufferTest, ReadTimeout) {
@@ -258,13 +245,15 @@
EXPECT_FALSE(buffer.timedOut());
EXPECT_TRUE(buffer.truncated());
wait(&pid);
- FdBuffer::iterator it = buffer.begin();
- it += fourMB;
+ EncodedBuffer::iterator it = buffer.data();
+ it.rp()->move(fourMB);
EXPECT_EQ(it.bytesRead(), fourMB);
- EXPECT_EQ(it, buffer.end());
- for (FdBuffer::iterator it = buffer.begin(); it != buffer.end(); it++) {
+ EXPECT_FALSE(it.hasNext());
+
+ it.rp()->rewind();
+ while (it.hasNext()) {
char c = 'A' + (it.bytesRead() % 64 / 8);
- ASSERT_TRUE(*it == c);
+ ASSERT_TRUE(it.next() == c);
}
}
}
diff --git a/cmds/incidentd/tests/EncodedBuffer_test.cpp b/cmds/incidentd/tests/PrivacyBuffer_test.cpp
similarity index 77%
rename from cmds/incidentd/tests/EncodedBuffer_test.cpp
rename to cmds/incidentd/tests/PrivacyBuffer_test.cpp
index 37a938a..8f6e355 100644
--- a/cmds/incidentd/tests/EncodedBuffer_test.cpp
+++ b/cmds/incidentd/tests/PrivacyBuffer_test.cpp
@@ -12,7 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include "EncodedBuffer.h"
+#include "FdBuffer.h"
+#include "PrivacyBuffer.h"
#include <android-base/file.h>
#include <android-base/test_utils.h>
@@ -42,9 +43,10 @@
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 {
+
+class PrivacyBufferTest : public Test {
public:
- virtual ~EncodedBufferTest() {
+ virtual ~PrivacyBufferTest() {
// Delete in reverse order of construction, to be consistent with
// regular allocation/deallocation.
while (!privacies.empty()) {
@@ -60,9 +62,10 @@
void writeToFdBuffer(string str) {
ASSERT_TRUE(WriteStringToFile(str, tf.path, false));
ASSERT_EQ(NO_ERROR, buffer.read(tf.fd, 10000));
+ ASSERT_EQ(str.size(), buffer.size());
}
- void assertBuffer(EncodedBuffer& buf, string expected) {
+ void assertBuffer(PrivacyBuffer& buf, string expected) {
ASSERT_EQ(buf.size(), expected.size());
CaptureStdout();
ASSERT_EQ(buf.flush(STDOUT_FILENO), NO_ERROR);
@@ -71,9 +74,10 @@
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);
+ EncodedBuffer::iterator bufData = buffer.data();
+ PrivacyBuffer privacyBuf(policy, bufData);
+ ASSERT_EQ(privacyBuf.strip(spec), NO_ERROR);
+ assertBuffer(privacyBuf, expected);
}
void assertStripByFields(uint8_t dest, string expected, int size, Privacy* privacy, ...) {
@@ -134,67 +138,67 @@
}
};
-TEST_F(EncodedBufferTest, NullFieldPolicy) {
+TEST_F(PrivacyBufferTest, NullFieldPolicy) {
writeToFdBuffer(STRING_FIELD_0);
assertStrip(EXPLICIT, STRING_FIELD_0, create_string_privacy(300, AUTOMATIC, NULL));
}
-TEST_F(EncodedBufferTest, StripSpecNotAllowed) {
+TEST_F(PrivacyBufferTest, StripSpecNotAllowed) {
writeToFdBuffer(STRING_FIELD_0);
assertStripByFields(AUTOMATIC, "", 1, create_privacy(0, STRING_TYPE, EXPLICIT));
}
-TEST_F(EncodedBufferTest, StripVarintField) {
+TEST_F(PrivacyBufferTest, StripVarintField) {
writeToFdBuffer(VARINT_FIELD_1);
assertStripByFields(EXPLICIT, "", 1, create_privacy(1, OTHER_TYPE, LOCAL));
}
-TEST_F(EncodedBufferTest, StripLengthDelimitedField_String) {
+TEST_F(PrivacyBufferTest, StripLengthDelimitedField_String) {
writeToFdBuffer(STRING_FIELD_2);
assertStripByFields(EXPLICIT, "", 1, create_privacy(2, STRING_TYPE, LOCAL));
}
-TEST_F(EncodedBufferTest, StripFixed64Field) {
+TEST_F(PrivacyBufferTest, StripFixed64Field) {
writeToFdBuffer(FIX64_FIELD_3);
assertStripByFields(EXPLICIT, "", 1, create_privacy(3, OTHER_TYPE, LOCAL));
}
-TEST_F(EncodedBufferTest, StripFixed32Field) {
+TEST_F(PrivacyBufferTest, StripFixed32Field) {
writeToFdBuffer(FIX32_FIELD_4);
assertStripByFields(EXPLICIT, "", 1, create_privacy(4, OTHER_TYPE, LOCAL));
}
-TEST_F(EncodedBufferTest, StripLengthDelimitedField_Message) {
+TEST_F(PrivacyBufferTest, StripLengthDelimitedField_Message) {
writeToFdBuffer(MESSAGE_FIELD_5);
assertStripByFields(EXPLICIT, "", 1, create_privacy(5, MESSAGE_TYPE, LOCAL));
}
-TEST_F(EncodedBufferTest, NoStripVarintField) {
+TEST_F(PrivacyBufferTest, NoStripVarintField) {
writeToFdBuffer(VARINT_FIELD_1);
assertStripByFields(EXPLICIT, VARINT_FIELD_1, 1, create_privacy(1, OTHER_TYPE, AUTOMATIC));
}
-TEST_F(EncodedBufferTest, NoStripLengthDelimitedField_String) {
+TEST_F(PrivacyBufferTest, NoStripLengthDelimitedField_String) {
writeToFdBuffer(STRING_FIELD_2);
assertStripByFields(EXPLICIT, STRING_FIELD_2, 1, create_privacy(2, STRING_TYPE, AUTOMATIC));
}
-TEST_F(EncodedBufferTest, NoStripFixed64Field) {
+TEST_F(PrivacyBufferTest, NoStripFixed64Field) {
writeToFdBuffer(FIX64_FIELD_3);
assertStripByFields(EXPLICIT, FIX64_FIELD_3, 1, create_privacy(3, OTHER_TYPE, AUTOMATIC));
}
-TEST_F(EncodedBufferTest, NoStripFixed32Field) {
+TEST_F(PrivacyBufferTest, NoStripFixed32Field) {
writeToFdBuffer(FIX32_FIELD_4);
assertStripByFields(EXPLICIT, FIX32_FIELD_4, 1, create_privacy(4, OTHER_TYPE, AUTOMATIC));
}
-TEST_F(EncodedBufferTest, NoStripLengthDelimitedField_Message) {
+TEST_F(PrivacyBufferTest, NoStripLengthDelimitedField_Message) {
writeToFdBuffer(MESSAGE_FIELD_5);
assertStripByFields(EXPLICIT, MESSAGE_FIELD_5, 1, create_privacy(5, MESSAGE_TYPE, AUTOMATIC));
}
-TEST_F(EncodedBufferTest, StripVarintAndString) {
+TEST_F(PrivacyBufferTest, 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;
@@ -202,7 +206,7 @@
create_privacy(1, OTHER_TYPE, LOCAL), create_privacy(2, STRING_TYPE, LOCAL));
}
-TEST_F(EncodedBufferTest, StripVarintAndFixed64) {
+TEST_F(PrivacyBufferTest, 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;
@@ -210,46 +214,49 @@
create_privacy(1, OTHER_TYPE, LOCAL), create_privacy(3, OTHER_TYPE, LOCAL));
}
-TEST_F(EncodedBufferTest, StripVarintInNestedMessage) {
+TEST_F(PrivacyBufferTest, StripVarintInNestedMessage) {
writeToFdBuffer(STRING_FIELD_0 + MESSAGE_FIELD_5);
Privacy* list[] = { create_privacy(1, OTHER_TYPE, LOCAL), NULL };
string expected = STRING_FIELD_0 + "\x2a\xd" + STRING_FIELD_2;
assertStripByFields(EXPLICIT, expected, 1, create_message_privacy(5, list));
}
-TEST_F(EncodedBufferTest, StripFix64AndVarintInNestedMessage) {
+TEST_F(PrivacyBufferTest, StripFix64AndVarintInNestedMessage) {
writeToFdBuffer(STRING_FIELD_0 + FIX64_FIELD_3 + MESSAGE_FIELD_5);
Privacy* list[] = { create_privacy(1, OTHER_TYPE, LOCAL), NULL };
string expected = STRING_FIELD_0 + "\x2a\xd" + STRING_FIELD_2;
assertStripByFields(EXPLICIT, expected, 2, create_privacy(3, OTHER_TYPE, LOCAL), create_message_privacy(5, list));
}
-TEST_F(EncodedBufferTest, ClearAndStrip) {
+TEST_F(PrivacyBufferTest, ClearAndStrip) {
string data = STRING_FIELD_0 + VARINT_FIELD_1;
writeToFdBuffer(data);
Privacy* list[] = { create_privacy(1, OTHER_TYPE, LOCAL), NULL };
- EncodedBuffer encodedBuf(buffer, create_message_privacy(300, list));
+ EncodedBuffer::iterator bufData = buffer.data();
+ PrivacyBuffer privacyBuf(create_message_privacy(300, list), bufData);
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);
+ ASSERT_EQ(privacyBuf.strip(spec1), NO_ERROR);
+ assertBuffer(privacyBuf, STRING_FIELD_0);
+ ASSERT_EQ(privacyBuf.strip(spec2), NO_ERROR);
+ assertBuffer(privacyBuf, data);
}
-TEST_F(EncodedBufferTest, BadDataInFdBuffer) {
+TEST_F(PrivacyBufferTest, BadDataInFdBuffer) {
writeToFdBuffer("iambaddata");
Privacy* list[] = { create_privacy(4, OTHER_TYPE, AUTOMATIC), NULL };
- EncodedBuffer encodedBuf(buffer, create_message_privacy(300, list));
+ EncodedBuffer::iterator bufData = buffer.data();
+ PrivacyBuffer privacyBuf(create_message_privacy(300, list), bufData);
PrivacySpec spec;
- ASSERT_EQ(encodedBuf.strip(spec), BAD_VALUE);
+ ASSERT_EQ(privacyBuf.strip(spec), BAD_VALUE);
}
-TEST_F(EncodedBufferTest, BadDataInNestedMessage) {
+TEST_F(PrivacyBufferTest, BadDataInNestedMessage) {
writeToFdBuffer(STRING_FIELD_0 + MESSAGE_FIELD_5 + "aoeoe");
Privacy* list[] = { create_privacy(1, OTHER_TYPE, LOCAL), NULL };
Privacy* field5[] = { create_message_privacy(5, list), NULL };
- EncodedBuffer encodedBuf(buffer, create_message_privacy(300, field5));
+ EncodedBuffer::iterator bufData = buffer.data();
+ PrivacyBuffer privacyBuf(create_message_privacy(300, field5), bufData);
PrivacySpec spec;
- ASSERT_EQ(encodedBuf.strip(spec), BAD_VALUE);
+ ASSERT_EQ(privacyBuf.strip(spec), BAD_VALUE);
}
diff --git a/libs/protoutil/Android.mk b/libs/protoutil/Android.mk
new file mode 100644
index 0000000..a534816
--- /dev/null
+++ b/libs/protoutil/Android.mk
@@ -0,0 +1,39 @@
+#
+# 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.
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libprotoutil
+
+LOCAL_CFLAGS := \
+ -Wall -Werror -Wno-missing-field-initializers -Wno-unused-variable -Wunused-parameter
+
+LOCAL_SHARED_LIBRARIES := \
+ libbinder \
+ liblog \
+ libutils
+
+LOCAL_C_INCLUDES := \
+ $(LOCAL_PATH)/include
+
+LOCAL_SRC_FILES := \
+ src/EncodedBuffer.cpp \
+ src/protobuf.cpp \
+
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+
+include $(BUILD_SHARED_LIBRARY)
+
diff --git a/libs/protoutil/include/android/util/EncodedBuffer.h b/libs/protoutil/include/android/util/EncodedBuffer.h
new file mode 100644
index 0000000..cf09609
--- /dev/null
+++ b/libs/protoutil/include/android/util/EncodedBuffer.h
@@ -0,0 +1,170 @@
+/*
+ * 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 ANDROID_UTIL_ENCODED_BUFFER_H
+#define ANDROID_UTIL_ENCODED_BUFFER_H
+
+#include <stdint.h>
+#include <vector>
+
+namespace android {
+namespace util {
+
+using namespace std;
+
+/**
+ * A stream of bytes containing a read pointer and a write pointer,
+ * backed by a set of fixed-size buffers. There are write functions for the
+ * primitive types stored by protocol buffers, but none of the logic
+ * for tags, inner objects, or any of that.
+ *
+ * Terminology:
+ * *Pos: Position in the whole data set (as if it were a single buffer).
+ * *Index: Index of a buffer within the mBuffers list.
+ * *Offset: Position within a buffer.
+ */
+class EncodedBuffer
+{
+public:
+ EncodedBuffer();
+ EncodedBuffer(size_t chunkSize);
+ ~EncodedBuffer();
+
+ class Pointer {
+ public:
+ Pointer();
+ Pointer(size_t chunkSize);
+
+ size_t pos() const;
+ size_t index() const;
+ size_t offset() const;
+
+ void move(size_t amt);
+ inline void move() { move(1); };
+
+ void rewind();
+ Pointer copy() const;
+
+ private:
+ size_t mChunkSize;
+ size_t mIndex;
+ size_t mOffset;
+ };
+
+ /******************************** Write APIs ************************************************/
+
+ /**
+ * Returns the number of bytes written in the buffer
+ */
+ size_t size() const;
+
+ /**
+ * Returns the write pointer.
+ */
+ Pointer* wp();
+
+ /**
+ * Returns the current position of write pointer, if the write buffer is full, it will automatically
+ * rotate to a new buffer with given chunkSize. If NULL is returned, it means NO_MEMORY
+ */
+ uint8_t* writeBuffer();
+
+ /**
+ * Returns the writeable size in the current write buffer .
+ */
+ size_t currentToWrite();
+
+ /**
+ * Write a varint into a vector. Return the size of the varint.
+ */
+ size_t writeRawVarint(uint32_t val);
+
+ /**
+ * Write a protobuf header. Return the size of the header.
+ */
+ size_t writeHeader(uint32_t fieldId, uint8_t wireType);
+
+ /********************************* Read APIs ************************************************/
+ class iterator;
+ friend class iterator;
+ class iterator {
+ public:
+ iterator(const EncodedBuffer& buffer);
+
+ /**
+ * Returns the number of bytes written in the buffer
+ */
+ size_t size() const;
+
+ /**
+ * Returns the size of total bytes read.
+ */
+ size_t bytesRead() const;
+
+ /**
+ * Returns the read pointer.
+ */
+ Pointer* rp();
+
+ /**
+ * Returns the current position of read pointer, if NULL is returned, it reaches end of buffer.
+ */
+ uint8_t const* readBuffer();
+
+ /**
+ * Returns the readable size in the current read buffer.
+ */
+ size_t currentToRead();
+
+ /**
+ * Returns true if next bytes is available for read.
+ */
+ bool hasNext();
+
+ /**
+ * Reads the current byte and moves pointer 1 bit.
+ */
+ uint8_t next();
+
+ /**
+ * Read varint from iterator, the iterator will point to next available byte.
+ * Return the number of bytes of the varint.
+ */
+ uint32_t readRawVarint();
+
+ private:
+ const EncodedBuffer& mData;
+ Pointer mRp;
+ };
+
+ /**
+ * Returns the iterator of EncodedBuffer so it guarantees consumers won't be able to modified the buffer.
+ */
+ iterator begin() const;
+
+private:
+ size_t mChunkSize;
+ vector<uint8_t*> mBuffers;
+
+ Pointer mWp;
+
+ inline uint8_t* at(const Pointer& p) const; // helper function to get value
+};
+
+} // util
+} // android
+
+#endif // ANDROID_UTIL_ENCODED_BUFFER_H
\ No newline at end of file
diff --git a/cmds/incidentd/src/protobuf.h b/libs/protoutil/include/android/util/protobuf.h
similarity index 74%
rename from cmds/incidentd/src/protobuf.h
rename to libs/protoutil/include/android/util/protobuf.h
index 263c864..f4e8d09 100644
--- a/cmds/incidentd/src/protobuf.h
+++ b/libs/protoutil/include/android/util/protobuf.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2016 The Android Open Source Project
+ * 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.
@@ -14,11 +14,13 @@
* limitations under the License.
*/
-#ifndef PROTOBUF_H
-#define PROTOBUF_H
+#ifndef ANDROID_UTIL_PROTOBUF_H
+#define ANDROID_UTIL_PROTOBUF_H
#include <stdint.h>
-#include <vector>
+
+namespace android {
+namespace util {
using namespace std;
@@ -50,19 +52,7 @@
*/
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);
+} // util
+} // android
-/**
- * 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
-};
-
-#endif // PROTOBUF_H
+#endif // ANDROID_UTIL_PROTOUBUF_H
diff --git a/libs/protoutil/src/EncodedBuffer.cpp b/libs/protoutil/src/EncodedBuffer.cpp
new file mode 100644
index 0000000..84dc5b6
--- /dev/null
+++ b/libs/protoutil/src/EncodedBuffer.cpp
@@ -0,0 +1,237 @@
+/*
+ * 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 <android/util/EncodedBuffer.h>
+
+#include <stdlib.h>
+
+namespace android {
+namespace util {
+
+const size_t BUFFER_SIZE = 8 * 1024; // 8 KB
+
+EncodedBuffer::Pointer::Pointer() : Pointer(BUFFER_SIZE)
+{
+}
+
+EncodedBuffer::Pointer::Pointer(size_t chunkSize)
+ :mIndex(0),
+ mOffset(0)
+{
+ mChunkSize = chunkSize == 0 ? BUFFER_SIZE : chunkSize;
+}
+
+size_t
+EncodedBuffer::Pointer::pos() const
+{
+ return mIndex * mChunkSize + mOffset;
+}
+
+size_t
+EncodedBuffer::Pointer::index() const
+{
+ return mIndex;
+}
+
+size_t
+EncodedBuffer::Pointer::offset() const
+{
+ return mOffset;
+}
+
+void
+EncodedBuffer::Pointer::move(size_t amt)
+{
+ size_t newOffset = mOffset + amt;
+ mIndex += newOffset / mChunkSize;
+ mOffset = newOffset % mChunkSize;
+}
+
+void
+EncodedBuffer::Pointer::rewind()
+{
+ mIndex = 0;
+ mOffset = 0;
+}
+
+EncodedBuffer::Pointer
+EncodedBuffer::Pointer::copy() const
+{
+ Pointer p = Pointer(mChunkSize);
+ p.mIndex = mIndex;
+ p.mOffset = mOffset;
+ return p;
+}
+
+// ===========================================================
+EncodedBuffer::EncodedBuffer() : EncodedBuffer(0)
+{
+}
+
+EncodedBuffer::EncodedBuffer(size_t chunkSize)
+ :mBuffers()
+{
+ mChunkSize = chunkSize == 0 ? BUFFER_SIZE : chunkSize;
+ mWp = Pointer(mChunkSize);
+}
+
+EncodedBuffer::~EncodedBuffer()
+{
+ for (size_t i=0; i<mBuffers.size(); i++) {
+ uint8_t* buf = mBuffers[i];
+ free(buf);
+ }
+}
+
+inline uint8_t*
+EncodedBuffer::at(const Pointer& p) const
+{
+ return mBuffers[p.index()] + p.offset();
+}
+
+/******************************** Write APIs ************************************************/
+size_t
+EncodedBuffer::size() const
+{
+ return mWp.pos();
+}
+
+EncodedBuffer::Pointer*
+EncodedBuffer::wp()
+{
+ return &mWp;
+}
+
+uint8_t*
+EncodedBuffer::writeBuffer()
+{
+ // This prevents write pointer move too fast than allocating the buffer.
+ if (mWp.index() > mBuffers.size()) return NULL;
+ uint8_t* buf = NULL;
+ if (mWp.index() == mBuffers.size()) {
+ buf = (uint8_t*)malloc(mChunkSize);
+
+ if (buf == NULL) return NULL; // This indicates NO_MEMORY
+
+ mBuffers.push_back(buf);
+ }
+ return at(mWp);
+}
+
+size_t
+EncodedBuffer::currentToWrite()
+{
+ return mChunkSize - mWp.offset();
+}
+
+size_t
+EncodedBuffer::writeRawVarint(uint32_t val)
+{
+ size_t size = 0;
+ while (true) {
+ size++;
+ if ((val & ~0x7F) == 0) {
+ *writeBuffer() = (uint8_t) val;
+ mWp.move();
+ return size;
+ } else {
+ *writeBuffer() = (uint8_t)((val & 0x7F) | 0x80);
+ mWp.move();
+ val >>= 7;
+ }
+ }
+}
+
+size_t
+EncodedBuffer::writeHeader(uint32_t fieldId, uint8_t wireType)
+{
+ return writeRawVarint((fieldId << 3) | wireType);
+}
+
+/********************************* Read APIs ************************************************/
+EncodedBuffer::iterator
+EncodedBuffer::begin() const
+{
+ return EncodedBuffer::iterator(*this);
+}
+
+EncodedBuffer::iterator::iterator(const EncodedBuffer& buffer)
+ :mData(buffer),
+ mRp(buffer.mChunkSize)
+{
+}
+
+size_t
+EncodedBuffer::iterator::size() const
+{
+ return mData.size();
+}
+
+size_t
+EncodedBuffer::iterator::bytesRead() const
+{
+ return mRp.pos();
+}
+
+EncodedBuffer::Pointer*
+EncodedBuffer::iterator::rp()
+{
+ return &mRp;
+}
+
+uint8_t const*
+EncodedBuffer::iterator::readBuffer()
+{
+ return hasNext() ? const_cast<uint8_t const*>(mData.at(mRp)) : NULL;
+}
+
+size_t
+EncodedBuffer::iterator::currentToRead()
+{
+ return (mData.mWp.index() > mRp.index()) ?
+ mData.mChunkSize - mRp.offset() :
+ mData.mWp.offset() - mRp.offset();
+}
+
+bool
+EncodedBuffer::iterator::hasNext()
+{
+ return mRp.pos() < mData.mWp.pos();
+}
+
+uint8_t
+EncodedBuffer::iterator::next()
+{
+ uint8_t res = *(mData.at(mRp));
+ mRp.move();
+ return res;
+}
+
+uint32_t
+EncodedBuffer::iterator::readRawVarint()
+{
+ uint32_t val = 0, shift = 0;
+ while (true) {
+ uint8_t byte = next();
+ val += (byte & 0x7F) << shift;
+ if ((byte & 0x80) == 0) break;
+ shift += 7;
+ }
+ return val;
+}
+
+} // util
+} // android
diff --git a/cmds/incidentd/src/protobuf.cpp b/libs/protoutil/src/protobuf.cpp
similarity index 67%
rename from cmds/incidentd/src/protobuf.cpp
rename to libs/protoutil/src/protobuf.cpp
index 4fffec1..ec5325c 100644
--- a/cmds/incidentd/src/protobuf.cpp
+++ b/libs/protoutil/src/protobuf.cpp
@@ -14,14 +14,19 @@
* limitations under the License.
*/
-#include "protobuf.h"
+#include <android/util/protobuf.h>
-uint8_t read_wire_type(uint32_t varint)
+namespace android {
+namespace util {
+
+uint8_t
+read_wire_type(uint32_t varint)
{
return (uint8_t) (varint & 0x07);
}
-uint32_t read_field_id(uint32_t varint)
+uint32_t
+read_field_id(uint32_t varint)
{
return varint >> 3;
}
@@ -49,24 +54,5 @@
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
+} // util
+} // android