Merge "Incident Report Extension API"
This commit is contained in:
@@ -409,6 +409,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",
|
||||
],
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
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 @@ 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) {
|
||||
|
||||
@@ -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();
|
||||
};
|
||||
|
||||
@@ -267,7 +267,7 @@ static void* worker_thread_func(void* cookie) {
|
||||
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(int id, const char* service, ...)
|
||||
|
||||
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 @@ status_t DumpsysSection::BlockingCall(int pipeWriteFd) const {
|
||||
return NAME_NOT_FOUND;
|
||||
}
|
||||
|
||||
service->dump(pipeWriteFd, mArgs);
|
||||
service->dump(pipeWriteFd.get(), mArgs);
|
||||
|
||||
return NO_ERROR;
|
||||
}
|
||||
@@ -526,7 +526,7 @@ static inline int32_t get4LE(uint8_t const* src) {
|
||||
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 @@ status_t LogSection::BlockingCall(int pipeWriteFd) const {
|
||||
}
|
||||
}
|
||||
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(int id, const char* type, const int64_t timeo
|
||||
|
||||
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 @@ status_t TombstoneSection::BlockingCall(int pipeWriteFd) const {
|
||||
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 @@ status_t TombstoneSection::BlockingCall(int pipeWriteFd) const {
|
||||
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
|
||||
|
||||
@@ -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 @@ public:
|
||||
|
||||
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 @@ public:
|
||||
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 @@ public:
|
||||
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 @@ public:
|
||||
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 @@ public:
|
||||
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
|
||||
|
||||
31
core/java/android/os/IIncidentDumpCallback.aidl
Normal file
31
core/java/android/os/IIncidentDumpCallback.aidl
Normal file
@@ -0,0 +1,31 @@
|
||||
/**
|
||||
* Copyright (c) 2019, The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.os;
|
||||
|
||||
import android.os.ParcelFileDescriptor;
|
||||
|
||||
/**
|
||||
* Callback from IIncidentManager to dump an extended section.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
oneway interface IIncidentDumpCallback {
|
||||
/**
|
||||
* Dumps section data to the given ParcelFileDescriptor.
|
||||
*/
|
||||
void onDumpSection(in ParcelFileDescriptor fd);
|
||||
}
|
||||
@@ -17,6 +17,7 @@
|
||||
package android.os;
|
||||
|
||||
import android.os.IIncidentReportStatusListener;
|
||||
import android.os.IIncidentDumpCallback;
|
||||
import android.os.IncidentManager;
|
||||
import android.os.IncidentReportArgs;
|
||||
|
||||
@@ -51,6 +52,19 @@ interface IIncidentManager {
|
||||
oneway void reportIncidentToDumpstate(FileDescriptor stream,
|
||||
@nullable IIncidentReportStatusListener listener);
|
||||
|
||||
/**
|
||||
* Register a section callback with the given id and name. The callback function
|
||||
* will be invoked when an incident report with all sections or sections matching
|
||||
* the given id is being taken.
|
||||
*/
|
||||
oneway void registerSection(int id, String name, IIncidentDumpCallback callback);
|
||||
|
||||
/**
|
||||
* Unregister a section callback associated with the given id. The section must be
|
||||
* previously registered with registerSection(int, String, IIncidentDumpCallback).
|
||||
*/
|
||||
oneway void unregisterSection(int id);
|
||||
|
||||
/**
|
||||
* Tell the incident daemon that the android system server is up and running.
|
||||
*/
|
||||
|
||||
@@ -31,6 +31,7 @@ import android.util.Slog;
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.util.ArrayList;
|
||||
@@ -420,6 +421,39 @@ public class IncidentManager {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for dumping an extended (usually vendor-supplied) incident report section
|
||||
*
|
||||
* @see #registerSection
|
||||
* @see #unregisterSection
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public static class DumpCallback {
|
||||
private Executor mExecutor;
|
||||
|
||||
IIncidentDumpCallback.Stub mBinder = new IIncidentDumpCallback.Stub() {
|
||||
@Override
|
||||
public void onDumpSection(ParcelFileDescriptor pfd) {
|
||||
if (mExecutor != null) {
|
||||
mExecutor.execute(() -> {
|
||||
DumpCallback.this.onDumpSection(
|
||||
new ParcelFileDescriptor.AutoCloseOutputStream(pfd));
|
||||
});
|
||||
} else {
|
||||
DumpCallback.this.onDumpSection(
|
||||
new ParcelFileDescriptor.AutoCloseOutputStream(pfd));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Called when incidentd requests to dump this section.
|
||||
*/
|
||||
public void onDumpSection(OutputStream out) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
@@ -527,6 +561,61 @@ public class IncidentManager {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a callback to dump an extended incident report section with the given id and name.
|
||||
* The callback function will be invoked when an incident report with all sections or sections
|
||||
* matching the given id is being taken.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public void registerSection(int id, String name, @NonNull DumpCallback callback) {
|
||||
registerSection(id, name, mContext.getMainExecutor(), callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a callback to dump an extended incident report section with the given id and name,
|
||||
* running on the supplied executor.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public void registerSection(int id, String name, @NonNull @CallbackExecutor Executor executor,
|
||||
@NonNull DumpCallback callback) {
|
||||
try {
|
||||
if (callback.mExecutor != null) {
|
||||
throw new RuntimeException("Do not reuse DumpCallback objects when calling"
|
||||
+ " registerSection");
|
||||
}
|
||||
callback.mExecutor = executor;
|
||||
final IIncidentManager service = getIIncidentManagerLocked();
|
||||
if (service == null) {
|
||||
Slog.e(TAG, "registerSection can't find incident binder service");
|
||||
return;
|
||||
}
|
||||
service.registerSection(id, name, callback.mBinder);
|
||||
} catch (RemoteException ex) {
|
||||
Slog.e(TAG, "registerSection failed", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregister an extended section dump function. The section must be previously registered with
|
||||
* {@link #registerSection(int, String, DumpCallback)}
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public void unregisterSection(int id) {
|
||||
try {
|
||||
final IIncidentManager service = getIIncidentManagerLocked();
|
||||
if (service == null) {
|
||||
Slog.e(TAG, "unregisterSection can't find incident binder service");
|
||||
return;
|
||||
}
|
||||
service.unregisterSection(id);
|
||||
} catch (RemoteException ex) {
|
||||
Slog.e(TAG, "unregisterSection failed", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the incident reports that are available for upload for the supplied
|
||||
* broadcast recevier.
|
||||
|
||||
Reference in New Issue
Block a user