summaryrefslogtreecommitdiff
path: root/cmds/incidentd/src/Reporter.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'cmds/incidentd/src/Reporter.cpp')
-rw-r--r--cmds/incidentd/src/Reporter.cpp256
1 files changed, 113 insertions, 143 deletions
diff --git a/cmds/incidentd/src/Reporter.cpp b/cmds/incidentd/src/Reporter.cpp
index 1ecb291c84a1..12764f8ff09c 100644
--- a/cmds/incidentd/src/Reporter.cpp
+++ b/cmds/incidentd/src/Reporter.cpp
@@ -13,242 +13,210 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
-#define LOG_TAG "incidentd"
+#define DEBUG false
+#include "Log.h"
#include "Reporter.h"
-#include "protobuf.h"
+#include "Privacy.h"
#include "report_directory.h"
#include "section_list.h"
-#include <private/android_filesystem_config.h>
#include <android/os/DropBoxManager.h>
+#include <private/android_filesystem_config.h>
#include <utils/SystemClock.h>
-#include <sys/types.h>
-#include <sys/stat.h>
#include <dirent.h>
-#include <fcntl.h>
#include <errno.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
/**
* The directory where the incident reports are stored.
*/
-static const String8 INCIDENT_DIRECTORY("/data/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;
-}
+static const char* INCIDENT_DIRECTORY = "/data/misc/incidents/";
// ================================================================================
ReportRequest::ReportRequest(const IncidentReportArgs& a,
- const sp<IIncidentReportStatusListener> &l, int f)
- :args(a),
- listener(l),
- fd(f),
- err(NO_ERROR)
-{
-}
+ const sp<IIncidentReportStatusListener>& l, int f)
+ : args(a), listener(l), fd(f), err(NO_ERROR) {}
-ReportRequest::~ReportRequest()
-{
+ReportRequest::~ReportRequest() {
+ if (fd >= 0) {
+ // clean up the opened file descriptor
+ close(fd);
+ }
}
+bool ReportRequest::ok() { return fd >= 0 && err == NO_ERROR; }
+
// ================================================================================
ReportRequestSet::ReportRequestSet()
- :mRequests(),
- mWritableCount(0),
- mMainFd(-1)
-{
-}
+ : mRequests(), mSections(), mMainFd(-1), mMainDest(-1), mMetadata(), mSectionStats() {}
-ReportRequestSet::~ReportRequestSet()
-{
-}
+ReportRequestSet::~ReportRequestSet() {}
-void
-ReportRequestSet::add(const sp<ReportRequest>& request)
-{
+// TODO: dedup on exact same args and fd, report the status back to listener!
+void ReportRequestSet::add(const sp<ReportRequest>& request) {
mRequests.push_back(request);
- mWritableCount++;
+ mSections.merge(request->args);
+ mMetadata.set_request_size(mMetadata.request_size() + 1);
}
-void
-ReportRequestSet::setMainFd(int fd)
-{
+void ReportRequestSet::setMainFd(int fd) {
mMainFd = fd;
- mWritableCount++;
+ mMetadata.set_use_dropbox(fd > 0);
}
-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--;
- }
- }
+void ReportRequestSet::setMainDest(int dest) {
+ mMainDest = dest;
+ PrivacySpec spec = PrivacySpec::new_spec(dest);
+ switch (spec.dest) {
+ case android::os::DEST_AUTOMATIC:
+ mMetadata.set_dest(IncidentMetadata_Destination_AUTOMATIC);
+ break;
+ case android::os::DEST_EXPLICIT:
+ mMetadata.set_dest(IncidentMetadata_Destination_EXPLICIT);
+ break;
+ case android::os::DEST_LOCAL:
+ mMetadata.set_dest(IncidentMetadata_Destination_LOCAL);
+ break;
}
+}
- // The dropbox file
- if (mMainFd >= 0) {
- err = write_all(mMainFd, buf, size);
- if (err != NO_ERROR) {
- mMainFd = -1;
- mWritableCount--;
- }
- }
+bool ReportRequestSet::containsSection(int id) { return mSections.containsSection(id); }
- // Return an error only when there are no FDs to write.
- return mWritableCount > 0 ? NO_ERROR : err;
+IncidentMetadata::SectionStats* ReportRequestSet::sectionStats(int id) {
+ if (mSectionStats.find(id) == mSectionStats.end()) {
+ auto stats = mMetadata.add_sections();
+ stats->set_id(id);
+ mSectionStats[id] = stats;
+ }
+ return mSectionStats[id];
}
-
// ================================================================================
-Reporter::Reporter()
- :args(),
- batch()
-{
+Reporter::Reporter() : Reporter(INCIDENT_DIRECTORY) { isTest = false; };
+
+Reporter::Reporter(const char* directory) : batch() {
char buf[100];
// TODO: Make the max size smaller for user builds.
mMaxSize = 100 * 1024 * 1024;
mMaxCount = 100;
+ // string ends up with '/' is a directory
+ String8 dir = String8(directory);
+ if (directory[dir.size() - 1] != '/') dir += "/";
+ mIncidentDirectory = dir.string();
+
// There can't be two at the same time because it's on one thread.
mStartTime = time(NULL);
- strftime(buf, sizeof(buf), "/incident-%Y%m%d-%H%M%S", localtime(&mStartTime));
- mFilename = INCIDENT_DIRECTORY + buf;
-}
-
-Reporter::~Reporter()
-{
+ strftime(buf, sizeof(buf), "incident-%Y%m%d-%H%M%S", localtime(&mStartTime));
+ mFilename = mIncidentDirectory + buf;
}
-Reporter::run_report_status_t
-Reporter::runReport()
-{
+Reporter::~Reporter() {}
+Reporter::run_report_status_t Reporter::runReport(size_t* reportByteSize) {
status_t err = NO_ERROR;
bool needMainFd = false;
int mainFd = -1;
+ int mainDest = -1;
+ HeaderSection headers;
+ MetadataSection metadataSection;
// See if we need the main file
- for (ReportRequestSet::iterator it=batch.begin(); it!=batch.end(); it++) {
+ for (ReportRequestSet::iterator it = batch.begin(); it != batch.end(); it++) {
if ((*it)->fd < 0 && mainFd < 0) {
needMainFd = true;
+ mainDest = (*it)->args.dest();
break;
}
}
if (needMainFd) {
// Create the directory
- err = create_directory(INCIDENT_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),
// delete the oldest ones until it's under the limit. Doing this first
// does mean that we can go over, so the max size is not a hard limit.
- clean_directory(INCIDENT_DIRECTORY, mMaxSize, mMaxCount);
+ if (!isTest) clean_directory(mIncidentDirectory, mMaxSize, mMaxCount);
// Open the file.
err = create_file(&mainFd);
if (err != NO_ERROR) {
- goto done;
+ goto DONE;
}
// Add to the set
batch.setMainFd(mainFd);
+ batch.setMainDest(mainDest);
}
// Tell everyone that we're starting.
- for (ReportRequestSet::iterator it=batch.begin(); it!=batch.end(); it++) {
+ for (ReportRequestSet::iterator it = batch.begin(); it != batch.end(); it++) {
if ((*it)->listener != NULL) {
(*it)->listener->onReportStarted();
}
}
// 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.
- for (const Section** section=SECTION_LIST; *section; section++) {
+ for (const Section** section = SECTION_LIST; *section; section++) {
const int id = (*section)->id;
- ALOGD("Taking incident report section %d '%s'", id, (*section)->name.string());
-
- if (this->args.containsSection(id)) {
- // Notify listener of starting
- for (ReportRequestSet::iterator it=batch.begin(); it!=batch.end(); it++) {
+ if (this->batch.containsSection(id)) {
+ ALOGD("Taking incident report section %d '%s'", id, (*section)->name.string());
+ for (ReportRequestSet::iterator it = batch.begin(); it != batch.end(); it++) {
if ((*it)->listener != NULL && (*it)->args.containsSection(id)) {
- (*it)->listener->onReportSectionStatus(id,
- IIncidentReportStatusListener::STATUS_STARTING);
+ (*it)->listener->onReportSectionStatus(
+ id, IIncidentReportStatusListener::STATUS_STARTING);
}
}
// Execute - go get the data and write it into the file descriptors.
+ auto stats = batch.sectionStats(id);
+ int64_t startTime = uptimeMillis();
err = (*section)->Execute(&batch);
+ int64_t endTime = uptimeMillis();
+ stats->set_success(err == NO_ERROR);
+ stats->set_exec_duration_ms(endTime - startTime);
if (err != NO_ERROR) {
- ALOGW("Incident section %s (%d) failed. Stopping report.",
- (*section)->name.string(), id);
- goto done;
+ ALOGW("Incident section %s (%d) failed: %s. Stopping report.",
+ (*section)->name.string(), id, strerror(-err));
+ goto DONE;
}
+ (*reportByteSize) += stats->report_size_bytes();
// Notify listener of starting
- for (ReportRequestSet::iterator it=batch.begin(); it!=batch.end(); it++) {
+ for (ReportRequestSet::iterator it = batch.begin(); it != batch.end(); it++) {
if ((*it)->listener != NULL && (*it)->args.containsSection(id)) {
- (*it)->listener->onReportSectionStatus(id,
- IIncidentReportStatusListener::STATUS_FINISHED);
+ (*it)->listener->onReportSectionStatus(
+ id, IIncidentReportStatusListener::STATUS_FINISHED);
}
}
+ ALOGD("Finish incident report section %d '%s'", id, (*section)->name.string());
}
}
-done:
+DONE:
+ // Reports the metdadata when taking the incident report.
+ if (!isTest) metadataSection.Execute(&batch);
+
// Close the file.
if (mainFd >= 0) {
close(mainFd);
}
// Tell everyone that we're done.
- for (ReportRequestSet::iterator it=batch.begin(); it!=batch.end(); it++) {
+ for (ReportRequestSet::iterator it = batch.begin(); it != batch.end(); it++) {
if ((*it)->listener != NULL) {
if (err == NO_ERROR) {
(*it)->listener->onReportFinished();
@@ -270,7 +238,7 @@ done:
// If the status was ok, delete the file. If not, leave it around until the next
// boot or the next checkin. If the directory gets too big older files will
// be rotated out.
- unlink(mFilename.c_str());
+ if (!isTest) unlink(mFilename.c_str());
}
return REPORT_FINISHED;
@@ -279,12 +247,10 @@ done:
/**
* Create our output file and set the access permissions to -rw-rw----
*/
-status_t
-Reporter::create_file(int* fd)
-{
+status_t Reporter::create_file(int* fd) {
const char* filename = mFilename.c_str();
- *fd = open(filename, O_CREAT | O_TRUNC | O_RDWR, 0660);
+ *fd = open(filename, O_CREAT | O_TRUNC | O_RDWR | O_CLOEXEC, 0660);
if (*fd < 0) {
ALOGE("Couldn't open incident file: %s (%s)", filename, strerror(errno));
return -errno;
@@ -293,7 +259,7 @@ Reporter::create_file(int* fd)
// Override umask. Not super critical. If it fails go on with life.
chmod(filename, 0660);
- if (chown(filename, AID_SYSTEM, AID_SYSTEM)) {
+ if (chown(filename, AID_INCIDENTD, AID_INCIDENTD)) {
ALOGE("Unable to change ownership of incident file %s: %s\n", filename, strerror(errno));
status_t err = -errno;
unlink(mFilename.c_str());
@@ -303,28 +269,32 @@ Reporter::create_file(int* fd)
return NO_ERROR;
}
-// ================================================================================
-Reporter::run_report_status_t
-Reporter::upload_backlog()
-{
+Reporter::run_report_status_t Reporter::upload_backlog() {
DIR* dir;
struct dirent* entry;
struct stat st;
+ status_t err;
+
+ ALOGD("Start uploading backlogs in %s", INCIDENT_DIRECTORY);
+ if ((err = create_directory(INCIDENT_DIRECTORY)) != NO_ERROR) {
+ ALOGE("directory doesn't exist: %s", strerror(-err));
+ return REPORT_FINISHED;
+ }
- if ((dir = opendir(INCIDENT_DIRECTORY.string())) == NULL) {
- ALOGE("Couldn't open incident directory: %s", INCIDENT_DIRECTORY.string());
+ if ((dir = opendir(INCIDENT_DIRECTORY)) == NULL) {
+ ALOGE("Couldn't open incident directory: %s", INCIDENT_DIRECTORY);
return REPORT_NEEDS_DROPBOX;
}
- String8 dirbase(INCIDENT_DIRECTORY + "/");
sp<DropBoxManager> dropbox = new DropBoxManager();
// Enumerate, count and add up size
+ int count = 0;
while ((entry = readdir(dir)) != NULL) {
if (entry->d_name[0] == '.') {
continue;
}
- String8 filename = dirbase + entry->d_name;
+ String8 filename = String8(INCIDENT_DIRECTORY) + entry->d_name;
if (stat(filename.string(), &st) != 0) {
ALOGE("Unable to stat file %s", filename.string());
continue;
@@ -343,10 +313,10 @@ Reporter::upload_backlog()
// boot or the next checkin. If the directory gets too big older files will
// be rotated out.
unlink(filename.string());
+ count++;
}
-
+ ALOGD("Successfully uploaded %d files to Dropbox.", count);
closedir(dir);
return REPORT_FINISHED;
}
-