Merge "Add an API to dump incident report for dumpstate"
This commit is contained in:
@@ -197,6 +197,26 @@ parse_receiver_arg(const string& arg, string* pkg, string* cls)
|
|||||||
return true;
|
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
|
static void
|
||||||
usage(FILE* out)
|
usage(FILE* out)
|
||||||
@@ -208,11 +228,13 @@ usage(FILE* out)
|
|||||||
fprintf(out, "OPTIONS\n");
|
fprintf(out, "OPTIONS\n");
|
||||||
fprintf(out, " -l list available sections\n");
|
fprintf(out, " -l list available sections\n");
|
||||||
fprintf(out, " -p privacy spec, LOCAL, EXPLICIT or AUTOMATIC. Default AUTOMATIC.\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, "\n");
|
||||||
fprintf(out, "and one of these destinations:\n");
|
fprintf(out, "and one of these destinations:\n");
|
||||||
fprintf(out, " -b (default) print the report to stdout (in proto format)\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, " -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, " -s PKG/CLS send broadcast to the broadcast receiver.\n");
|
||||||
fprintf(out, "\n");
|
fprintf(out, "\n");
|
||||||
fprintf(out, " SECTION the field numbers of the incident report fields to include\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;
|
Status status;
|
||||||
IncidentReportArgs args;
|
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;
|
int privacyPolicy = PRIVACY_POLICY_AUTOMATIC;
|
||||||
string reason;
|
string reason;
|
||||||
string receiverArg;
|
string receiverArg;
|
||||||
|
|
||||||
// Parse the args
|
// Parse the args
|
||||||
int opt;
|
int opt;
|
||||||
while ((opt = getopt(argc, argv, "bhdlp:r:s:")) != -1) {
|
while ((opt = getopt(argc, argv, "bhdlp:r:s:u")) != -1) {
|
||||||
switch (opt) {
|
switch (opt) {
|
||||||
case 'h':
|
case 'h':
|
||||||
usage(stdout);
|
usage(stdout);
|
||||||
@@ -253,6 +275,13 @@ main(int argc, char** argv)
|
|||||||
}
|
}
|
||||||
destination = DEST_DROPBOX;
|
destination = DEST_DROPBOX;
|
||||||
break;
|
break;
|
||||||
|
case 'u':
|
||||||
|
if (!(destination == DEST_UNSET || destination == DEST_DUMPSTATE)) {
|
||||||
|
usage(stderr);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
destination = DEST_DUMPSTATE;
|
||||||
|
break;
|
||||||
case 'p':
|
case 'p':
|
||||||
privacyPolicy = get_privacy_policy(optarg);
|
privacyPolicy = get_privacy_policy(optarg);
|
||||||
break;
|
break;
|
||||||
@@ -355,21 +384,16 @@ main(int argc, char** argv)
|
|||||||
|
|
||||||
// Wait for the result and print out the data they send.
|
// Wait for the result and print out the data they send.
|
||||||
//IPCThreadState::self()->joinThreadPool();
|
//IPCThreadState::self()->joinThreadPool();
|
||||||
|
return stream_output(fds[0], STDOUT_FILENO);
|
||||||
while (true) {
|
} else if (destination == DEST_DUMPSTATE) {
|
||||||
uint8_t buf[4096];
|
// Call into the service
|
||||||
ssize_t amt = TEMP_FAILURE_RETRY(read(fds[0], buf, sizeof(buf)));
|
sp<StatusListener> listener(new StatusListener());
|
||||||
if (amt < 0) {
|
status = service->reportIncidentToDumpstate(writeEnd, listener);
|
||||||
break;
|
if (!status.isOk()) {
|
||||||
} else if (amt == 0) {
|
fprintf(stderr, "reportIncident returned \"%s\"\n", status.toString8().string());
|
||||||
break;
|
return 1;
|
||||||
}
|
|
||||||
|
|
||||||
ssize_t wamt = TEMP_FAILURE_RETRY(write(STDOUT_FILENO, buf, amt));
|
|
||||||
if (wamt != amt) {
|
|
||||||
return errno;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
return stream_output(fds[0], STDOUT_FILENO);
|
||||||
} else {
|
} else {
|
||||||
status = service->reportIncident(args);
|
status = service->reportIncident(args);
|
||||||
if (!status.isOk()) {
|
if (!status.isOk()) {
|
||||||
|
|||||||
@@ -46,12 +46,11 @@ enum {
|
|||||||
#define DEFAULT_BYTES_SIZE_LIMIT (96 * 1024 * 1024) // 96MB
|
#define DEFAULT_BYTES_SIZE_LIMIT (96 * 1024 * 1024) // 96MB
|
||||||
#define DEFAULT_REFACTORY_PERIOD_MS (24 * 60 * 60 * 1000) // 1 Day
|
#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 logs (1100 - 1108) and traces (1200 - 1202) because they are already in the bug report.
|
||||||
// Skip 3018 because it takes too long.
|
#define SKIPPED_DUMPSTATE_SECTIONS { \
|
||||||
#define SKIPPED_SECTIONS { 1100, 1101, 1102, 1103, 1104, 1105, 1106, 1107, 1108, /* Logs */ \
|
1100, 1101, 1102, 1103, 1104, 1105, 1106, 1107, 1108, /* Logs */ \
|
||||||
1200, 1201, 1202, /* Native, hal, java traces */ \
|
1200, 1201, 1202, /* Native, hal, java traces */ }
|
||||||
3018 /* "meminfo -a --proto" */ }
|
|
||||||
|
|
||||||
namespace android {
|
namespace android {
|
||||||
namespace os {
|
namespace os {
|
||||||
@@ -307,6 +306,39 @@ Status IncidentService::reportIncidentToStream(const IncidentReportArgs& args,
|
|||||||
return Status::ok();
|
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() {
|
Status IncidentService::systemRunning() {
|
||||||
if (IPCThreadState::self()->getCallingUid() != AID_SYSTEM) {
|
if (IPCThreadState::self()->getCallingUid() != AID_SYSTEM) {
|
||||||
return Status::fromExceptionCode(Status::EX_SECURITY,
|
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;
|
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 incidentd
|
||||||
} // namespace os
|
} // namespace os
|
||||||
} // namespace android
|
} // namespace android
|
||||||
|
|||||||
@@ -123,6 +123,9 @@ public:
|
|||||||
const sp<IIncidentReportStatusListener>& listener,
|
const sp<IIncidentReportStatusListener>& listener,
|
||||||
const unique_fd& stream);
|
const unique_fd& stream);
|
||||||
|
|
||||||
|
virtual Status reportIncidentToDumpstate(const unique_fd& stream,
|
||||||
|
const sp<IIncidentReportStatusListener>& listener);
|
||||||
|
|
||||||
virtual Status systemRunning();
|
virtual Status systemRunning();
|
||||||
|
|
||||||
virtual Status getIncidentReportList(const String16& pkg, const String16& cls,
|
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,
|
virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply,
|
||||||
uint32_t flags) override;
|
uint32_t flags) override;
|
||||||
virtual status_t command(FILE* in, FILE* out, FILE* err, Vector<String8>& args);
|
virtual status_t command(FILE* in, FILE* out, FILE* err, Vector<String8>& args);
|
||||||
virtual status_t dump(int fd, const Vector<String16>& args);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
sp<WorkDirectory> mWorkDirectory;
|
sp<WorkDirectory> mWorkDirectory;
|
||||||
|
|||||||
@@ -45,8 +45,7 @@ int main(int /*argc*/, char** /*argv*/) {
|
|||||||
|
|
||||||
// Create the service
|
// Create the service
|
||||||
sp<IncidentService> service = new IncidentService(looper);
|
sp<IncidentService> service = new IncidentService(looper);
|
||||||
if (defaultServiceManager()->addService(String16("incident"), service, false,
|
if (defaultServiceManager()->addService(String16("incident"), service) != 0) {
|
||||||
IServiceManager::DUMP_FLAG_PRIORITY_NORMAL | IServiceManager::DUMP_FLAG_PROTO) != 0) {
|
|
||||||
ALOGE("Failed to add service");
|
ALOGE("Failed to add service");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,6 +42,15 @@ interface IIncidentManager {
|
|||||||
@nullable IIncidentReportStatusListener listener,
|
@nullable IIncidentReportStatusListener listener,
|
||||||
FileDescriptor stream);
|
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.
|
* Tell the incident daemon that the android system server is up and running.
|
||||||
*/
|
*/
|
||||||
|
|||||||
Reference in New Issue
Block a user