blob: 8f62da202606ebb9cdabc1f759460826a857113e [file] [log] [blame]
Joe Onorato1754d742016-11-21 17:51:35 -08001/*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
Yi Jin4e843102018-02-14 15:36:18 -080016#define DEBUG false
Yi Jinb592e3b2018-02-01 15:17:04 -080017#include "Log.h"
Joe Onorato1754d742016-11-21 17:51:35 -080018
19#include "Reporter.h"
Joe Onorato1754d742016-11-21 17:51:35 -080020
Yi Jin329130b2018-02-09 16:47:47 -080021#include "Privacy.h"
Joe Onorato1754d742016-11-21 17:51:35 -080022#include "report_directory.h"
23#include "section_list.h"
24
Kweku Adams3d160912018-05-07 11:26:27 -070025#include <android-base/properties.h>
Joe Onorato1754d742016-11-21 17:51:35 -080026#include <android/os/DropBoxManager.h>
Yi Jin0a3406f2017-06-22 19:23:11 -070027#include <private/android_filesystem_config.h>
Joe Onorato1754d742016-11-21 17:51:35 -080028#include <utils/SystemClock.h>
29
Joe Onorato1754d742016-11-21 17:51:35 -080030#include <dirent.h>
Joe Onorato1754d742016-11-21 17:51:35 -080031#include <errno.h>
Yi Jinb592e3b2018-02-01 15:17:04 -080032#include <fcntl.h>
33#include <sys/stat.h>
34#include <sys/types.h>
Kweku Adams3d160912018-05-07 11:26:27 -070035#include <string>
Joe Onorato1754d742016-11-21 17:51:35 -080036
37/**
38 * The directory where the incident reports are stored.
39 */
Yi Jinadd11e92017-07-30 16:10:07 -070040static const char* INCIDENT_DIRECTORY = "/data/misc/incidents/";
Joe Onorato1754d742016-11-21 17:51:35 -080041
Yi Jin6cacbcb2018-03-30 14:04:52 -070042namespace android {
43namespace os {
44namespace incidentd {
45
Yi Jin0a3406f2017-06-22 19:23:11 -070046// ================================================================================
Joe Onorato1754d742016-11-21 17:51:35 -080047ReportRequest::ReportRequest(const IncidentReportArgs& a,
Yi Jinb592e3b2018-02-01 15:17:04 -080048 const sp<IIncidentReportStatusListener>& l, int f)
49 : args(a), listener(l), fd(f), err(NO_ERROR) {}
Joe Onorato1754d742016-11-21 17:51:35 -080050
Yi Jinb592e3b2018-02-01 15:17:04 -080051ReportRequest::~ReportRequest() {
Yi Jin22769e02017-10-16 14:42:50 -070052 if (fd >= 0) {
53 // clean up the opened file descriptor
54 close(fd);
55 }
Joe Onorato1754d742016-11-21 17:51:35 -080056}
57
Yi Jinb592e3b2018-02-01 15:17:04 -080058bool ReportRequest::ok() { return fd >= 0 && err == NO_ERROR; }
Yi Jinedfd5bb2017-09-06 17:09:11 -070059
Joe Onorato1754d742016-11-21 17:51:35 -080060// ================================================================================
61ReportRequestSet::ReportRequestSet()
Yi Jinb592e3b2018-02-01 15:17:04 -080062 : mRequests(), mSections(), mMainFd(-1), mMainDest(-1), mMetadata(), mSectionStats() {}
Joe Onorato1754d742016-11-21 17:51:35 -080063
Yi Jinb592e3b2018-02-01 15:17:04 -080064ReportRequestSet::~ReportRequestSet() {}
Joe Onorato1754d742016-11-21 17:51:35 -080065
Yi Jinadd11e92017-07-30 16:10:07 -070066// TODO: dedup on exact same args and fd, report the status back to listener!
Yi Jinb592e3b2018-02-01 15:17:04 -080067void ReportRequestSet::add(const sp<ReportRequest>& request) {
Joe Onorato1754d742016-11-21 17:51:35 -080068 mRequests.push_back(request);
Yi Jinadd11e92017-07-30 16:10:07 -070069 mSections.merge(request->args);
Yi Jin329130b2018-02-09 16:47:47 -080070 mMetadata.set_request_size(mMetadata.request_size() + 1);
Joe Onorato1754d742016-11-21 17:51:35 -080071}
72
Yi Jinb592e3b2018-02-01 15:17:04 -080073void ReportRequestSet::setMainFd(int fd) {
Joe Onorato1754d742016-11-21 17:51:35 -080074 mMainFd = fd;
Yi Jin329130b2018-02-09 16:47:47 -080075 mMetadata.set_use_dropbox(fd > 0);
Joe Onorato1754d742016-11-21 17:51:35 -080076}
77
Yi Jinb592e3b2018-02-01 15:17:04 -080078void ReportRequestSet::setMainDest(int dest) {
Yi Jin3ec5cc72018-01-26 13:42:43 -080079 mMainDest = dest;
Yi Jin329130b2018-02-09 16:47:47 -080080 PrivacySpec spec = PrivacySpec::new_spec(dest);
81 switch (spec.dest) {
82 case android::os::DEST_AUTOMATIC:
83 mMetadata.set_dest(IncidentMetadata_Destination_AUTOMATIC);
84 break;
85 case android::os::DEST_EXPLICIT:
86 mMetadata.set_dest(IncidentMetadata_Destination_EXPLICIT);
87 break;
88 case android::os::DEST_LOCAL:
89 mMetadata.set_dest(IncidentMetadata_Destination_LOCAL);
90 break;
91 }
Yi Jin3ec5cc72018-01-26 13:42:43 -080092}
93
Yi Jinb592e3b2018-02-01 15:17:04 -080094bool ReportRequestSet::containsSection(int id) { return mSections.containsSection(id); }
Joe Onorato1754d742016-11-21 17:51:35 -080095
Yi Jinb592e3b2018-02-01 15:17:04 -080096IncidentMetadata::SectionStats* ReportRequestSet::sectionStats(int id) {
Yi Jin329130b2018-02-09 16:47:47 -080097 if (mSectionStats.find(id) == mSectionStats.end()) {
Yi Jin86dce412018-03-07 11:36:57 -080098 IncidentMetadata::SectionStats stats;
99 stats.set_id(id);
Yi Jin329130b2018-02-09 16:47:47 -0800100 mSectionStats[id] = stats;
101 }
Yi Jin86dce412018-03-07 11:36:57 -0800102 return &mSectionStats[id];
Yi Jin329130b2018-02-09 16:47:47 -0800103}
104
Joe Onorato1754d742016-11-21 17:51:35 -0800105// ================================================================================
Yi Jinadd11e92017-07-30 16:10:07 -0700106Reporter::Reporter() : Reporter(INCIDENT_DIRECTORY) { isTest = false; };
107
Yi Jinb592e3b2018-02-01 15:17:04 -0800108Reporter::Reporter(const char* directory) : batch() {
Joe Onorato1754d742016-11-21 17:51:35 -0800109 char buf[100];
110
Yi Jin91d43302018-04-12 11:06:57 -0700111 mMaxSize = 30 * 1024 * 1024; // incident reports can take up to 30MB on disk
Joe Onorato1754d742016-11-21 17:51:35 -0800112 mMaxCount = 100;
113
Yi Jinadd11e92017-07-30 16:10:07 -0700114 // string ends up with '/' is a directory
115 String8 dir = String8(directory);
116 if (directory[dir.size() - 1] != '/') dir += "/";
117 mIncidentDirectory = dir.string();
118
Joe Onorato1754d742016-11-21 17:51:35 -0800119 // There can't be two at the same time because it's on one thread.
120 mStartTime = time(NULL);
Yi Jinadd11e92017-07-30 16:10:07 -0700121 strftime(buf, sizeof(buf), "incident-%Y%m%d-%H%M%S", localtime(&mStartTime));
122 mFilename = mIncidentDirectory + buf;
Joe Onorato1754d742016-11-21 17:51:35 -0800123}
124
Yi Jinb592e3b2018-02-01 15:17:04 -0800125Reporter::~Reporter() {}
Joe Onorato1754d742016-11-21 17:51:35 -0800126
Yi Jin4e843102018-02-14 15:36:18 -0800127Reporter::run_report_status_t Reporter::runReport(size_t* reportByteSize) {
Joe Onorato1754d742016-11-21 17:51:35 -0800128 status_t err = NO_ERROR;
129 bool needMainFd = false;
130 int mainFd = -1;
Yi Jin3ec5cc72018-01-26 13:42:43 -0800131 int mainDest = -1;
Mike Ma28381692018-12-04 15:46:29 -0800132 int sectionCount = 0;
Yi Jinedfd5bb2017-09-06 17:09:11 -0700133 HeaderSection headers;
Yi Jin329130b2018-02-09 16:47:47 -0800134 MetadataSection metadataSection;
Kweku Adams3d160912018-05-07 11:26:27 -0700135 std::string buildType = android::base::GetProperty("ro.build.type", "");
136 const bool isUserdebugOrEng = buildType == "userdebug" || buildType == "eng";
Joe Onorato1754d742016-11-21 17:51:35 -0800137
138 // See if we need the main file
Yi Jinb592e3b2018-02-01 15:17:04 -0800139 for (ReportRequestSet::iterator it = batch.begin(); it != batch.end(); it++) {
Joe Onorato1754d742016-11-21 17:51:35 -0800140 if ((*it)->fd < 0 && mainFd < 0) {
141 needMainFd = true;
Yi Jin3ec5cc72018-01-26 13:42:43 -0800142 mainDest = (*it)->args.dest();
Joe Onorato1754d742016-11-21 17:51:35 -0800143 break;
144 }
145 }
146 if (needMainFd) {
147 // Create the directory
Yi Jinadd11e92017-07-30 16:10:07 -0700148 if (!isTest) err = create_directory(mIncidentDirectory);
Joe Onorato1754d742016-11-21 17:51:35 -0800149 if (err != NO_ERROR) {
Yi Jinedfd5bb2017-09-06 17:09:11 -0700150 goto DONE;
Joe Onorato1754d742016-11-21 17:51:35 -0800151 }
152
153 // If there are too many files in the directory (for whatever reason),
154 // delete the oldest ones until it's under the limit. Doing this first
155 // does mean that we can go over, so the max size is not a hard limit.
Yi Jinadd11e92017-07-30 16:10:07 -0700156 if (!isTest) clean_directory(mIncidentDirectory, mMaxSize, mMaxCount);
Joe Onorato1754d742016-11-21 17:51:35 -0800157
158 // Open the file.
159 err = create_file(&mainFd);
160 if (err != NO_ERROR) {
Yi Jinedfd5bb2017-09-06 17:09:11 -0700161 goto DONE;
Joe Onorato1754d742016-11-21 17:51:35 -0800162 }
163
164 // Add to the set
165 batch.setMainFd(mainFd);
Yi Jin3ec5cc72018-01-26 13:42:43 -0800166 batch.setMainDest(mainDest);
Joe Onorato1754d742016-11-21 17:51:35 -0800167 }
168
169 // Tell everyone that we're starting.
Yi Jinb592e3b2018-02-01 15:17:04 -0800170 for (ReportRequestSet::iterator it = batch.begin(); it != batch.end(); it++) {
Joe Onorato1754d742016-11-21 17:51:35 -0800171 if ((*it)->listener != NULL) {
172 (*it)->listener->onReportStarted();
173 }
174 }
175
176 // Write the incident headers
Yi Jinedfd5bb2017-09-06 17:09:11 -0700177 headers.Execute(&batch);
Joe Onorato1754d742016-11-21 17:51:35 -0800178
179 // For each of the report fields, see if we need it, and if so, execute the command
180 // and report to those that care that we're doing it.
Yi Jinb592e3b2018-02-01 15:17:04 -0800181 for (const Section** section = SECTION_LIST; *section; section++) {
Joe Onorato1754d742016-11-21 17:51:35 -0800182 const int id = (*section)->id;
Kweku Adams3d160912018-05-07 11:26:27 -0700183 if ((*section)->userdebugAndEngOnly && !isUserdebugOrEng) {
Mike Ma28381692018-12-04 15:46:29 -0800184 VLOG("Skipping incident report section %d '%s' because it's limited to userdebug/eng",
Kweku Adams3d160912018-05-07 11:26:27 -0700185 id, (*section)->name.string());
186 continue;
187 }
Yi Jinadd11e92017-07-30 16:10:07 -0700188 if (this->batch.containsSection(id)) {
Mike Ma28381692018-12-04 15:46:29 -0800189 VLOG("Taking incident report section %d '%s'", id, (*section)->name.string());
Yi Jinb592e3b2018-02-01 15:17:04 -0800190 for (ReportRequestSet::iterator it = batch.begin(); it != batch.end(); it++) {
Joe Onorato1754d742016-11-21 17:51:35 -0800191 if ((*it)->listener != NULL && (*it)->args.containsSection(id)) {
Yi Jinb592e3b2018-02-01 15:17:04 -0800192 (*it)->listener->onReportSectionStatus(
193 id, IIncidentReportStatusListener::STATUS_STARTING);
Joe Onorato1754d742016-11-21 17:51:35 -0800194 }
195 }
196
197 // Execute - go get the data and write it into the file descriptors.
Yi Jin86dce412018-03-07 11:36:57 -0800198 IncidentMetadata::SectionStats* stats = batch.sectionStats(id);
Yi Jin329130b2018-02-09 16:47:47 -0800199 int64_t startTime = uptimeMillis();
Joe Onorato1754d742016-11-21 17:51:35 -0800200 err = (*section)->Execute(&batch);
Yi Jin329130b2018-02-09 16:47:47 -0800201 int64_t endTime = uptimeMillis();
Yi Jin329130b2018-02-09 16:47:47 -0800202 stats->set_exec_duration_ms(endTime - startTime);
Joe Onorato1754d742016-11-21 17:51:35 -0800203 if (err != NO_ERROR) {
Yi Jina5c5e8a2017-09-27 18:24:58 -0700204 ALOGW("Incident section %s (%d) failed: %s. Stopping report.",
Yi Jinb592e3b2018-02-01 15:17:04 -0800205 (*section)->name.string(), id, strerror(-err));
Mike Ma28381692018-12-04 15:46:29 -0800206 // Execute() has already recorded this status. Only update if there's new failure.
207 stats->set_success(false);
Yi Jinedfd5bb2017-09-06 17:09:11 -0700208 goto DONE;
Joe Onorato1754d742016-11-21 17:51:35 -0800209 }
Yi Jin4e843102018-02-14 15:36:18 -0800210 (*reportByteSize) += stats->report_size_bytes();
Joe Onorato1754d742016-11-21 17:51:35 -0800211
Yi Jinb592e3b2018-02-01 15:17:04 -0800212 // Notify listener of starting
213 for (ReportRequestSet::iterator it = batch.begin(); it != batch.end(); it++) {
Joe Onorato1754d742016-11-21 17:51:35 -0800214 if ((*it)->listener != NULL && (*it)->args.containsSection(id)) {
Yi Jinb592e3b2018-02-01 15:17:04 -0800215 (*it)->listener->onReportSectionStatus(
216 id, IIncidentReportStatusListener::STATUS_FINISHED);
Joe Onorato1754d742016-11-21 17:51:35 -0800217 }
218 }
Mike Ma28381692018-12-04 15:46:29 -0800219 VLOG("Finish incident report section %d '%s'", id, (*section)->name.string());
220 sectionCount++;
Joe Onorato1754d742016-11-21 17:51:35 -0800221 }
222 }
223
Yi Jinedfd5bb2017-09-06 17:09:11 -0700224DONE:
Mike Ma28381692018-12-04 15:46:29 -0800225 ALOGD("Incident reporting took %d sections.", sectionCount);
Yi Jin329130b2018-02-09 16:47:47 -0800226 // Reports the metdadata when taking the incident report.
227 if (!isTest) metadataSection.Execute(&batch);
228
Joe Onorato1754d742016-11-21 17:51:35 -0800229 // Close the file.
230 if (mainFd >= 0) {
231 close(mainFd);
232 }
233
234 // Tell everyone that we're done.
Yi Jinb592e3b2018-02-01 15:17:04 -0800235 for (ReportRequestSet::iterator it = batch.begin(); it != batch.end(); it++) {
Joe Onorato1754d742016-11-21 17:51:35 -0800236 if ((*it)->listener != NULL) {
237 if (err == NO_ERROR) {
238 (*it)->listener->onReportFinished();
239 } else {
240 (*it)->listener->onReportFailed();
241 }
242 }
243 }
244
245 // Put the report into dropbox.
246 if (needMainFd && err == NO_ERROR) {
247 sp<DropBoxManager> dropbox = new DropBoxManager();
248 Status status = dropbox->addFile(String16("incident"), mFilename, 0);
249 ALOGD("Incident report done. dropbox status=%s\n", status.toString8().string());
250 if (!status.isOk()) {
251 return REPORT_NEEDS_DROPBOX;
252 }
253
254 // If the status was ok, delete the file. If not, leave it around until the next
255 // boot or the next checkin. If the directory gets too big older files will
256 // be rotated out.
Yi Jinb592e3b2018-02-01 15:17:04 -0800257 if (!isTest) unlink(mFilename.c_str());
Joe Onorato1754d742016-11-21 17:51:35 -0800258 }
259
260 return REPORT_FINISHED;
261}
262
263/**
264 * Create our output file and set the access permissions to -rw-rw----
265 */
Yi Jinb592e3b2018-02-01 15:17:04 -0800266status_t Reporter::create_file(int* fd) {
Joe Onorato1754d742016-11-21 17:51:35 -0800267 const char* filename = mFilename.c_str();
268
Yi Jinadd11e92017-07-30 16:10:07 -0700269 *fd = open(filename, O_CREAT | O_TRUNC | O_RDWR | O_CLOEXEC, 0660);
Joe Onorato1754d742016-11-21 17:51:35 -0800270 if (*fd < 0) {
271 ALOGE("Couldn't open incident file: %s (%s)", filename, strerror(errno));
272 return -errno;
273 }
274
275 // Override umask. Not super critical. If it fails go on with life.
276 chmod(filename, 0660);
277
Yi Jin4bab3a12018-01-10 16:50:59 -0800278 if (chown(filename, AID_INCIDENTD, AID_INCIDENTD)) {
Joe Onorato1754d742016-11-21 17:51:35 -0800279 ALOGE("Unable to change ownership of incident file %s: %s\n", filename, strerror(errno));
280 status_t err = -errno;
281 unlink(mFilename.c_str());
282 return err;
283 }
284
285 return NO_ERROR;
286}
287
Yi Jinb592e3b2018-02-01 15:17:04 -0800288Reporter::run_report_status_t Reporter::upload_backlog() {
Joe Onorato1754d742016-11-21 17:51:35 -0800289 DIR* dir;
290 struct dirent* entry;
291 struct stat st;
Yi Jinadd11e92017-07-30 16:10:07 -0700292 status_t err;
Joe Onorato1754d742016-11-21 17:51:35 -0800293
Yi Jinf32af482017-08-11 15:00:49 -0700294 ALOGD("Start uploading backlogs in %s", INCIDENT_DIRECTORY);
Yi Jinadd11e92017-07-30 16:10:07 -0700295 if ((err = create_directory(INCIDENT_DIRECTORY)) != NO_ERROR) {
296 ALOGE("directory doesn't exist: %s", strerror(-err));
297 return REPORT_FINISHED;
298 }
299
300 if ((dir = opendir(INCIDENT_DIRECTORY)) == NULL) {
301 ALOGE("Couldn't open incident directory: %s", INCIDENT_DIRECTORY);
Joe Onorato1754d742016-11-21 17:51:35 -0800302 return REPORT_NEEDS_DROPBOX;
303 }
304
Joe Onorato1754d742016-11-21 17:51:35 -0800305 sp<DropBoxManager> dropbox = new DropBoxManager();
306
307 // Enumerate, count and add up size
Yi Jinf32af482017-08-11 15:00:49 -0700308 int count = 0;
Joe Onorato1754d742016-11-21 17:51:35 -0800309 while ((entry = readdir(dir)) != NULL) {
310 if (entry->d_name[0] == '.') {
311 continue;
312 }
Yi Jinadd11e92017-07-30 16:10:07 -0700313 String8 filename = String8(INCIDENT_DIRECTORY) + entry->d_name;
Joe Onorato1754d742016-11-21 17:51:35 -0800314 if (stat(filename.string(), &st) != 0) {
315 ALOGE("Unable to stat file %s", filename.string());
316 continue;
317 }
318 if (!S_ISREG(st.st_mode)) {
319 continue;
320 }
321
322 Status status = dropbox->addFile(String16("incident"), filename.string(), 0);
323 ALOGD("Incident report done. dropbox status=%s\n", status.toString8().string());
324 if (!status.isOk()) {
325 return REPORT_NEEDS_DROPBOX;
326 }
327
328 // If the status was ok, delete the file. If not, leave it around until the next
329 // boot or the next checkin. If the directory gets too big older files will
330 // be rotated out.
331 unlink(filename.string());
Yi Jinf32af482017-08-11 15:00:49 -0700332 count++;
Joe Onorato1754d742016-11-21 17:51:35 -0800333 }
Yi Jinf32af482017-08-11 15:00:49 -0700334 ALOGD("Successfully uploaded %d files to Dropbox.", count);
Joe Onorato1754d742016-11-21 17:51:35 -0800335 closedir(dir);
336
337 return REPORT_FINISHED;
338}
Yi Jin6cacbcb2018-03-30 14:04:52 -0700339
340} // namespace incidentd
341} // namespace os
342} // namespace android