blob: 06baebaf3a4cc6b0e8c8e817d390798036c3af9d [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 */
16
17#define LOG_TAG "incidentd"
18
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
Joe Onorato1754d742016-11-21 17:51:35 -080025#include <android/os/DropBoxManager.h>
Yi Jin0a3406f2017-06-22 19:23:11 -070026#include <private/android_filesystem_config.h>
Joe Onorato1754d742016-11-21 17:51:35 -080027#include <utils/SystemClock.h>
28
29#include <sys/types.h>
30#include <sys/stat.h>
31#include <dirent.h>
32#include <fcntl.h>
33#include <errno.h>
34
35/**
36 * The directory where the incident reports are stored.
37 */
Yi Jinadd11e92017-07-30 16:10:07 -070038static const char* INCIDENT_DIRECTORY = "/data/misc/incidents/";
Joe Onorato1754d742016-11-21 17:51:35 -080039
Yi Jin0a3406f2017-06-22 19:23:11 -070040// ================================================================================
Joe Onorato1754d742016-11-21 17:51:35 -080041ReportRequest::ReportRequest(const IncidentReportArgs& a,
42 const sp<IIncidentReportStatusListener> &l, int f)
43 :args(a),
44 listener(l),
45 fd(f),
46 err(NO_ERROR)
47{
48}
49
50ReportRequest::~ReportRequest()
51{
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 Jinedfd5bb2017-09-06 17:09:11 -070058bool
59ReportRequest::ok()
60{
61 return fd >= 0 && err == NO_ERROR;
62}
63
Joe Onorato1754d742016-11-21 17:51:35 -080064// ================================================================================
65ReportRequestSet::ReportRequestSet()
66 :mRequests(),
Yi Jinadd11e92017-07-30 16:10:07 -070067 mSections(),
Yi Jin3ec5cc72018-01-26 13:42:43 -080068 mMainFd(-1),
Yi Jin329130b2018-02-09 16:47:47 -080069 mMainDest(-1),
70 mMetadata(),
71 mSectionStats()
Joe Onorato1754d742016-11-21 17:51:35 -080072{
73}
74
75ReportRequestSet::~ReportRequestSet()
76{
77}
78
Yi Jinadd11e92017-07-30 16:10:07 -070079// TODO: dedup on exact same args and fd, report the status back to listener!
Joe Onorato1754d742016-11-21 17:51:35 -080080void
81ReportRequestSet::add(const sp<ReportRequest>& request)
82{
83 mRequests.push_back(request);
Yi Jinadd11e92017-07-30 16:10:07 -070084 mSections.merge(request->args);
Yi Jin329130b2018-02-09 16:47:47 -080085 mMetadata.set_request_size(mMetadata.request_size() + 1);
Joe Onorato1754d742016-11-21 17:51:35 -080086}
87
88void
89ReportRequestSet::setMainFd(int fd)
90{
91 mMainFd = fd;
Yi Jin329130b2018-02-09 16:47:47 -080092 mMetadata.set_use_dropbox(fd > 0);
Joe Onorato1754d742016-11-21 17:51:35 -080093}
94
Yi Jin3ec5cc72018-01-26 13:42:43 -080095void
96ReportRequestSet::setMainDest(int dest)
97{
98 mMainDest = dest;
Yi Jin329130b2018-02-09 16:47:47 -080099 PrivacySpec spec = PrivacySpec::new_spec(dest);
100 switch (spec.dest) {
101 case android::os::DEST_AUTOMATIC:
102 mMetadata.set_dest(IncidentMetadata_Destination_AUTOMATIC);
103 break;
104 case android::os::DEST_EXPLICIT:
105 mMetadata.set_dest(IncidentMetadata_Destination_EXPLICIT);
106 break;
107 case android::os::DEST_LOCAL:
108 mMetadata.set_dest(IncidentMetadata_Destination_LOCAL);
109 break;
110 }
Yi Jin3ec5cc72018-01-26 13:42:43 -0800111}
112
Yi Jinadd11e92017-07-30 16:10:07 -0700113bool
114ReportRequestSet::containsSection(int id) {
115 return mSections.containsSection(id);
116}
Joe Onorato1754d742016-11-21 17:51:35 -0800117
Yi Jin329130b2018-02-09 16:47:47 -0800118IncidentMetadata::SectionStats*
119ReportRequestSet::sectionStats(int id) {
120 if (mSectionStats.find(id) == mSectionStats.end()) {
121 auto stats = mMetadata.add_sections();
122 stats->set_id(id);
123 mSectionStats[id] = stats;
124 }
125 return mSectionStats[id];
126}
127
Joe Onorato1754d742016-11-21 17:51:35 -0800128// ================================================================================
Yi Jinadd11e92017-07-30 16:10:07 -0700129Reporter::Reporter() : Reporter(INCIDENT_DIRECTORY) { isTest = false; };
130
131Reporter::Reporter(const char* directory)
132 :batch()
Joe Onorato1754d742016-11-21 17:51:35 -0800133{
134 char buf[100];
135
136 // TODO: Make the max size smaller for user builds.
137 mMaxSize = 100 * 1024 * 1024;
138 mMaxCount = 100;
139
Yi Jinadd11e92017-07-30 16:10:07 -0700140 // string ends up with '/' is a directory
141 String8 dir = String8(directory);
142 if (directory[dir.size() - 1] != '/') dir += "/";
143 mIncidentDirectory = dir.string();
144
Joe Onorato1754d742016-11-21 17:51:35 -0800145 // There can't be two at the same time because it's on one thread.
146 mStartTime = time(NULL);
Yi Jinadd11e92017-07-30 16:10:07 -0700147 strftime(buf, sizeof(buf), "incident-%Y%m%d-%H%M%S", localtime(&mStartTime));
148 mFilename = mIncidentDirectory + buf;
Joe Onorato1754d742016-11-21 17:51:35 -0800149}
150
151Reporter::~Reporter()
152{
153}
154
155Reporter::run_report_status_t
156Reporter::runReport()
157{
Joe Onorato1754d742016-11-21 17:51:35 -0800158 status_t err = NO_ERROR;
159 bool needMainFd = false;
160 int mainFd = -1;
Yi Jin3ec5cc72018-01-26 13:42:43 -0800161 int mainDest = -1;
Yi Jinedfd5bb2017-09-06 17:09:11 -0700162 HeaderSection headers;
Yi Jin329130b2018-02-09 16:47:47 -0800163 MetadataSection metadataSection;
Joe Onorato1754d742016-11-21 17:51:35 -0800164
165 // See if we need the main file
166 for (ReportRequestSet::iterator it=batch.begin(); it!=batch.end(); it++) {
167 if ((*it)->fd < 0 && mainFd < 0) {
168 needMainFd = true;
Yi Jin3ec5cc72018-01-26 13:42:43 -0800169 mainDest = (*it)->args.dest();
Joe Onorato1754d742016-11-21 17:51:35 -0800170 break;
171 }
172 }
173 if (needMainFd) {
174 // Create the directory
Yi Jinadd11e92017-07-30 16:10:07 -0700175 if (!isTest) err = create_directory(mIncidentDirectory);
Joe Onorato1754d742016-11-21 17:51:35 -0800176 if (err != NO_ERROR) {
Yi Jinedfd5bb2017-09-06 17:09:11 -0700177 goto DONE;
Joe Onorato1754d742016-11-21 17:51:35 -0800178 }
179
180 // If there are too many files in the directory (for whatever reason),
181 // delete the oldest ones until it's under the limit. Doing this first
182 // does mean that we can go over, so the max size is not a hard limit.
Yi Jinadd11e92017-07-30 16:10:07 -0700183 if (!isTest) clean_directory(mIncidentDirectory, mMaxSize, mMaxCount);
Joe Onorato1754d742016-11-21 17:51:35 -0800184
185 // Open the file.
186 err = create_file(&mainFd);
187 if (err != NO_ERROR) {
Yi Jinedfd5bb2017-09-06 17:09:11 -0700188 goto DONE;
Joe Onorato1754d742016-11-21 17:51:35 -0800189 }
190
191 // Add to the set
192 batch.setMainFd(mainFd);
Yi Jin3ec5cc72018-01-26 13:42:43 -0800193 batch.setMainDest(mainDest);
Joe Onorato1754d742016-11-21 17:51:35 -0800194 }
195
196 // Tell everyone that we're starting.
197 for (ReportRequestSet::iterator it=batch.begin(); it!=batch.end(); it++) {
198 if ((*it)->listener != NULL) {
199 (*it)->listener->onReportStarted();
200 }
201 }
202
203 // Write the incident headers
Yi Jinedfd5bb2017-09-06 17:09:11 -0700204 headers.Execute(&batch);
Joe Onorato1754d742016-11-21 17:51:35 -0800205
206 // For each of the report fields, see if we need it, and if so, execute the command
207 // and report to those that care that we're doing it.
208 for (const Section** section=SECTION_LIST; *section; section++) {
209 const int id = (*section)->id;
Yi Jinadd11e92017-07-30 16:10:07 -0700210 if (this->batch.containsSection(id)) {
Yi Jinf32af482017-08-11 15:00:49 -0700211 ALOGD("Taking incident report section %d '%s'", id, (*section)->name.string());
Yi Jin329130b2018-02-09 16:47:47 -0800212 // Notify listener of starting.
Joe Onorato1754d742016-11-21 17:51:35 -0800213 for (ReportRequestSet::iterator it=batch.begin(); it!=batch.end(); it++) {
214 if ((*it)->listener != NULL && (*it)->args.containsSection(id)) {
215 (*it)->listener->onReportSectionStatus(id,
216 IIncidentReportStatusListener::STATUS_STARTING);
217 }
218 }
219
220 // Execute - go get the data and write it into the file descriptors.
Yi Jin329130b2018-02-09 16:47:47 -0800221 auto stats = batch.sectionStats(id);
222 int64_t startTime = uptimeMillis();
Joe Onorato1754d742016-11-21 17:51:35 -0800223 err = (*section)->Execute(&batch);
Yi Jin329130b2018-02-09 16:47:47 -0800224 int64_t endTime = uptimeMillis();
225
226 stats->set_success(err == NO_ERROR);
227 stats->set_exec_duration_ms(endTime - startTime);
Joe Onorato1754d742016-11-21 17:51:35 -0800228 if (err != NO_ERROR) {
Yi Jina5c5e8a2017-09-27 18:24:58 -0700229 ALOGW("Incident section %s (%d) failed: %s. Stopping report.",
230 (*section)->name.string(), id, strerror(-err));
Yi Jinedfd5bb2017-09-06 17:09:11 -0700231 goto DONE;
Joe Onorato1754d742016-11-21 17:51:35 -0800232 }
233
Yi Jin329130b2018-02-09 16:47:47 -0800234 // Notify listener of ending.
Joe Onorato1754d742016-11-21 17:51:35 -0800235 for (ReportRequestSet::iterator it=batch.begin(); it!=batch.end(); it++) {
236 if ((*it)->listener != NULL && (*it)->args.containsSection(id)) {
237 (*it)->listener->onReportSectionStatus(id,
238 IIncidentReportStatusListener::STATUS_FINISHED);
239 }
240 }
Yi Jinf32af482017-08-11 15:00:49 -0700241 ALOGD("Finish incident report section %d '%s'", id, (*section)->name.string());
Joe Onorato1754d742016-11-21 17:51:35 -0800242 }
243 }
244
Yi Jinedfd5bb2017-09-06 17:09:11 -0700245DONE:
Yi Jin329130b2018-02-09 16:47:47 -0800246 // Reports the metdadata when taking the incident report.
247 if (!isTest) metadataSection.Execute(&batch);
248
Joe Onorato1754d742016-11-21 17:51:35 -0800249 // Close the file.
250 if (mainFd >= 0) {
251 close(mainFd);
252 }
253
254 // Tell everyone that we're done.
255 for (ReportRequestSet::iterator it=batch.begin(); it!=batch.end(); it++) {
256 if ((*it)->listener != NULL) {
257 if (err == NO_ERROR) {
258 (*it)->listener->onReportFinished();
259 } else {
260 (*it)->listener->onReportFailed();
261 }
262 }
263 }
264
265 // Put the report into dropbox.
266 if (needMainFd && err == NO_ERROR) {
267 sp<DropBoxManager> dropbox = new DropBoxManager();
268 Status status = dropbox->addFile(String16("incident"), mFilename, 0);
269 ALOGD("Incident report done. dropbox status=%s\n", status.toString8().string());
270 if (!status.isOk()) {
271 return REPORT_NEEDS_DROPBOX;
272 }
273
274 // If the status was ok, delete the file. If not, leave it around until the next
275 // boot or the next checkin. If the directory gets too big older files will
276 // be rotated out.
Yi Jinadd11e92017-07-30 16:10:07 -0700277 if(!isTest) unlink(mFilename.c_str());
Joe Onorato1754d742016-11-21 17:51:35 -0800278 }
279
280 return REPORT_FINISHED;
281}
282
283/**
284 * Create our output file and set the access permissions to -rw-rw----
285 */
286status_t
287Reporter::create_file(int* fd)
288{
289 const char* filename = mFilename.c_str();
290
Yi Jinadd11e92017-07-30 16:10:07 -0700291 *fd = open(filename, O_CREAT | O_TRUNC | O_RDWR | O_CLOEXEC, 0660);
Joe Onorato1754d742016-11-21 17:51:35 -0800292 if (*fd < 0) {
293 ALOGE("Couldn't open incident file: %s (%s)", filename, strerror(errno));
294 return -errno;
295 }
296
297 // Override umask. Not super critical. If it fails go on with life.
298 chmod(filename, 0660);
299
Yi Jin4bab3a12018-01-10 16:50:59 -0800300 if (chown(filename, AID_INCIDENTD, AID_INCIDENTD)) {
Joe Onorato1754d742016-11-21 17:51:35 -0800301 ALOGE("Unable to change ownership of incident file %s: %s\n", filename, strerror(errno));
302 status_t err = -errno;
303 unlink(mFilename.c_str());
304 return err;
305 }
306
307 return NO_ERROR;
308}
309
Joe Onorato1754d742016-11-21 17:51:35 -0800310Reporter::run_report_status_t
311Reporter::upload_backlog()
312{
313 DIR* dir;
314 struct dirent* entry;
315 struct stat st;
Yi Jinadd11e92017-07-30 16:10:07 -0700316 status_t err;
Joe Onorato1754d742016-11-21 17:51:35 -0800317
Yi Jinf32af482017-08-11 15:00:49 -0700318 ALOGD("Start uploading backlogs in %s", INCIDENT_DIRECTORY);
Yi Jinadd11e92017-07-30 16:10:07 -0700319 if ((err = create_directory(INCIDENT_DIRECTORY)) != NO_ERROR) {
320 ALOGE("directory doesn't exist: %s", strerror(-err));
321 return REPORT_FINISHED;
322 }
323
324 if ((dir = opendir(INCIDENT_DIRECTORY)) == NULL) {
325 ALOGE("Couldn't open incident directory: %s", INCIDENT_DIRECTORY);
Joe Onorato1754d742016-11-21 17:51:35 -0800326 return REPORT_NEEDS_DROPBOX;
327 }
328
Joe Onorato1754d742016-11-21 17:51:35 -0800329 sp<DropBoxManager> dropbox = new DropBoxManager();
330
331 // Enumerate, count and add up size
Yi Jinf32af482017-08-11 15:00:49 -0700332 int count = 0;
Joe Onorato1754d742016-11-21 17:51:35 -0800333 while ((entry = readdir(dir)) != NULL) {
334 if (entry->d_name[0] == '.') {
335 continue;
336 }
Yi Jinadd11e92017-07-30 16:10:07 -0700337 String8 filename = String8(INCIDENT_DIRECTORY) + entry->d_name;
Joe Onorato1754d742016-11-21 17:51:35 -0800338 if (stat(filename.string(), &st) != 0) {
339 ALOGE("Unable to stat file %s", filename.string());
340 continue;
341 }
342 if (!S_ISREG(st.st_mode)) {
343 continue;
344 }
345
346 Status status = dropbox->addFile(String16("incident"), filename.string(), 0);
347 ALOGD("Incident report done. dropbox status=%s\n", status.toString8().string());
348 if (!status.isOk()) {
349 return REPORT_NEEDS_DROPBOX;
350 }
351
352 // If the status was ok, delete the file. If not, leave it around until the next
353 // boot or the next checkin. If the directory gets too big older files will
354 // be rotated out.
355 unlink(filename.string());
Yi Jinf32af482017-08-11 15:00:49 -0700356 count++;
Joe Onorato1754d742016-11-21 17:51:35 -0800357 }
Yi Jinf32af482017-08-11 15:00:49 -0700358 ALOGD("Successfully uploaded %d files to Dropbox.", count);
Joe Onorato1754d742016-11-21 17:51:35 -0800359 closedir(dir);
360
361 return REPORT_FINISHED;
362}
363