Merge "Add an API to dump incident report for dumpstate"
am: a11beeca12
Change-Id: If5ca98de9a5a85ae6e2aff90224a80e925ea419c
This commit is contained in:
@@ -197,6 +197,26 @@ parse_receiver_arg(const string& arg, string* pkg, string* cls)
|
||||
return true;
|
||||
}
|
||||
|
||||
// ================================================================================
|
||||
static int
|
||||
stream_output(const int read_fd, const int write_fd) {
|
||||
while (true) {
|
||||
uint8_t buf[4096];
|
||||
ssize_t amt = TEMP_FAILURE_RETRY(read(read_fd, buf, sizeof(buf)));
|
||||
if (amt < 0) {
|
||||
break;
|
||||
} else if (amt == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
ssize_t wamt = TEMP_FAILURE_RETRY(write(write_fd, buf, amt));
|
||||
if (wamt != amt) {
|
||||
return errno;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// ================================================================================
|
||||
static void
|
||||
usage(FILE* out)
|
||||
@@ -208,11 +228,13 @@ usage(FILE* out)
|
||||
fprintf(out, "OPTIONS\n");
|
||||
fprintf(out, " -l list available sections\n");
|
||||
fprintf(out, " -p privacy spec, LOCAL, EXPLICIT or AUTOMATIC. Default AUTOMATIC.\n");
|
||||
fprintf(out, " -r REASON human readable description of why the report is taken.\n");
|
||||
fprintf(out, "\n");
|
||||
fprintf(out, "and one of these destinations:\n");
|
||||
fprintf(out, " -b (default) print the report to stdout (in proto format)\n");
|
||||
fprintf(out, " -d send the report into dropbox\n");
|
||||
fprintf(out, " -r REASON human readable description of why the report is taken.\n");
|
||||
fprintf(out, " -u print a full report to stdout for dumpstate to zip as a bug\n");
|
||||
fprintf(out, " report. SECTION is ignored. Should only be called by dumpstate.\n");
|
||||
fprintf(out, " -s PKG/CLS send broadcast to the broadcast receiver.\n");
|
||||
fprintf(out, "\n");
|
||||
fprintf(out, " SECTION the field numbers of the incident report fields to include\n");
|
||||
@@ -224,14 +246,14 @@ main(int argc, char** argv)
|
||||
{
|
||||
Status status;
|
||||
IncidentReportArgs args;
|
||||
enum { DEST_UNSET, DEST_DROPBOX, DEST_STDOUT, DEST_BROADCAST } destination = DEST_UNSET;
|
||||
enum { DEST_UNSET, DEST_DROPBOX, DEST_STDOUT, DEST_BROADCAST, DEST_DUMPSTATE } destination = DEST_UNSET;
|
||||
int privacyPolicy = PRIVACY_POLICY_AUTOMATIC;
|
||||
string reason;
|
||||
string receiverArg;
|
||||
|
||||
// Parse the args
|
||||
int opt;
|
||||
while ((opt = getopt(argc, argv, "bhdlp:r:s:")) != -1) {
|
||||
while ((opt = getopt(argc, argv, "bhdlp:r:s:u")) != -1) {
|
||||
switch (opt) {
|
||||
case 'h':
|
||||
usage(stdout);
|
||||
@@ -253,6 +275,13 @@ main(int argc, char** argv)
|
||||
}
|
||||
destination = DEST_DROPBOX;
|
||||
break;
|
||||
case 'u':
|
||||
if (!(destination == DEST_UNSET || destination == DEST_DUMPSTATE)) {
|
||||
usage(stderr);
|
||||
return 1;
|
||||
}
|
||||
destination = DEST_DUMPSTATE;
|
||||
break;
|
||||
case 'p':
|
||||
privacyPolicy = get_privacy_policy(optarg);
|
||||
break;
|
||||
@@ -355,21 +384,16 @@ main(int argc, char** argv)
|
||||
|
||||
// Wait for the result and print out the data they send.
|
||||
//IPCThreadState::self()->joinThreadPool();
|
||||
|
||||
while (true) {
|
||||
uint8_t buf[4096];
|
||||
ssize_t amt = TEMP_FAILURE_RETRY(read(fds[0], buf, sizeof(buf)));
|
||||
if (amt < 0) {
|
||||
break;
|
||||
} else if (amt == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
ssize_t wamt = TEMP_FAILURE_RETRY(write(STDOUT_FILENO, buf, amt));
|
||||
if (wamt != amt) {
|
||||
return errno;
|
||||
}
|
||||
return stream_output(fds[0], STDOUT_FILENO);
|
||||
} else if (destination == DEST_DUMPSTATE) {
|
||||
// Call into the service
|
||||
sp<StatusListener> listener(new StatusListener());
|
||||
status = service->reportIncidentToDumpstate(writeEnd, listener);
|
||||
if (!status.isOk()) {
|
||||
fprintf(stderr, "reportIncident returned \"%s\"\n", status.toString8().string());
|
||||
return 1;
|
||||
}
|
||||
return stream_output(fds[0], STDOUT_FILENO);
|
||||
} else {
|
||||
status = service->reportIncident(args);
|
||||
if (!status.isOk()) {
|
||||
|
||||
@@ -46,12 +46,11 @@ enum {
|
||||
#define DEFAULT_BYTES_SIZE_LIMIT (96 * 1024 * 1024) // 96MB
|
||||
#define DEFAULT_REFACTORY_PERIOD_MS (24 * 60 * 60 * 1000) // 1 Day
|
||||
|
||||
// Skip these sections for dumpstate only. Dumpstate allows 10s max for each service to dump.
|
||||
// Skip these sections (for dumpstate only)
|
||||
// Skip logs (1100 - 1108) and traces (1200 - 1202) because they are already in the bug report.
|
||||
// Skip 3018 because it takes too long.
|
||||
#define SKIPPED_SECTIONS { 1100, 1101, 1102, 1103, 1104, 1105, 1106, 1107, 1108, /* Logs */ \
|
||||
1200, 1201, 1202, /* Native, hal, java traces */ \
|
||||
3018 /* "meminfo -a --proto" */ }
|
||||
#define SKIPPED_DUMPSTATE_SECTIONS { \
|
||||
1100, 1101, 1102, 1103, 1104, 1105, 1106, 1107, 1108, /* Logs */ \
|
||||
1200, 1201, 1202, /* Native, hal, java traces */ }
|
||||
|
||||
namespace android {
|
||||
namespace os {
|
||||
@@ -307,6 +306,39 @@ Status IncidentService::reportIncidentToStream(const IncidentReportArgs& args,
|
||||
return Status::ok();
|
||||
}
|
||||
|
||||
Status IncidentService::reportIncidentToDumpstate(const unique_fd& stream,
|
||||
const sp<IIncidentReportStatusListener>& listener) {
|
||||
uid_t caller = IPCThreadState::self()->getCallingUid();
|
||||
if (caller != AID_ROOT && caller != AID_SHELL) {
|
||||
ALOGW("Calling uid %d does not have permission: only ROOT or SHELL allowed", caller);
|
||||
return Status::fromExceptionCode(Status::EX_SECURITY, "Only ROOT or SHELL allowed");
|
||||
}
|
||||
|
||||
ALOGD("Stream incident report to dumpstate");
|
||||
IncidentReportArgs incidentArgs;
|
||||
// Privacy policy for dumpstate incident reports is always EXPLICIT.
|
||||
incidentArgs.setPrivacyPolicy(PRIVACY_POLICY_EXPLICIT);
|
||||
|
||||
int skipped[] = SKIPPED_DUMPSTATE_SECTIONS;
|
||||
for (const Section** section = SECTION_LIST; *section; section++) {
|
||||
const int id = (*section)->id;
|
||||
if (std::find(std::begin(skipped), std::end(skipped), id) == std::end(skipped)
|
||||
&& !section_requires_specific_mention(id)) {
|
||||
incidentArgs.addSection(id);
|
||||
}
|
||||
}
|
||||
|
||||
// The ReportRequest takes ownership of the fd, so we need to dup it.
|
||||
int fd = dup(stream.get());
|
||||
if (fd < 0) {
|
||||
return Status::fromStatusT(-errno);
|
||||
}
|
||||
|
||||
mHandler->scheduleStreamingReport(incidentArgs, listener, fd);
|
||||
|
||||
return Status::ok();
|
||||
}
|
||||
|
||||
Status IncidentService::systemRunning() {
|
||||
if (IPCThreadState::self()->getCallingUid() != AID_SYSTEM) {
|
||||
return Status::fromExceptionCode(Status::EX_SECURITY,
|
||||
@@ -551,43 +583,6 @@ status_t IncidentService::cmd_privacy(FILE* in, FILE* out, FILE* err, Vector<Str
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
status_t IncidentService::dump(int fd, const Vector<String16>& args) {
|
||||
if (std::find(args.begin(), args.end(), String16("--proto")) == args.end()) {
|
||||
ALOGD("Skip dumping incident. Only proto format is supported.");
|
||||
dprintf(fd, "Incident dump only supports proto version.\n");
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
ALOGD("Dump incident proto");
|
||||
IncidentReportArgs incidentArgs;
|
||||
incidentArgs.setPrivacyPolicy(PRIVACY_POLICY_EXPLICIT);
|
||||
int skipped[] = SKIPPED_SECTIONS;
|
||||
for (const Section** section = SECTION_LIST; *section; section++) {
|
||||
const int id = (*section)->id;
|
||||
if (std::find(std::begin(skipped), std::end(skipped), id) == std::end(skipped)
|
||||
&& !section_requires_specific_mention(id)) {
|
||||
incidentArgs.addSection(id);
|
||||
}
|
||||
}
|
||||
|
||||
if (!checkIncidentPermissions(incidentArgs).isOk()) {
|
||||
return PERMISSION_DENIED;
|
||||
}
|
||||
|
||||
// The ReportRequest takes ownership of the fd, so we need to dup it.
|
||||
int fd1 = dup(fd);
|
||||
if (fd1 < 0) {
|
||||
return -errno;
|
||||
}
|
||||
|
||||
// TODO: Remove this. Someone even dumpstate, wanting to get an incident report
|
||||
// should use the API. That will take making dumpstated call the API, which is a
|
||||
// good thing. It also means it won't be subject to the timeout.
|
||||
mHandler->scheduleStreamingReport(incidentArgs, NULL, fd1);
|
||||
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
} // namespace incidentd
|
||||
} // namespace os
|
||||
} // namespace android
|
||||
|
||||
@@ -123,6 +123,9 @@ public:
|
||||
const sp<IIncidentReportStatusListener>& listener,
|
||||
const unique_fd& stream);
|
||||
|
||||
virtual Status reportIncidentToDumpstate(const unique_fd& stream,
|
||||
const sp<IIncidentReportStatusListener>& listener);
|
||||
|
||||
virtual Status systemRunning();
|
||||
|
||||
virtual Status getIncidentReportList(const String16& pkg, const String16& cls,
|
||||
@@ -140,7 +143,6 @@ public:
|
||||
virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply,
|
||||
uint32_t flags) override;
|
||||
virtual status_t command(FILE* in, FILE* out, FILE* err, Vector<String8>& args);
|
||||
virtual status_t dump(int fd, const Vector<String16>& args);
|
||||
|
||||
private:
|
||||
sp<WorkDirectory> mWorkDirectory;
|
||||
|
||||
@@ -45,8 +45,7 @@ int main(int /*argc*/, char** /*argv*/) {
|
||||
|
||||
// Create the service
|
||||
sp<IncidentService> service = new IncidentService(looper);
|
||||
if (defaultServiceManager()->addService(String16("incident"), service, false,
|
||||
IServiceManager::DUMP_FLAG_PRIORITY_NORMAL | IServiceManager::DUMP_FLAG_PROTO) != 0) {
|
||||
if (defaultServiceManager()->addService(String16("incident"), service) != 0) {
|
||||
ALOGE("Failed to add service");
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -42,6 +42,15 @@ interface IIncidentManager {
|
||||
@nullable IIncidentReportStatusListener listener,
|
||||
FileDescriptor stream);
|
||||
|
||||
/**
|
||||
* Takes a report with the given args, reporting status to the optional listener.
|
||||
* This should only be callable by dumpstate (enforced by callee).
|
||||
*
|
||||
* When the report is completed, the system report listener will be notified.
|
||||
*/
|
||||
oneway void reportIncidentToDumpstate(FileDescriptor stream,
|
||||
@nullable IIncidentReportStatusListener listener);
|
||||
|
||||
/**
|
||||
* Tell the incident daemon that the android system server is up and running.
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user