Merge changes Icdcbeded,I6753df11
* changes: Extract IncidentHeaderProto to a separated file for statsd to consume Implement Pii Stripper Part 3
This commit is contained in:
@@ -25,6 +25,7 @@
|
||||
#include <binder/IServiceManager.h>
|
||||
#include <utils/Looper.h>
|
||||
|
||||
#include <cstring>
|
||||
#include <fcntl.h>
|
||||
#include <getopt.h>
|
||||
#include <stdio.h>
|
||||
@@ -143,6 +144,16 @@ find_section(const char* name)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// ================================================================================
|
||||
static int
|
||||
get_dest(const char* arg)
|
||||
{
|
||||
if (strcmp(arg, "LOCAL") == 0) return 0;
|
||||
if (strcmp(arg, "EXPLICIT") == 0) return 1;
|
||||
if (strcmp(arg, "AUTOMATIC") == 0) return 2;
|
||||
return -1; // return the default value
|
||||
}
|
||||
|
||||
// ================================================================================
|
||||
static void
|
||||
usage(FILE* out)
|
||||
@@ -155,6 +166,7 @@ usage(FILE* out)
|
||||
fprintf(out, " -b (default) print the report to stdout (in proto format)\n");
|
||||
fprintf(out, " -d send the report into dropbox\n");
|
||||
fprintf(out, " -l list available sections\n");
|
||||
fprintf(out, " -p privacy spec, LOCAL, EXPLICIT or AUTOMATIC\n");
|
||||
fprintf(out, "\n");
|
||||
fprintf(out, " SECTION the field numbers of the incident report fields to include\n");
|
||||
fprintf(out, "\n");
|
||||
@@ -166,10 +178,11 @@ main(int argc, char** argv)
|
||||
Status status;
|
||||
IncidentReportArgs args;
|
||||
enum { DEST_DROPBOX, DEST_STDOUT } destination = DEST_STDOUT;
|
||||
int dest = -1; // default
|
||||
|
||||
// Parse the args
|
||||
int opt;
|
||||
while ((opt = getopt(argc, argv, "bhdl")) != -1) {
|
||||
while ((opt = getopt(argc, argv, "bhdlp:")) != -1) {
|
||||
switch (opt) {
|
||||
case 'h':
|
||||
usage(stdout);
|
||||
@@ -183,6 +196,9 @@ main(int argc, char** argv)
|
||||
case 'd':
|
||||
destination = DEST_DROPBOX;
|
||||
break;
|
||||
case 'p':
|
||||
dest = get_dest(optarg);
|
||||
break;
|
||||
default:
|
||||
usage(stderr);
|
||||
return 1;
|
||||
@@ -210,8 +226,7 @@ main(int argc, char** argv)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
args.setDest(dest);
|
||||
|
||||
// Start the thread pool.
|
||||
sp<ProcessState> ps(ProcessState::self());
|
||||
|
||||
@@ -70,7 +70,6 @@ write_field_or_skip(FdBuffer::iterator &iterator, vector<uint8_t> &buf, uint8_t
|
||||
if (skip) {
|
||||
iterator += bytesToWrite;
|
||||
} else {
|
||||
buf.reserve(bytesToWrite);
|
||||
for (size_t i=0; i<bytesToWrite; i++) {
|
||||
buf.push_back(*iterator);
|
||||
iterator++;
|
||||
@@ -192,4 +191,5 @@ EncodedBuffer::flush(int fd)
|
||||
if (err != NO_ERROR) return err;
|
||||
}
|
||||
return NO_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -258,6 +258,12 @@ FdBuffer::flush(int fd) const
|
||||
return write_all(fd, mBuffers[i], mCurrentWritten);
|
||||
}
|
||||
|
||||
FdBuffer::iterator
|
||||
FdBuffer::begin() const
|
||||
{
|
||||
return iterator(*this, 0, 0);
|
||||
}
|
||||
|
||||
FdBuffer::iterator
|
||||
FdBuffer::end() const
|
||||
{
|
||||
@@ -268,6 +274,17 @@ FdBuffer::end() const
|
||||
return FdBuffer::iterator(*this, mBuffers.size() - 1, mCurrentWritten);
|
||||
}
|
||||
|
||||
// ===============================================================================
|
||||
FdBuffer::iterator::iterator(const FdBuffer& buffer, ssize_t index, ssize_t offset)
|
||||
: mFdBuffer(buffer),
|
||||
mIndex(index),
|
||||
mOffset(offset)
|
||||
{
|
||||
}
|
||||
|
||||
FdBuffer::iterator&
|
||||
FdBuffer::iterator::operator=(iterator& other) const { return other; }
|
||||
|
||||
FdBuffer::iterator&
|
||||
FdBuffer::iterator::operator+(size_t offset)
|
||||
{
|
||||
@@ -280,8 +297,50 @@ FdBuffer::iterator::operator+(size_t offset)
|
||||
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();
|
||||
}
|
||||
|
||||
@@ -87,32 +87,29 @@ public:
|
||||
friend class iterator;
|
||||
class iterator : public std::iterator<std::random_access_iterator_tag, uint8_t> {
|
||||
public:
|
||||
iterator(const FdBuffer& buffer, ssize_t index, ssize_t offset)
|
||||
: mFdBuffer(buffer), mIndex(index), mOffset(offset) {}
|
||||
iterator& operator=(iterator& other) const { return other; }
|
||||
iterator& operator+(size_t offset); // this is implemented in .cpp
|
||||
iterator& operator+=(size_t offset) { return *this + offset; }
|
||||
iterator& operator++() { return *this + 1; }
|
||||
iterator operator++(int) { return *this + 1; }
|
||||
bool operator==(iterator other) const {
|
||||
return mIndex == other.mIndex && mOffset == other.mOffset;
|
||||
}
|
||||
bool operator!=(iterator other) const { return !(*this == other); }
|
||||
int operator-(iterator other) const { return (int)bytesRead() - (int)other.bytesRead(); }
|
||||
reference operator*() const { return mFdBuffer.mBuffers[mIndex][mOffset]; }
|
||||
iterator(const FdBuffer& buffer, ssize_t index, ssize_t offset);
|
||||
iterator& operator=(iterator& other) const;
|
||||
iterator& operator+(size_t offset);
|
||||
iterator& operator+=(size_t offset);
|
||||
iterator& operator++();
|
||||
iterator operator++(int);
|
||||
bool operator==(iterator other) const;
|
||||
bool operator!=(iterator other) const;
|
||||
int operator-(iterator other) const;
|
||||
reference operator*() const;
|
||||
|
||||
// return the snapshot of the current iterator
|
||||
iterator snapshot() const { return iterator(mFdBuffer, mIndex, mOffset); }
|
||||
iterator snapshot() const;
|
||||
// how many bytes are read
|
||||
size_t bytesRead() const;
|
||||
// random access could make the iterator out of bound
|
||||
bool outOfBound() const { return bytesRead() > mFdBuffer.size(); }
|
||||
bool outOfBound() const;
|
||||
private:
|
||||
const FdBuffer& mFdBuffer;
|
||||
size_t mIndex;
|
||||
size_t mOffset;
|
||||
};
|
||||
iterator begin() const { return iterator(*this, 0, 0); }
|
||||
iterator begin() const;
|
||||
iterator end() const;
|
||||
|
||||
private:
|
||||
|
||||
@@ -87,6 +87,12 @@ static bool allowDest(const uint8_t dest, const uint8_t policy)
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
PrivacySpec::operator<(const PrivacySpec& other) const
|
||||
{
|
||||
return dest < other.dest;
|
||||
}
|
||||
|
||||
bool
|
||||
PrivacySpec::CheckPremission(const Privacy* privacy) const
|
||||
{
|
||||
@@ -97,4 +103,9 @@ PrivacySpec::CheckPremission(const Privacy* privacy) const
|
||||
bool
|
||||
PrivacySpec::RequireAll() const { return dest == DEST_LOCAL; }
|
||||
|
||||
PrivacySpec new_spec_from_args(int dest) {
|
||||
if (dest < 0) return PrivacySpec();
|
||||
return PrivacySpec(dest);
|
||||
}
|
||||
|
||||
PrivacySpec get_default_dropbox_spec() { return PrivacySpec(DEST_AUTOMATIC); }
|
||||
@@ -58,10 +58,13 @@ public:
|
||||
PrivacySpec() : dest(DEST_DEFAULT_VALUE) {}
|
||||
PrivacySpec(uint8_t dest) : dest(dest) {}
|
||||
|
||||
bool operator<(const PrivacySpec& other) const;
|
||||
|
||||
bool CheckPremission(const Privacy* privacy) const;
|
||||
bool RequireAll() const;
|
||||
};
|
||||
|
||||
PrivacySpec new_spec_from_args(int dest);
|
||||
PrivacySpec get_default_dropbox_spec();
|
||||
|
||||
#endif // PRIVACY_H
|
||||
|
||||
@@ -17,8 +17,6 @@
|
||||
#define LOG_TAG "incidentd"
|
||||
|
||||
#include "Reporter.h"
|
||||
#include "io_util.h"
|
||||
#include "protobuf.h"
|
||||
|
||||
#include "report_directory.h"
|
||||
#include "section_list.h"
|
||||
@@ -52,6 +50,12 @@ ReportRequest::~ReportRequest()
|
||||
{
|
||||
}
|
||||
|
||||
bool
|
||||
ReportRequest::ok()
|
||||
{
|
||||
return fd >= 0 && err == NO_ERROR;
|
||||
}
|
||||
|
||||
// ================================================================================
|
||||
ReportRequestSet::ReportRequestSet()
|
||||
:mRequests(),
|
||||
@@ -117,6 +121,7 @@ Reporter::runReport()
|
||||
status_t err = NO_ERROR;
|
||||
bool needMainFd = false;
|
||||
int mainFd = -1;
|
||||
HeaderSection headers;
|
||||
|
||||
// See if we need the main file
|
||||
for (ReportRequestSet::iterator it=batch.begin(); it!=batch.end(); it++) {
|
||||
@@ -129,7 +134,7 @@ Reporter::runReport()
|
||||
// Create the directory
|
||||
if (!isTest) err = create_directory(mIncidentDirectory);
|
||||
if (err != NO_ERROR) {
|
||||
goto done;
|
||||
goto DONE;
|
||||
}
|
||||
|
||||
// If there are too many files in the directory (for whatever reason),
|
||||
@@ -140,7 +145,7 @@ Reporter::runReport()
|
||||
// Open the file.
|
||||
err = create_file(&mainFd);
|
||||
if (err != NO_ERROR) {
|
||||
goto done;
|
||||
goto DONE;
|
||||
}
|
||||
|
||||
// Add to the set
|
||||
@@ -155,24 +160,7 @@ Reporter::runReport()
|
||||
}
|
||||
|
||||
// Write the incident headers
|
||||
for (ReportRequestSet::iterator it=batch.begin(); it!=batch.end(); it++) {
|
||||
const sp<ReportRequest> request = (*it);
|
||||
const vector<vector<int8_t>>& headers = request->args.headers();
|
||||
|
||||
for (vector<vector<int8_t>>::const_iterator buf=headers.begin(); buf!=headers.end();
|
||||
buf++) {
|
||||
int fd = request->fd >= 0 ? request->fd : mainFd;
|
||||
|
||||
uint8_t buffer[20];
|
||||
uint8_t* p = write_length_delimited_tag_header(buffer, FIELD_ID_INCIDENT_HEADER,
|
||||
buf->size());
|
||||
write_all(fd, buffer, p-buffer);
|
||||
|
||||
write_all(fd, (uint8_t const*)buf->data(), buf->size());
|
||||
// If there was an error now, there will be an error later and we will remove
|
||||
// it from the list then.
|
||||
}
|
||||
}
|
||||
headers.Execute(&batch);
|
||||
|
||||
// For each of the report fields, see if we need it, and if so, execute the command
|
||||
// and report to those that care that we're doing it.
|
||||
@@ -193,7 +181,7 @@ Reporter::runReport()
|
||||
if (err != NO_ERROR) {
|
||||
ALOGW("Incident section %s (%d) failed. Stopping report.",
|
||||
(*section)->name.string(), id);
|
||||
goto done;
|
||||
goto DONE;
|
||||
}
|
||||
|
||||
// Notify listener of starting
|
||||
@@ -207,7 +195,7 @@ Reporter::runReport()
|
||||
}
|
||||
}
|
||||
|
||||
done:
|
||||
DONE:
|
||||
// Close the file.
|
||||
if (mainFd >= 0) {
|
||||
close(mainFd);
|
||||
|
||||
@@ -40,6 +40,8 @@ struct ReportRequest : public virtual RefBase
|
||||
ReportRequest(const IncidentReportArgs& args,
|
||||
const sp<IIncidentReportStatusListener> &listener, int fd);
|
||||
virtual ~ReportRequest();
|
||||
|
||||
bool ok(); // returns true if the request is ok for write.
|
||||
};
|
||||
|
||||
// ================================================================================
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
|
||||
#include <private/android_filesystem_config.h>
|
||||
#include <binder/IServiceManager.h>
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
#include <wait.h>
|
||||
#include <unistd.h>
|
||||
@@ -38,7 +39,7 @@ const struct timespec WAIT_INTERVAL_NS = {0, 200 * 1000 * 1000};
|
||||
const char* INCIDENT_HELPER = "/system/bin/incident_helper";
|
||||
|
||||
static pid_t
|
||||
forkAndExecuteIncidentHelper(const int id, const char* name, Fpipe& p2cPipe, Fpipe& c2pPipe)
|
||||
fork_execute_incident_helper(const int id, const char* name, Fpipe& p2cPipe, Fpipe& c2pPipe)
|
||||
{
|
||||
const char* ihArgs[] { INCIDENT_HELPER, "-s", String8::format("%d", id).string(), NULL };
|
||||
|
||||
@@ -73,14 +74,14 @@ forkAndExecuteIncidentHelper(const int id, const char* name, Fpipe& p2cPipe, Fpi
|
||||
}
|
||||
|
||||
// ================================================================================
|
||||
static status_t killChild(pid_t pid) {
|
||||
static status_t kill_child(pid_t pid) {
|
||||
int status;
|
||||
kill(pid, SIGKILL);
|
||||
if (waitpid(pid, &status, 0) == -1) return -1;
|
||||
return WIFEXITED(status) == 0 ? NO_ERROR : -WEXITSTATUS(status);
|
||||
}
|
||||
|
||||
static status_t waitForChild(pid_t pid) {
|
||||
static status_t wait_child(pid_t pid) {
|
||||
int status;
|
||||
bool died = false;
|
||||
// wait for child to report status up to 1 seconds
|
||||
@@ -89,13 +90,12 @@ static status_t waitForChild(pid_t pid) {
|
||||
// sleep for 0.2 second
|
||||
nanosleep(&WAIT_INTERVAL_NS, NULL);
|
||||
}
|
||||
if (!died) return killChild(pid);
|
||||
if (!died) return kill_child(pid);
|
||||
return WIFEXITED(status) == 0 ? NO_ERROR : -WEXITSTATUS(status);
|
||||
}
|
||||
|
||||
// ================================================================================
|
||||
static const Privacy*
|
||||
GetPrivacyOfSection(int id)
|
||||
get_privacy_of_section(int id)
|
||||
{
|
||||
if (id < 0) return NULL;
|
||||
int i=0;
|
||||
@@ -108,60 +108,74 @@ GetPrivacyOfSection(int id)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// ================================================================================
|
||||
static status_t
|
||||
WriteToRequest(const int id, const int fd, EncodedBuffer& buffer, const PrivacySpec& spec)
|
||||
write_section_header(int fd, int sectionId, size_t size)
|
||||
{
|
||||
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;
|
||||
uint8_t *p = write_length_delimited_tag_header(buf, sectionId, size);
|
||||
return write_all(fd, buf, p-buf);
|
||||
}
|
||||
|
||||
static status_t
|
||||
WriteToReportRequests(const int id, const FdBuffer& buffer, ReportRequestSet* requests)
|
||||
write_report_requests(const int id, const FdBuffer& buffer, ReportRequestSet* requests)
|
||||
{
|
||||
status_t err = EBADF;
|
||||
EncodedBuffer encodedBuffer(buffer, GetPrivacyOfSection(id));
|
||||
status_t err = -EBADF;
|
||||
EncodedBuffer encodedBuffer(buffer, get_privacy_of_section(id));
|
||||
int writeable = 0;
|
||||
|
||||
// The streaming ones
|
||||
// The streaming ones, group requests by spec in order to save unnecessary strip operations
|
||||
map<PrivacySpec, vector<sp<ReportRequest>>> requestsBySpec;
|
||||
for (ReportRequestSet::iterator it = requests->begin(); it != requests->end(); it++) {
|
||||
sp<ReportRequest> request = *it;
|
||||
PrivacySpec spec; // TODO: this should be derived from each request.
|
||||
err = WriteToRequest(id, request->fd, encodedBuffer, spec);
|
||||
if (err != NO_ERROR) {
|
||||
request->err = err;
|
||||
} else {
|
||||
writeable++;
|
||||
if (!request->ok() || !request->args.containsSection(id)) {
|
||||
continue; // skip invalid request
|
||||
}
|
||||
PrivacySpec spec = new_spec_from_args(request->args.dest());
|
||||
requestsBySpec[spec].push_back(request);
|
||||
}
|
||||
|
||||
for (map<PrivacySpec, vector<sp<ReportRequest>>>::iterator mit = requestsBySpec.begin(); mit != requestsBySpec.end(); mit++) {
|
||||
PrivacySpec spec = mit->first;
|
||||
err = encodedBuffer.strip(spec);
|
||||
if (err != NO_ERROR) return err; // it means the encodedBuffer data is corrupted.
|
||||
if (encodedBuffer.size() == 0) continue;
|
||||
|
||||
for (vector<sp<ReportRequest>>::iterator it = mit->second.begin(); it != mit->second.end(); it++) {
|
||||
sp<ReportRequest> request = *it;
|
||||
err = write_section_header(request->fd, id, encodedBuffer.size());
|
||||
if (err != NO_ERROR) { request->err = err; continue; }
|
||||
err = encodedBuffer.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);
|
||||
}
|
||||
encodedBuffer.clear();
|
||||
}
|
||||
|
||||
// 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++;
|
||||
}
|
||||
err = encodedBuffer.strip(get_default_dropbox_spec());
|
||||
if (err != NO_ERROR) return err; // the buffer data is corrupted.
|
||||
if (encodedBuffer.size() == 0) goto DONE;
|
||||
|
||||
err = write_section_header(requests->mainFd(), id, encodedBuffer.size());
|
||||
if (err != NO_ERROR) { requests->setMainFd(-1); goto DONE; }
|
||||
err = encodedBuffer.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());
|
||||
}
|
||||
|
||||
DONE:
|
||||
// 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)
|
||||
:id(i),
|
||||
timeoutMs(timeoutMs)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -169,9 +183,42 @@ Section::~Section()
|
||||
{
|
||||
}
|
||||
|
||||
// ================================================================================
|
||||
HeaderSection::HeaderSection()
|
||||
:Section(FIELD_ID_INCIDENT_HEADER, 0)
|
||||
{
|
||||
}
|
||||
|
||||
HeaderSection::~HeaderSection()
|
||||
{
|
||||
}
|
||||
|
||||
status_t
|
||||
HeaderSection::Execute(ReportRequestSet* requests) const
|
||||
{
|
||||
for (ReportRequestSet::iterator it=requests->begin(); it!=requests->end(); it++) {
|
||||
const sp<ReportRequest> request = *it;
|
||||
const vector<vector<int8_t>>& headers = request->args.headers();
|
||||
|
||||
for (vector<vector<int8_t>>::const_iterator buf=headers.begin(); buf!=headers.end(); buf++) {
|
||||
if (buf->empty()) continue;
|
||||
|
||||
// So the idea is only requests with negative fd are written to dropbox file.
|
||||
int fd = request->fd >= 0 ? request->fd : requests->mainFd();
|
||||
write_section_header(fd, FIELD_ID_INCIDENT_HEADER, buf->size());
|
||||
write_all(fd, (uint8_t const*)buf->data(), buf->size());
|
||||
// If there was an error now, there will be an error later and we will remove
|
||||
// it from the list then.
|
||||
}
|
||||
}
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
// ================================================================================
|
||||
FileSection::FileSection(int id, const char* filename, const int64_t timeoutMs)
|
||||
: Section(id, timeoutMs), mFilename(filename) {
|
||||
:Section(id, timeoutMs),
|
||||
mFilename(filename)
|
||||
{
|
||||
name = filename;
|
||||
}
|
||||
|
||||
@@ -197,7 +244,7 @@ FileSection::Execute(ReportRequestSet* requests) const
|
||||
return -errno;
|
||||
}
|
||||
|
||||
pid_t pid = forkAndExecuteIncidentHelper(this->id, this->name.string(), p2cPipe, c2pPipe);
|
||||
pid_t pid = fork_execute_incident_helper(this->id, this->name.string(), p2cPipe, c2pPipe);
|
||||
if (pid == -1) {
|
||||
ALOGW("FileSection '%s' failed to fork", this->name.string());
|
||||
return -errno;
|
||||
@@ -209,11 +256,11 @@ FileSection::Execute(ReportRequestSet* requests) const
|
||||
if (readStatus != NO_ERROR || buffer.timedOut()) {
|
||||
ALOGW("FileSection '%s' failed to read data from incident helper: %s, timedout: %s, kill: %s",
|
||||
this->name.string(), strerror(-readStatus), buffer.timedOut() ? "true" : "false",
|
||||
strerror(-killChild(pid)));
|
||||
strerror(-kill_child(pid)));
|
||||
return readStatus;
|
||||
}
|
||||
|
||||
status_t ihStatus = waitForChild(pid);
|
||||
status_t ihStatus = wait_child(pid);
|
||||
if (ihStatus != NO_ERROR) {
|
||||
ALOGW("FileSection '%s' abnormal child process: %s", this->name.string(), strerror(-ihStatus));
|
||||
return ihStatus;
|
||||
@@ -221,7 +268,7 @@ FileSection::Execute(ReportRequestSet* requests) const
|
||||
|
||||
ALOGD("FileSection '%s' wrote %zd bytes in %d ms", this->name.string(), buffer.size(),
|
||||
(int)buffer.durationMs());
|
||||
status_t err = WriteToReportRequests(this->id, buffer, requests);
|
||||
status_t err = write_report_requests(this->id, buffer, requests);
|
||||
if (err != NO_ERROR) {
|
||||
ALOGW("FileSection '%s' failed writing: %s", this->name.string(), strerror(-err));
|
||||
return err;
|
||||
@@ -378,7 +425,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());
|
||||
err = WriteToReportRequests(this->id, buffer, requests);
|
||||
err = write_report_requests(this->id, buffer, requests);
|
||||
if (err != NO_ERROR) {
|
||||
ALOGW("WorkerThreadSection '%s' failed writing: '%s'", this->name.string(), strerror(-err));
|
||||
return err;
|
||||
@@ -415,7 +462,7 @@ CommandSection::init(const char* command, va_list args)
|
||||
}
|
||||
|
||||
CommandSection::CommandSection(int id, const int64_t timeoutMs, const char* command, ...)
|
||||
: Section(id, timeoutMs)
|
||||
:Section(id, timeoutMs)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, command);
|
||||
@@ -424,7 +471,7 @@ CommandSection::CommandSection(int id, const int64_t timeoutMs, const char* comm
|
||||
}
|
||||
|
||||
CommandSection::CommandSection(int id, const char* command, ...)
|
||||
: Section(id)
|
||||
:Section(id)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, command);
|
||||
@@ -466,7 +513,7 @@ CommandSection::Execute(ReportRequestSet* requests) const
|
||||
ALOGW("CommandSection '%s' failed in executing command: %s", this->name.string(), strerror(errno));
|
||||
_exit(err); // exit with command error code
|
||||
}
|
||||
pid_t ihPid = forkAndExecuteIncidentHelper(this->id, this->name.string(), cmdPipe, ihPipe);
|
||||
pid_t ihPid = fork_execute_incident_helper(this->id, this->name.string(), cmdPipe, ihPipe);
|
||||
if (ihPid == -1) {
|
||||
ALOGW("CommandSection '%s' failed to fork", this->name.string());
|
||||
return -errno;
|
||||
@@ -478,14 +525,14 @@ CommandSection::Execute(ReportRequestSet* requests) const
|
||||
ALOGW("CommandSection '%s' failed to read data from incident helper: %s, "
|
||||
"timedout: %s, kill command: %s, kill incident helper: %s",
|
||||
this->name.string(), strerror(-readStatus), buffer.timedOut() ? "true" : "false",
|
||||
strerror(-killChild(cmdPid)), strerror(-killChild(ihPid)));
|
||||
strerror(-kill_child(cmdPid)), strerror(-kill_child(ihPid)));
|
||||
return readStatus;
|
||||
}
|
||||
|
||||
// TODO: wait for command here has one trade-off: the failed status of command won't be detected until
|
||||
// buffer timeout, but it has advatage on starting the data stream earlier.
|
||||
status_t cmdStatus = waitForChild(cmdPid);
|
||||
status_t ihStatus = waitForChild(ihPid);
|
||||
status_t cmdStatus = wait_child(cmdPid);
|
||||
status_t ihStatus = wait_child(ihPid);
|
||||
if (cmdStatus != NO_ERROR || ihStatus != NO_ERROR) {
|
||||
ALOGW("CommandSection '%s' abnormal child processes, return status: command: %s, incident helper: %s",
|
||||
this->name.string(), strerror(-cmdStatus), strerror(-ihStatus));
|
||||
@@ -494,7 +541,7 @@ CommandSection::Execute(ReportRequestSet* requests) const
|
||||
|
||||
ALOGD("CommandSection '%s' wrote %zd bytes in %d ms", this->name.string(), buffer.size(),
|
||||
(int)buffer.durationMs());
|
||||
status_t err = WriteToReportRequests(this->id, buffer, requests);
|
||||
status_t err = write_report_requests(this->id, buffer, requests);
|
||||
if (err != NO_ERROR) {
|
||||
ALOGW("CommandSection '%s' failed writing: %s", this->name.string(), strerror(-err));
|
||||
return err;
|
||||
|
||||
@@ -44,6 +44,18 @@ public:
|
||||
virtual status_t Execute(ReportRequestSet* requests) const = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Section that generates incident headers.
|
||||
*/
|
||||
class HeaderSection : public Section
|
||||
{
|
||||
public:
|
||||
HeaderSection();
|
||||
virtual ~HeaderSection();
|
||||
|
||||
virtual status_t Execute(ReportRequestSet* requests) const;
|
||||
};
|
||||
|
||||
/**
|
||||
* Section that reads in a file.
|
||||
*/
|
||||
|
||||
@@ -76,8 +76,7 @@ public:
|
||||
};
|
||||
|
||||
protected:
|
||||
IBinder* onAsBinder() override { return nullptr; };
|
||||
|
||||
virtual IBinder* onAsBinder() override { return nullptr; };
|
||||
};
|
||||
|
||||
class ReporterTest : public Test {
|
||||
|
||||
@@ -33,12 +33,66 @@ 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 android::binder;
|
||||
using namespace std;
|
||||
using ::testing::StrEq;
|
||||
using ::testing::internal::CaptureStdout;
|
||||
using ::testing::internal::GetCapturedStdout;
|
||||
|
||||
// NOTICE: this test requires /system/bin/incident_helper is installed.
|
||||
|
||||
class SimpleListener : public IIncidentReportStatusListener
|
||||
{
|
||||
public:
|
||||
SimpleListener() {};
|
||||
virtual ~SimpleListener() {};
|
||||
|
||||
virtual Status onReportStarted() { return Status::ok(); };
|
||||
virtual Status onReportSectionStatus(int /*section*/, int /*status*/) { return Status::ok(); };
|
||||
virtual Status onReportFinished() { return Status::ok(); };
|
||||
virtual Status onReportFailed() { return Status::ok(); };
|
||||
|
||||
protected:
|
||||
virtual IBinder* onAsBinder() override { return nullptr; };
|
||||
};
|
||||
|
||||
TEST(SectionTest, HeaderSection) {
|
||||
TemporaryFile output2;
|
||||
HeaderSection hs;
|
||||
ReportRequestSet requests;
|
||||
|
||||
IncidentReportArgs args1, args2;
|
||||
args1.addSection(1);
|
||||
args1.addSection(2);
|
||||
args2.setAll(true);
|
||||
|
||||
vector<int8_t> head1;
|
||||
head1.push_back('a');
|
||||
head1.push_back('x');
|
||||
head1.push_back('e');
|
||||
|
||||
vector<int8_t> head2;
|
||||
head2.push_back('p');
|
||||
head2.push_back('u');
|
||||
head2.push_back('p');
|
||||
|
||||
args1.addHeader(head1);
|
||||
args1.addHeader(head2);
|
||||
args2.addHeader(head2);
|
||||
|
||||
requests.add(new ReportRequest(args1, new SimpleListener(), -1));
|
||||
requests.add(new ReportRequest(args2, new SimpleListener(), output2.fd));
|
||||
requests.setMainFd(STDOUT_FILENO);
|
||||
|
||||
string content;
|
||||
CaptureStdout();
|
||||
ASSERT_EQ(NO_ERROR, hs.Execute(&requests));
|
||||
EXPECT_THAT(GetCapturedStdout(), StrEq("\n\x3" "axe\n\x03pup"));
|
||||
|
||||
EXPECT_TRUE(ReadFileToString(output2.path, &content));
|
||||
EXPECT_THAT(content, StrEq("\n\x03pup"));
|
||||
}
|
||||
|
||||
TEST(SectionTest, FileSection) {
|
||||
TemporaryFile tf;
|
||||
FileSection fs(REVERSE_PARSER, tf.path);
|
||||
@@ -126,4 +180,118 @@ TEST(SectionTest, TestFilterPiiTaggedFields) {
|
||||
CaptureStdout();
|
||||
ASSERT_EQ(NO_ERROR, fs.Execute(&requests));
|
||||
EXPECT_THAT(GetCapturedStdout(), StrEq("\x02\r" + STRING_FIELD_2));
|
||||
}
|
||||
|
||||
TEST(SectionTest, TestBadFdRequest) {
|
||||
TemporaryFile input;
|
||||
FileSection fs(NOOP_PARSER, input.path);
|
||||
ReportRequestSet requests;
|
||||
ASSERT_TRUE(WriteStringToFile(VARINT_FIELD_1 + STRING_FIELD_2 + FIX64_FIELD_3, input.path, false));
|
||||
|
||||
IncidentReportArgs args;
|
||||
args.setAll(true);
|
||||
args.setDest(0);
|
||||
sp<ReportRequest> badFdRequest = new ReportRequest(args, new SimpleListener(), 1234567);
|
||||
requests.add(badFdRequest);
|
||||
requests.setMainFd(STDOUT_FILENO);
|
||||
|
||||
CaptureStdout();
|
||||
ASSERT_EQ(NO_ERROR, fs.Execute(&requests));
|
||||
EXPECT_THAT(GetCapturedStdout(), StrEq("\x02\r" + STRING_FIELD_2));
|
||||
EXPECT_EQ(badFdRequest->err, -EBADF);
|
||||
}
|
||||
|
||||
TEST(SectionTest, TestBadRequests) {
|
||||
TemporaryFile input;
|
||||
FileSection fs(NOOP_PARSER, input.path);
|
||||
ReportRequestSet requests;
|
||||
ASSERT_TRUE(WriteStringToFile(VARINT_FIELD_1 + STRING_FIELD_2 + FIX64_FIELD_3, input.path, false));
|
||||
|
||||
IncidentReportArgs args;
|
||||
args.setAll(true);
|
||||
args.setDest(0);
|
||||
requests.add(new ReportRequest(args, new SimpleListener(), -1));
|
||||
EXPECT_EQ(fs.Execute(&requests), -EBADF);
|
||||
}
|
||||
|
||||
TEST(SectionTest, TestMultipleRequests) {
|
||||
TemporaryFile input, output1, output2, output3;
|
||||
FileSection fs(NOOP_PARSER, input.path);
|
||||
ReportRequestSet requests;
|
||||
|
||||
ASSERT_TRUE(input.fd != -1);
|
||||
ASSERT_TRUE(output1.fd != -1);
|
||||
ASSERT_TRUE(output2.fd != -1);
|
||||
ASSERT_TRUE(output3.fd != -1);
|
||||
ASSERT_TRUE(WriteStringToFile(VARINT_FIELD_1 + STRING_FIELD_2 + FIX64_FIELD_3, input.path, false));
|
||||
|
||||
IncidentReportArgs args1, args2, args3;
|
||||
args1.setAll(true);
|
||||
args1.setDest(0); // LOCAL
|
||||
args2.setAll(true); // default to explicit
|
||||
sp<SimpleListener> l = new SimpleListener();
|
||||
requests.add(new ReportRequest(args1, l, output1.fd));
|
||||
requests.add(new ReportRequest(args2, l, output2.fd));
|
||||
requests.add(new ReportRequest(args3, l, output3.fd));
|
||||
requests.setMainFd(STDOUT_FILENO);
|
||||
|
||||
CaptureStdout();
|
||||
ASSERT_EQ(NO_ERROR, fs.Execute(&requests));
|
||||
EXPECT_THAT(GetCapturedStdout(), StrEq("\x02\r" + STRING_FIELD_2));
|
||||
|
||||
string content, expect;
|
||||
expect = VARINT_FIELD_1 + STRING_FIELD_2 + FIX64_FIELD_3;
|
||||
char c = (char) expect.size();
|
||||
EXPECT_TRUE(ReadFileToString(output1.path, &content));
|
||||
EXPECT_THAT(content, StrEq(string("\x02") + c + expect));
|
||||
|
||||
expect = STRING_FIELD_2 + FIX64_FIELD_3;
|
||||
c = (char) expect.size();
|
||||
EXPECT_TRUE(ReadFileToString(output2.path, &content));
|
||||
EXPECT_THAT(content, StrEq(string("\x02") + c + expect));
|
||||
|
||||
// because args3 doesn't set section, so it should receive nothing
|
||||
EXPECT_TRUE(ReadFileToString(output3.path, &content));
|
||||
EXPECT_THAT(content, StrEq(""));
|
||||
}
|
||||
|
||||
TEST(SectionTest, TestMultipleRequestsBySpec) {
|
||||
TemporaryFile input, output1, output2, output3;
|
||||
FileSection fs(NOOP_PARSER, input.path);
|
||||
ReportRequestSet requests;
|
||||
|
||||
ASSERT_TRUE(input.fd != -1);
|
||||
ASSERT_TRUE(output1.fd != -1);
|
||||
ASSERT_TRUE(output2.fd != -1);
|
||||
ASSERT_TRUE(output3.fd != -1);
|
||||
|
||||
ASSERT_TRUE(WriteStringToFile(VARINT_FIELD_1 + STRING_FIELD_2 + FIX64_FIELD_3, input.path, false));
|
||||
|
||||
IncidentReportArgs args1, args2, args3, args4;
|
||||
args1.setAll(true);
|
||||
args2.setAll(true);
|
||||
args4.setAll(true);
|
||||
sp<SimpleListener> l = new SimpleListener();
|
||||
requests.add(new ReportRequest(args1, l, output1.fd));
|
||||
requests.add(new ReportRequest(args2, l, output2.fd));
|
||||
requests.add(new ReportRequest(args3, l, output3.fd));
|
||||
requests.setMainFd(STDOUT_FILENO);
|
||||
|
||||
CaptureStdout();
|
||||
ASSERT_EQ(NO_ERROR, fs.Execute(&requests));
|
||||
EXPECT_THAT(GetCapturedStdout(), StrEq("\x02\r" + STRING_FIELD_2));
|
||||
|
||||
string content, expect;
|
||||
expect = STRING_FIELD_2 + FIX64_FIELD_3;
|
||||
char c = (char) expect.size();
|
||||
|
||||
// output1 and output2 are the same
|
||||
EXPECT_TRUE(ReadFileToString(output1.path, &content));
|
||||
EXPECT_THAT(content, StrEq(string("\x02") + c + expect));
|
||||
EXPECT_TRUE(ReadFileToString(output2.path, &content));
|
||||
EXPECT_THAT(content, StrEq(string("\x02") + c + expect));
|
||||
|
||||
// because args3 doesn't set section, so it should receive nothing
|
||||
EXPECT_TRUE(ReadFileToString(output3.path, &content));
|
||||
EXPECT_THAT(content, StrEq(""));
|
||||
}
|
||||
@@ -35,6 +35,7 @@ public final class IncidentReportArgs implements Parcelable {
|
||||
private final IntArray mSections = new IntArray();
|
||||
private final ArrayList<byte[]> mHeaders = new ArrayList<byte[]>();
|
||||
private boolean mAll;
|
||||
private int mDest;
|
||||
|
||||
/**
|
||||
* Construct an incident report args with no fields.
|
||||
@@ -69,6 +70,8 @@ public final class IncidentReportArgs implements Parcelable {
|
||||
for (int i=0; i<N; i++) {
|
||||
out.writeByteArray(mHeaders.get(i));
|
||||
}
|
||||
|
||||
out.writeInt(mDest);
|
||||
}
|
||||
|
||||
public void readFromParcel(Parcel in) {
|
||||
@@ -85,6 +88,8 @@ public final class IncidentReportArgs implements Parcelable {
|
||||
for (int i=0; i<N; i++) {
|
||||
mHeaders.add(in.createByteArray());
|
||||
}
|
||||
|
||||
mDest = in.readInt();
|
||||
}
|
||||
|
||||
public static final Parcelable.Creator<IncidentReportArgs> CREATOR
|
||||
@@ -118,7 +123,8 @@ public final class IncidentReportArgs implements Parcelable {
|
||||
}
|
||||
sb.append(", ");
|
||||
sb.append(mHeaders.size());
|
||||
sb.append(" headers)");
|
||||
sb.append(" headers), ");
|
||||
sb.append("Dest enum value: ").append(mDest);
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@@ -132,6 +138,14 @@ public final class IncidentReportArgs implements Parcelable {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set this incident report privacy policy spec.
|
||||
* @hide
|
||||
*/
|
||||
public void setPrivacyPolicy(int dest) {
|
||||
mDest = dest;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add this section to the incident report. Skip if the input is smaller than 2 since section
|
||||
* id are only valid for positive integer as Protobuf field id. Here 1 is reserved for Header.
|
||||
|
||||
@@ -31,22 +31,12 @@ import "frameworks/base/core/proto/android/service/package.proto";
|
||||
import "frameworks/base/core/proto/android/service/power.proto";
|
||||
import "frameworks/base/core/proto/android/service/print.proto";
|
||||
import "frameworks/base/core/proto/android/providers/settings.proto";
|
||||
import "frameworks/base/core/proto/android/os/incidentheader.proto";
|
||||
import "frameworks/base/core/proto/android/os/kernelwake.proto";
|
||||
import "frameworks/base/core/proto/android/os/procrank.proto";
|
||||
|
||||
package android.os;
|
||||
|
||||
message IncidentHeaderProto {
|
||||
enum Cause {
|
||||
CAUSE_UNKNOWN = 0;
|
||||
CAUSE_USER = 1;
|
||||
CAUSE_ANR = 2;
|
||||
CAUSE_CRASH = 3;
|
||||
}
|
||||
|
||||
Cause cause = 1;
|
||||
}
|
||||
|
||||
// privacy field options must not be set at this level because all
|
||||
// the sections are able to be controlled and configured by section ids.
|
||||
// Instead privacy field options need to be configured in each section proto message.
|
||||
|
||||
34
core/proto/android/os/incidentheader.proto
Normal file
34
core/proto/android/os/incidentheader.proto
Normal file
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
syntax = "proto3";
|
||||
|
||||
option java_multiple_files = true;
|
||||
option java_outer_classname = "IncidentHeaderProtoMetadata";
|
||||
|
||||
package android.os;
|
||||
|
||||
message IncidentHeaderProto {
|
||||
enum Cause {
|
||||
CAUSE_UNKNOWN = 0;
|
||||
CAUSE_USER = 1;
|
||||
CAUSE_ANR = 2;
|
||||
CAUSE_CRASH = 3;
|
||||
}
|
||||
|
||||
Cause cause = 1;
|
||||
}
|
||||
|
||||
@@ -39,12 +39,13 @@ public:
|
||||
virtual status_t readFromParcel(const Parcel* in);
|
||||
|
||||
void setAll(bool all);
|
||||
void setDest(int dest);
|
||||
void addSection(int section);
|
||||
void addHeader(const vector<int8_t>& header);
|
||||
|
||||
inline bool all() const { return mAll; };
|
||||
inline bool all() const { return mAll; }
|
||||
bool containsSection(int section) const;
|
||||
|
||||
inline int dest() const { return mDest; }
|
||||
inline const set<int>& sections() const { return mSections; }
|
||||
inline const vector<vector<int8_t>>& headers() const { return mHeaders; }
|
||||
|
||||
@@ -54,6 +55,7 @@ private:
|
||||
set<int> mSections;
|
||||
vector<vector<int8_t>> mHeaders;
|
||||
bool mAll;
|
||||
int mDest;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -25,14 +25,16 @@ namespace os {
|
||||
|
||||
IncidentReportArgs::IncidentReportArgs()
|
||||
:mSections(),
|
||||
mAll(false)
|
||||
mAll(false),
|
||||
mDest(-1)
|
||||
{
|
||||
}
|
||||
|
||||
IncidentReportArgs::IncidentReportArgs(const IncidentReportArgs& that)
|
||||
:mSections(that.mSections),
|
||||
mHeaders(that.mHeaders),
|
||||
mAll(that.mAll)
|
||||
mAll(that.mAll),
|
||||
mDest(that.mDest)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -74,6 +76,11 @@ IncidentReportArgs::writeToParcel(Parcel* out) const
|
||||
}
|
||||
}
|
||||
|
||||
err = out->writeInt32(mDest);
|
||||
if (err != NO_ERROR) {
|
||||
return err;
|
||||
}
|
||||
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
@@ -120,6 +127,13 @@ IncidentReportArgs::readFromParcel(const Parcel* in)
|
||||
}
|
||||
}
|
||||
|
||||
int32_t dest;
|
||||
err = in->readInt32(&dest);
|
||||
if (err != NO_ERROR) {
|
||||
return err;
|
||||
}
|
||||
mDest = dest;
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
@@ -132,6 +146,12 @@ IncidentReportArgs::setAll(bool all)
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
IncidentReportArgs::setDest(int dest)
|
||||
{
|
||||
mDest = dest;
|
||||
}
|
||||
|
||||
void
|
||||
IncidentReportArgs::addSection(int section)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user