Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 643de923 authored by Mike Ma's avatar Mike Ma
Browse files

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
parent a4d4f94e
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -408,6 +408,7 @@ filegroup {
filegroup {
    name: "libincident_aidl",
    srcs: [
        "core/java/android/os/IIncidentDumpCallback.aidl",
        "core/java/android/os/IIncidentManager.aidl",
        "core/java/android/os/IIncidentReportStatusListener.aidl",
    ],
+51 −4
Original line number Diff line number Diff line
@@ -123,14 +123,17 @@ static string build_uri(const string& pkg, const string& cls, const string& id)

// ================================================================================
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 @@ void ReportHandler::take_report() {
        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 @@ IncidentService::IncidentService(const sp<Looper>& handlerLooper) {
    mWorkDirectory = new WorkDirectory();
    mBroadcaster = new Broadcaster(mWorkDirectory);
    mHandler = new ReportHandler(mWorkDirectory, mBroadcaster, handlerLooper,
            mThrottler);
            mThrottler, mRegisteredSections);
    mBroadcaster->setHandler(mHandler);
}

@@ -327,6 +330,11 @@ Status IncidentService::reportIncidentToDumpstate(unique_fd stream,
            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 @@ Status IncidentService::reportIncidentToDumpstate(unique_fd stream,
    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,
+14 −2
Original line number Diff line number Diff line
@@ -40,12 +40,16 @@ using namespace android::base;
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 @@ private:
    nsecs_t mBacklogDelay;
    sp<Throttler> mThrottler;

    const vector<BringYourOwnSection*>& mRegisteredSections;

    sp<ReportBatch> mBatch;

    /**
@@ -126,6 +132,11 @@ public:
    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 @@ private:
    sp<Broadcaster> mBroadcaster;
    sp<ReportHandler> mHandler;
    sp<Throttler> mThrottler;
    vector<BringYourOwnSection*> mRegisteredSections;

    /**
     * Commands print out help.
+59 −43
Original line number Diff line number Diff line
@@ -364,7 +364,6 @@ void ReportWriter::startSection(int sectionId) {
    mSectionBufferSuccess = false;
    mHadError = false;
    mSectionErrors.clear();
    
}

void ReportWriter::setSectionStats(const FdBuffer& buffer) {
@@ -470,10 +469,13 @@ status_t ReportWriter::writeSection(const FdBuffer& buffer) {


// ================================================================================
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 @@ void Reporter::runReport(size_t* reportByteSize) {
    // 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;
        if (execute_section(*section, &metadata, reportByteSize) != NO_ERROR) {
            goto DONE;
        }
    }

        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.");
    for (const Section* section : mRegisteredSections) {
        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());
    }

DONE:
@@ -681,6 +648,55 @@ DONE:
    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) {
+10 −2
Original line number Diff line number Diff line
@@ -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 std;
using namespace android::content;
using namespace android::os;

class BringYourOwnSection;
class Section;

// ================================================================================
@@ -122,7 +124,7 @@ public:
    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 @@ private:
// ================================================================================
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 @@ private:
    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();
};
Loading