Incident Report Extension API
Add an API for priv and system app to register a dump callback with
Incident Service.
Bug: 145924375
Test: Register a callback dumping a string. Capture an incident report
and verify that the customized section exist.
Change-Id: I6fff6c1ee97e25963068d284ba37adce1bb5ec31
diff --git a/cmds/incidentd/src/IncidentService.cpp b/cmds/incidentd/src/IncidentService.cpp
index cfd77c2..62312d1 100644
--- a/cmds/incidentd/src/IncidentService.cpp
+++ b/cmds/incidentd/src/IncidentService.cpp
@@ -123,14 +123,17 @@
// ================================================================================
ReportHandler::ReportHandler(const sp<WorkDirectory>& workDirectory,
- const sp<Broadcaster>& broadcaster, const sp<Looper>& handlerLooper,
- const sp<Throttler>& throttler)
+ const sp<Broadcaster>& broadcaster,
+ const sp<Looper>& handlerLooper,
+ const sp<Throttler>& throttler,
+ const vector<BringYourOwnSection*>& registeredSections)
:mLock(),
mWorkDirectory(workDirectory),
mBroadcaster(broadcaster),
mHandlerLooper(handlerLooper),
mBacklogDelay(DEFAULT_DELAY_NS),
mThrottler(throttler),
+ mRegisteredSections(registeredSections),
mBatch(new ReportBatch()) {
}
@@ -185,7 +188,7 @@
return;
}
- sp<Reporter> reporter = new Reporter(mWorkDirectory, batch);
+ sp<Reporter> reporter = new Reporter(mWorkDirectory, batch, mRegisteredSections);
// Take the report, which might take a while. More requests might queue
// up while we're doing this, and we'll handle them in their next batch.
@@ -237,7 +240,7 @@
mWorkDirectory = new WorkDirectory();
mBroadcaster = new Broadcaster(mWorkDirectory);
mHandler = new ReportHandler(mWorkDirectory, mBroadcaster, handlerLooper,
- mThrottler);
+ mThrottler, mRegisteredSections);
mBroadcaster->setHandler(mHandler);
}
@@ -327,6 +330,11 @@
incidentArgs.addSection(id);
}
}
+ for (const Section* section : mRegisteredSections) {
+ if (!section_requires_specific_mention(section->id)) {
+ incidentArgs.addSection(section->id);
+ }
+ }
// The ReportRequest takes ownership of the fd, so we need to dup it.
int fd = dup(stream.get());
@@ -339,6 +347,45 @@
return Status::ok();
}
+Status IncidentService::registerSection(const int id, const String16& name16,
+ const sp<IIncidentDumpCallback>& callback) {
+ const char* name = String8(name16).c_str();
+ ALOGI("Register section %d: %s", id, name);
+ if (callback == nullptr) {
+ return Status::fromExceptionCode(Status::EX_NULL_POINTER);
+ }
+ const uid_t callingUid = IPCThreadState::self()->getCallingUid();
+ for (int i = 0; i < mRegisteredSections.size(); i++) {
+ if (mRegisteredSections.at(i)->id == id) {
+ if (mRegisteredSections.at(i)->uid != callingUid) {
+ ALOGW("Error registering section %d: calling uid does not match", id);
+ return Status::fromExceptionCode(Status::EX_SECURITY);
+ }
+ mRegisteredSections.at(i) = new BringYourOwnSection(id, name, callingUid, callback);
+ return Status::ok();
+ }
+ }
+ mRegisteredSections.push_back(new BringYourOwnSection(id, name, callingUid, callback));
+ return Status::ok();
+}
+
+Status IncidentService::unregisterSection(const int id) {
+ ALOGI("Unregister section %d", id);
+ uid_t callingUid = IPCThreadState::self()->getCallingUid();
+ for (auto it = mRegisteredSections.begin(); it != mRegisteredSections.end(); it++) {
+ if ((*it)->id == id) {
+ if ((*it)->uid != callingUid) {
+ ALOGW("Error unregistering section %d: calling uid does not match", id);
+ return Status::fromExceptionCode(Status::EX_SECURITY);
+ }
+ mRegisteredSections.erase(it);
+ return Status::ok();
+ }
+ }
+ ALOGW("Section %d not found", id);
+ return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE);
+}
+
Status IncidentService::systemRunning() {
if (IPCThreadState::self()->getCallingUid() != AID_SYSTEM) {
return Status::fromExceptionCode(Status::EX_SECURITY,
diff --git a/cmds/incidentd/src/IncidentService.h b/cmds/incidentd/src/IncidentService.h
index b2c7f23..49fc566 100644
--- a/cmds/incidentd/src/IncidentService.h
+++ b/cmds/incidentd/src/IncidentService.h
@@ -40,12 +40,16 @@
using namespace android::binder;
using namespace android::os;
+class BringYourOwnSection;
+
// ================================================================================
class ReportHandler : public MessageHandler {
public:
ReportHandler(const sp<WorkDirectory>& workDirectory,
- const sp<Broadcaster>& broadcaster, const sp<Looper>& handlerLooper,
- const sp<Throttler>& throttler);
+ const sp<Broadcaster>& broadcaster,
+ const sp<Looper>& handlerLooper,
+ const sp<Throttler>& throttler,
+ const vector<BringYourOwnSection*>& registeredSections);
virtual ~ReportHandler();
virtual void handleMessage(const Message& message);
@@ -79,6 +83,8 @@
nsecs_t mBacklogDelay;
sp<Throttler> mThrottler;
+ const vector<BringYourOwnSection*>& mRegisteredSections;
+
sp<ReportBatch> mBatch;
/**
@@ -126,6 +132,11 @@
virtual Status reportIncidentToDumpstate(unique_fd stream,
const sp<IIncidentReportStatusListener>& listener);
+ virtual Status registerSection(int id, const String16& name,
+ const sp<IIncidentDumpCallback>& callback);
+
+ virtual Status unregisterSection(int id);
+
virtual Status systemRunning();
virtual Status getIncidentReportList(const String16& pkg, const String16& cls,
@@ -149,6 +160,7 @@
sp<Broadcaster> mBroadcaster;
sp<ReportHandler> mHandler;
sp<Throttler> mThrottler;
+ vector<BringYourOwnSection*> mRegisteredSections;
/**
* Commands print out help.
diff --git a/cmds/incidentd/src/Reporter.cpp b/cmds/incidentd/src/Reporter.cpp
index 02b6bbe..aa40f85 100644
--- a/cmds/incidentd/src/Reporter.cpp
+++ b/cmds/incidentd/src/Reporter.cpp
@@ -364,7 +364,6 @@
mSectionBufferSuccess = false;
mHadError = false;
mSectionErrors.clear();
-
}
void ReportWriter::setSectionStats(const FdBuffer& buffer) {
@@ -470,10 +469,13 @@
// ================================================================================
-Reporter::Reporter(const sp<WorkDirectory>& workDirectory, const sp<ReportBatch>& batch)
+Reporter::Reporter(const sp<WorkDirectory>& workDirectory,
+ const sp<ReportBatch>& batch,
+ const vector<BringYourOwnSection*>& registeredSections)
:mWorkDirectory(workDirectory),
mWriter(batch),
- mBatch(batch) {
+ mBatch(batch),
+ mRegisteredSections(registeredSections) {
}
Reporter::~Reporter() {
@@ -580,50 +582,15 @@
// 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++) {
- const int sectionId = (*section)->id;
-
- // If nobody wants this section, skip it.
- if (!mBatch->containsSection(sectionId)) {
- continue;
- }
-
- ALOGD("Start incident report section %d '%s'", sectionId, (*section)->name.string());
- IncidentMetadata::SectionStats* sectionMetadata = metadata.add_sections();
-
- // Notify listener of starting
- mBatch->forEachListener(sectionId, [sectionId](const auto& listener) {
- listener->onReportSectionStatus(
- sectionId, IIncidentReportStatusListener::STATUS_STARTING);
- });
-
- // Go get the data and write it into the file descriptors.
- mWriter.startSection(sectionId);
- err = (*section)->Execute(&mWriter);
- mWriter.endSection(sectionMetadata);
-
- // Sections returning errors are fatal. Most errors should not be fatal.
- if (err != NO_ERROR) {
- mWriter.error((*section), err, "Section failed. Stopping report.");
+ if (execute_section(*section, &metadata, reportByteSize) != NO_ERROR) {
goto DONE;
}
+ }
- // The returned max data size is used for throttling too many incident reports.
- (*reportByteSize) += sectionMetadata->report_size_bytes();
-
- // For any requests that failed during this section, remove them now. We do this
- // before calling back about section finished, so listeners do not erroniously get the
- // impression that the section succeeded. But we do it here instead of inside
- // writeSection so that the callback is done from a known context and not from the
- // bowels of a section, where changing the batch could cause odd errors.
- cancel_and_remove_failed_requests();
-
- // Notify listener of finishing
- mBatch->forEachListener(sectionId, [sectionId](const auto& listener) {
- listener->onReportSectionStatus(
- sectionId, IIncidentReportStatusListener::STATUS_FINISHED);
- });
-
- ALOGD("Finish incident report section %d '%s'", sectionId, (*section)->name.string());
+ for (const Section* section : mRegisteredSections) {
+ if (execute_section(section, &metadata, reportByteSize) != NO_ERROR) {
+ goto DONE;
+ }
}
DONE:
@@ -681,6 +648,55 @@
ALOGI("Done taking incident report err=%s", strerror(-err));
}
+status_t Reporter::execute_section(const Section* section, IncidentMetadata* metadata,
+ size_t* reportByteSize) {
+ const int sectionId = section->id;
+
+ // If nobody wants this section, skip it.
+ if (!mBatch->containsSection(sectionId)) {
+ return NO_ERROR;
+ }
+
+ ALOGD("Start incident report section %d '%s'", sectionId, section->name.string());
+ IncidentMetadata::SectionStats* sectionMetadata = metadata->add_sections();
+
+ // Notify listener of starting
+ mBatch->forEachListener(sectionId, [sectionId](const auto& listener) {
+ listener->onReportSectionStatus(
+ sectionId, IIncidentReportStatusListener::STATUS_STARTING);
+ });
+
+ // Go get the data and write it into the file descriptors.
+ mWriter.startSection(sectionId);
+ status_t err = section->Execute(&mWriter);
+ mWriter.endSection(sectionMetadata);
+
+ // Sections returning errors are fatal. Most errors should not be fatal.
+ if (err != NO_ERROR) {
+ mWriter.error(section, err, "Section failed. Stopping report.");
+ return err;
+ }
+
+ // The returned max data size is used for throttling too many incident reports.
+ (*reportByteSize) += sectionMetadata->report_size_bytes();
+
+ // For any requests that failed during this section, remove them now. We do this
+ // before calling back about section finished, so listeners do not erroniously get the
+ // impression that the section succeeded. But we do it here instead of inside
+ // writeSection so that the callback is done from a known context and not from the
+ // bowels of a section, where changing the batch could cause odd errors.
+ cancel_and_remove_failed_requests();
+
+ // Notify listener of finishing
+ mBatch->forEachListener(sectionId, [sectionId](const auto& listener) {
+ listener->onReportSectionStatus(
+ sectionId, IIncidentReportStatusListener::STATUS_FINISHED);
+ });
+
+ ALOGD("Finish incident report section %d '%s'", sectionId, section->name.string());
+ return NO_ERROR;
+}
+
void Reporter::cancel_and_remove_failed_requests() {
// Handle a failure in the persisted file
if (mPersistedFile != nullptr) {
diff --git a/cmds/incidentd/src/Reporter.h b/cmds/incidentd/src/Reporter.h
index fb3961a..cbc8b13 100644
--- a/cmds/incidentd/src/Reporter.h
+++ b/cmds/incidentd/src/Reporter.h
@@ -21,6 +21,7 @@
#include "frameworks/base/core/proto/android/os/metadata.pb.h"
#include <android/content/ComponentName.h>
#include <android/os/IIncidentReportStatusListener.h>
+#include <android/os/IIncidentDumpCallback.h>
#include <android/os/IncidentReportArgs.h>
#include <android/util/protobuf.h>
@@ -39,6 +40,7 @@
using namespace android::content;
using namespace android::os;
+class BringYourOwnSection;
class Section;
// ================================================================================
@@ -122,7 +124,7 @@
void forEachStreamingRequest(const function<void (const sp<ReportRequest>&)>& func);
/**
- * Call func(request) for each file descriptor that has
+ * Call func(request) for each file descriptor.
*/
void forEachFd(int sectionId, const function<void (const sp<ReportRequest>&)>& func);
@@ -251,7 +253,9 @@
// ================================================================================
class Reporter : public virtual RefBase {
public:
- Reporter(const sp<WorkDirectory>& workDirectory, const sp<ReportBatch>& batch);
+ Reporter(const sp<WorkDirectory>& workDirectory,
+ const sp<ReportBatch>& batch,
+ const vector<BringYourOwnSection*>& registeredSections);
virtual ~Reporter();
@@ -263,6 +267,10 @@
ReportWriter mWriter;
sp<ReportBatch> mBatch;
sp<ReportFile> mPersistedFile;
+ const vector<BringYourOwnSection*>& mRegisteredSections;
+
+ status_t execute_section(const Section* section, IncidentMetadata* metadata,
+ size_t* reportByteSize);
void cancel_and_remove_failed_requests();
};
diff --git a/cmds/incidentd/src/Section.cpp b/cmds/incidentd/src/Section.cpp
index c9277a5..2229e1c 100644
--- a/cmds/incidentd/src/Section.cpp
+++ b/cmds/incidentd/src/Section.cpp
@@ -267,7 +267,7 @@
signal(SIGPIPE, sigpipe_handler);
WorkerThreadData* data = (WorkerThreadData*)cookie;
- status_t err = data->section->BlockingCall(data->pipe.writeFd().get());
+ status_t err = data->section->BlockingCall(data->pipe.writeFd());
{
unique_lock<mutex> lock(data->lock);
@@ -458,7 +458,7 @@
DumpsysSection::~DumpsysSection() {}
-status_t DumpsysSection::BlockingCall(int pipeWriteFd) const {
+status_t DumpsysSection::BlockingCall(unique_fd& pipeWriteFd) const {
// checkService won't wait for the service to show up like getService will.
sp<IBinder> service = defaultServiceManager()->checkService(mService);
@@ -467,7 +467,7 @@
return NAME_NOT_FOUND;
}
- service->dump(pipeWriteFd, mArgs);
+ service->dump(pipeWriteFd.get(), mArgs);
return NO_ERROR;
}
@@ -526,7 +526,7 @@
return src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24);
}
-status_t LogSection::BlockingCall(int pipeWriteFd) const {
+status_t LogSection::BlockingCall(unique_fd& pipeWriteFd) const {
// Open log buffer and getting logs since last retrieved time if any.
unique_ptr<logger_list, void (*)(logger_list*)> loggers(
gLastLogsRetrieved.find(mLogID) == gLastLogsRetrieved.end()
@@ -643,7 +643,7 @@
}
}
gLastLogsRetrieved[mLogID] = lastTimestamp;
- if (!proto.flush(pipeWriteFd) && errno == EPIPE) {
+ if (!proto.flush(pipeWriteFd.get()) && errno == EPIPE) {
ALOGE("[%s] wrote to a broken pipe\n", this->name.string());
return EPIPE;
}
@@ -660,7 +660,7 @@
TombstoneSection::~TombstoneSection() {}
-status_t TombstoneSection::BlockingCall(int pipeWriteFd) const {
+status_t TombstoneSection::BlockingCall(unique_fd& pipeWriteFd) const {
std::unique_ptr<DIR, decltype(&closedir)> proc(opendir("/proc"), closedir);
if (proc.get() == nullptr) {
ALOGE("opendir /proc failed: %s\n", strerror(errno));
@@ -768,7 +768,7 @@
dumpPipe.readFd().reset();
}
- if (!proto.flush(pipeWriteFd) && errno == EPIPE) {
+ if (!proto.flush(pipeWriteFd.get()) && errno == EPIPE) {
ALOGE("[%s] wrote to a broken pipe\n", this->name.string());
if (err != NO_ERROR) {
return EPIPE;
@@ -778,6 +778,22 @@
return err;
}
+// ================================================================================
+BringYourOwnSection::BringYourOwnSection(int id, const char* customName, const uid_t callingUid,
+ const sp<IIncidentDumpCallback>& callback)
+ : WorkerThreadSection(id, REMOTE_CALL_TIMEOUT_MS), uid(callingUid), mCallback(callback) {
+ name = "registered ";
+ name += customName;
+}
+
+BringYourOwnSection::~BringYourOwnSection() {}
+
+status_t BringYourOwnSection::BlockingCall(unique_fd& pipeWriteFd) const {
+ android::os::ParcelFileDescriptor pfd(std::move(pipeWriteFd));
+ mCallback->onDumpSection(pfd);
+ return NO_ERROR;
+}
+
} // namespace incidentd
} // namespace os
} // namespace android
diff --git a/cmds/incidentd/src/Section.h b/cmds/incidentd/src/Section.h
index fcf12f7..0bb9da9 100644
--- a/cmds/incidentd/src/Section.h
+++ b/cmds/incidentd/src/Section.h
@@ -23,6 +23,8 @@
#include <stdarg.h>
#include <map>
+#include <android/os/IIncidentDumpCallback.h>
+
#include <utils/String16.h>
#include <utils/String8.h>
#include <utils/Vector.h>
@@ -89,7 +91,7 @@
virtual status_t Execute(ReportWriter* writer) const;
- virtual status_t BlockingCall(int pipeWriteFd) const = 0;
+ virtual status_t BlockingCall(unique_fd& pipeWriteFd) const = 0;
};
/**
@@ -117,7 +119,7 @@
DumpsysSection(int id, const char* service, ...);
virtual ~DumpsysSection();
- virtual status_t BlockingCall(int pipeWriteFd) const;
+ virtual status_t BlockingCall(unique_fd& pipeWriteFd) const;
private:
String16 mService;
@@ -132,7 +134,7 @@
SystemPropertyDumpsysSection(int id, const char* service, ...);
virtual ~SystemPropertyDumpsysSection();
- virtual status_t BlockingCall(int pipeWriteFd) const;
+ virtual status_t BlockingCall(unique_fd& pipeWriteFd) const;
private:
String16 mService;
@@ -153,7 +155,7 @@
LogSection(int id, const char* logID, ...);
virtual ~LogSection();
- virtual status_t BlockingCall(int pipeWriteFd) const;
+ virtual status_t BlockingCall(unique_fd& pipeWriteFd) const;
private:
log_id_t mLogID;
@@ -169,12 +171,29 @@
TombstoneSection(int id, const char* type, int64_t timeoutMs = 120000 /* 2 minutes */);
virtual ~TombstoneSection();
- virtual status_t BlockingCall(int pipeWriteFd) const;
+ virtual status_t BlockingCall(unique_fd& pipeWriteFd) const;
private:
std::string mType;
};
+/**
+ * Section that gets data from a registered dump callback.
+ */
+class BringYourOwnSection : public WorkerThreadSection {
+public:
+ const uid_t uid;
+
+ BringYourOwnSection(int id, const char* customName, const uid_t callingUid,
+ const sp<IIncidentDumpCallback>& callback);
+ virtual ~BringYourOwnSection();
+
+ virtual status_t BlockingCall(unique_fd& pipeWriteFd) const;
+
+private:
+ const sp<IIncidentDumpCallback>& mCallback;
+};
+
/**
* These sections will not be generated when doing an 'all' report, either