Put metadata or stats into each dropbox incident report.
Bug: 65451198 Test: atest incidentd_test Change-Id: Ib406b177ad7f1b4bda7fef2e606fc66a9836e060
This commit is contained in:
@@ -56,6 +56,7 @@ LOCAL_SHARED_LIBRARIES := \
|
||||
libcutils \
|
||||
libincident \
|
||||
liblog \
|
||||
libprotobuf-cpp-lite \
|
||||
libprotoutil \
|
||||
libselinux \
|
||||
libservices \
|
||||
|
||||
@@ -73,11 +73,6 @@ PrivacySpec PrivacySpec::new_spec(int dest)
|
||||
case android::os::DEST_LOCAL:
|
||||
return PrivacySpec(dest);
|
||||
default:
|
||||
return PrivacySpec();
|
||||
return PrivacySpec(android::os::DEST_AUTOMATIC);
|
||||
}
|
||||
}
|
||||
|
||||
PrivacySpec PrivacySpec::get_default_dropbox_spec()
|
||||
{
|
||||
return PrivacySpec(android::os::DEST_AUTOMATIC);
|
||||
}
|
||||
|
||||
@@ -75,7 +75,6 @@ public:
|
||||
|
||||
// Constructs spec using static methods below.
|
||||
static PrivacySpec new_spec(int dest);
|
||||
static PrivacySpec get_default_dropbox_spec();
|
||||
private:
|
||||
PrivacySpec(uint8_t dest) : dest(dest) {}
|
||||
};
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
|
||||
#include "Reporter.h"
|
||||
|
||||
#include "Privacy.h"
|
||||
#include "report_directory.h"
|
||||
#include "section_list.h"
|
||||
|
||||
@@ -65,7 +66,9 @@ ReportRequestSet::ReportRequestSet()
|
||||
:mRequests(),
|
||||
mSections(),
|
||||
mMainFd(-1),
|
||||
mMainDest(-1)
|
||||
mMainDest(-1),
|
||||
mMetadata(),
|
||||
mSectionStats()
|
||||
{
|
||||
}
|
||||
|
||||
@@ -79,18 +82,32 @@ ReportRequestSet::add(const sp<ReportRequest>& request)
|
||||
{
|
||||
mRequests.push_back(request);
|
||||
mSections.merge(request->args);
|
||||
mMetadata.set_request_size(mMetadata.request_size() + 1);
|
||||
}
|
||||
|
||||
void
|
||||
ReportRequestSet::setMainFd(int fd)
|
||||
{
|
||||
mMainFd = fd;
|
||||
mMetadata.set_use_dropbox(fd > 0);
|
||||
}
|
||||
|
||||
void
|
||||
ReportRequestSet::setMainDest(int dest)
|
||||
{
|
||||
mMainDest = dest;
|
||||
PrivacySpec spec = PrivacySpec::new_spec(dest);
|
||||
switch (spec.dest) {
|
||||
case android::os::DEST_AUTOMATIC:
|
||||
mMetadata.set_dest(IncidentMetadata_Destination_AUTOMATIC);
|
||||
break;
|
||||
case android::os::DEST_EXPLICIT:
|
||||
mMetadata.set_dest(IncidentMetadata_Destination_EXPLICIT);
|
||||
break;
|
||||
case android::os::DEST_LOCAL:
|
||||
mMetadata.set_dest(IncidentMetadata_Destination_LOCAL);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
@@ -98,6 +115,16 @@ ReportRequestSet::containsSection(int id) {
|
||||
return mSections.containsSection(id);
|
||||
}
|
||||
|
||||
IncidentMetadata::SectionStats*
|
||||
ReportRequestSet::sectionStats(int id) {
|
||||
if (mSectionStats.find(id) == mSectionStats.end()) {
|
||||
auto stats = mMetadata.add_sections();
|
||||
stats->set_id(id);
|
||||
mSectionStats[id] = stats;
|
||||
}
|
||||
return mSectionStats[id];
|
||||
}
|
||||
|
||||
// ================================================================================
|
||||
Reporter::Reporter() : Reporter(INCIDENT_DIRECTORY) { isTest = false; };
|
||||
|
||||
@@ -128,12 +155,12 @@ Reporter::~Reporter()
|
||||
Reporter::run_report_status_t
|
||||
Reporter::runReport()
|
||||
{
|
||||
|
||||
status_t err = NO_ERROR;
|
||||
bool needMainFd = false;
|
||||
int mainFd = -1;
|
||||
int mainDest = -1;
|
||||
HeaderSection headers;
|
||||
MetadataSection metadataSection;
|
||||
|
||||
// See if we need the main file
|
||||
for (ReportRequestSet::iterator it=batch.begin(); it!=batch.end(); it++) {
|
||||
@@ -182,7 +209,7 @@ Reporter::runReport()
|
||||
const int id = (*section)->id;
|
||||
if (this->batch.containsSection(id)) {
|
||||
ALOGD("Taking incident report section %d '%s'", id, (*section)->name.string());
|
||||
// Notify listener of starting
|
||||
// Notify listener of starting.
|
||||
for (ReportRequestSet::iterator it=batch.begin(); it!=batch.end(); it++) {
|
||||
if ((*it)->listener != NULL && (*it)->args.containsSection(id)) {
|
||||
(*it)->listener->onReportSectionStatus(id,
|
||||
@@ -191,14 +218,20 @@ Reporter::runReport()
|
||||
}
|
||||
|
||||
// Execute - go get the data and write it into the file descriptors.
|
||||
auto stats = batch.sectionStats(id);
|
||||
int64_t startTime = uptimeMillis();
|
||||
err = (*section)->Execute(&batch);
|
||||
int64_t endTime = uptimeMillis();
|
||||
|
||||
stats->set_success(err == NO_ERROR);
|
||||
stats->set_exec_duration_ms(endTime - startTime);
|
||||
if (err != NO_ERROR) {
|
||||
ALOGW("Incident section %s (%d) failed: %s. Stopping report.",
|
||||
(*section)->name.string(), id, strerror(-err));
|
||||
goto DONE;
|
||||
}
|
||||
|
||||
// Notify listener of starting
|
||||
// Notify listener of ending.
|
||||
for (ReportRequestSet::iterator it=batch.begin(); it!=batch.end(); it++) {
|
||||
if ((*it)->listener != NULL && (*it)->args.containsSection(id)) {
|
||||
(*it)->listener->onReportSectionStatus(id,
|
||||
@@ -210,6 +243,9 @@ Reporter::runReport()
|
||||
}
|
||||
|
||||
DONE:
|
||||
// Reports the metdadata when taking the incident report.
|
||||
if (!isTest) metadataSection.Execute(&batch);
|
||||
|
||||
// Close the file.
|
||||
if (mainFd >= 0) {
|
||||
close(mainFd);
|
||||
|
||||
@@ -17,9 +17,12 @@
|
||||
#ifndef REPORTER_H
|
||||
#define REPORTER_H
|
||||
|
||||
#include "frameworks/base/libs/incident/proto/android/os/metadata.pb.h"
|
||||
|
||||
#include <android/os/IIncidentReportStatusListener.h>
|
||||
#include <android/os/IncidentReportArgs.h>
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
@@ -61,13 +64,20 @@ public:
|
||||
iterator end() { return mRequests.end(); }
|
||||
|
||||
int mainFd() { return mMainFd; }
|
||||
bool containsSection(int id);
|
||||
int mainDest() { return mMainDest; }
|
||||
IncidentMetadata& metadata() { return mMetadata; }
|
||||
|
||||
bool containsSection(int id);
|
||||
IncidentMetadata::SectionStats* sectionStats(int id);
|
||||
|
||||
private:
|
||||
vector<sp<ReportRequest>> mRequests;
|
||||
IncidentReportArgs mSections;
|
||||
int mMainFd;
|
||||
int mMainDest;
|
||||
|
||||
IncidentMetadata mMetadata;
|
||||
map<int, IncidentMetadata::SectionStats*> mSectionStats;
|
||||
};
|
||||
|
||||
// ================================================================================
|
||||
@@ -81,8 +91,8 @@ public:
|
||||
|
||||
ReportRequestSet batch;
|
||||
|
||||
Reporter();
|
||||
Reporter(const char* directory);
|
||||
Reporter(); // PROD must use this constructor.
|
||||
Reporter(const char* directory); // For testing purpose only.
|
||||
virtual ~Reporter();
|
||||
|
||||
// Run the report as described in the batch and args parameters.
|
||||
|
||||
@@ -45,6 +45,7 @@ using namespace std;
|
||||
|
||||
// special section ids
|
||||
const int FIELD_ID_INCIDENT_HEADER = 1;
|
||||
const int FIELD_ID_INCIDENT_METADATA = 2;
|
||||
|
||||
// incident section parameters
|
||||
const int WAIT_MAX = 5;
|
||||
@@ -149,6 +150,12 @@ write_report_requests(const int id, const FdBuffer& buffer, ReportRequestSet* re
|
||||
EncodedBuffer::iterator data = buffer.data();
|
||||
PrivacyBuffer privacyBuffer(get_privacy_of_section(id), data);
|
||||
int writeable = 0;
|
||||
auto stats = requests->sectionStats(id);
|
||||
|
||||
stats->set_dump_size_bytes(data.size());
|
||||
stats->set_dump_duration_ms(buffer.durationMs());
|
||||
stats->set_timed_out(buffer.timedOut());
|
||||
stats->set_is_truncated(buffer.truncated());
|
||||
|
||||
// The streaming ones, group requests by spec in order to save unnecessary strip operations
|
||||
map<PrivacySpec, vector<sp<ReportRequest>>> requestsBySpec;
|
||||
@@ -182,9 +189,7 @@ write_report_requests(const int id, const FdBuffer& buffer, ReportRequestSet* re
|
||||
|
||||
// The dropbox file
|
||||
if (requests->mainFd() >= 0) {
|
||||
PrivacySpec spec = requests->mainDest() < 0 ?
|
||||
PrivacySpec::get_default_dropbox_spec() :
|
||||
PrivacySpec::new_spec(requests->mainDest());
|
||||
PrivacySpec spec = PrivacySpec::new_spec(requests->mainDest());
|
||||
err = privacyBuffer.strip(spec);
|
||||
if (err != NO_ERROR) return err; // the buffer data is corrupted.
|
||||
if (privacyBuffer.size() == 0) goto DONE;
|
||||
@@ -196,6 +201,7 @@ write_report_requests(const int id, const FdBuffer& buffer, ReportRequestSet* re
|
||||
writeable++;
|
||||
ALOGD("Section %d flushed %zu bytes to dropbox %d with spec %d", id,
|
||||
privacyBuffer.size(), requests->mainFd(), spec.dest);
|
||||
stats->set_report_size_bytes(privacyBuffer.size());
|
||||
}
|
||||
|
||||
DONE:
|
||||
@@ -236,7 +242,7 @@ HeaderSection::Execute(ReportRequestSet* requests) const
|
||||
|
||||
// So the idea is only requests with negative fd are written to dropbox file.
|
||||
int fd = request->fd >= 0 ? request->fd : requests->mainFd();
|
||||
write_section_header(fd, FIELD_ID_INCIDENT_HEADER, buf->size());
|
||||
write_section_header(fd, id, buf->size());
|
||||
write_all(fd, (uint8_t const*)buf->data(), buf->size());
|
||||
// If there was an error now, there will be an error later and we will remove
|
||||
// it from the list then.
|
||||
@@ -244,7 +250,35 @@ HeaderSection::Execute(ReportRequestSet* requests) const
|
||||
}
|
||||
return NO_ERROR;
|
||||
}
|
||||
// ================================================================================
|
||||
MetadataSection::MetadataSection()
|
||||
:Section(FIELD_ID_INCIDENT_METADATA, 0)
|
||||
{
|
||||
}
|
||||
|
||||
MetadataSection::~MetadataSection()
|
||||
{
|
||||
}
|
||||
|
||||
status_t
|
||||
MetadataSection::Execute(ReportRequestSet* requests) const
|
||||
{
|
||||
std::string metadataBuf;
|
||||
requests->metadata().SerializeToString(&metadataBuf);
|
||||
for (ReportRequestSet::iterator it=requests->begin(); it!=requests->end(); it++) {
|
||||
const sp<ReportRequest> request = *it;
|
||||
if (metadataBuf.empty() || request->fd < 0 || request->err != NO_ERROR) {
|
||||
continue;
|
||||
}
|
||||
write_section_header(request->fd, id, metadataBuf.size());
|
||||
write_all(request->fd, (uint8_t const*)metadataBuf.data(), metadataBuf.size());
|
||||
}
|
||||
if (requests->mainFd() >= 0 && !metadataBuf.empty()) {
|
||||
write_section_header(requests->mainFd(), id, metadataBuf.size());
|
||||
write_all(requests->mainFd(), (uint8_t const*)metadataBuf.data(), metadataBuf.size());
|
||||
}
|
||||
return NO_ERROR;
|
||||
}
|
||||
// ================================================================================
|
||||
FileSection::FileSection(int id, const char* filename, const int64_t timeoutMs)
|
||||
:Section(id, timeoutMs),
|
||||
|
||||
@@ -58,6 +58,18 @@ public:
|
||||
virtual status_t Execute(ReportRequestSet* requests) const;
|
||||
};
|
||||
|
||||
/**
|
||||
* Section that generates incident metadata.
|
||||
*/
|
||||
class MetadataSection : public Section
|
||||
{
|
||||
public:
|
||||
MetadataSection();
|
||||
virtual ~MetadataSection();
|
||||
|
||||
virtual status_t Execute(ReportRequestSet* requests) const;
|
||||
};
|
||||
|
||||
/**
|
||||
* Section that reads in a file.
|
||||
*/
|
||||
|
||||
@@ -180,3 +180,18 @@ TEST_F(ReporterTest, RunReportToGivenDirectory) {
|
||||
ASSERT_EQ((int)results.size(), 1);
|
||||
EXPECT_EQ(results[0], "\n\x2" "\b\f\n\x6" "\x12\x4" "abcd");
|
||||
}
|
||||
|
||||
TEST_F(ReporterTest, ReportMetadata) {
|
||||
IncidentReportArgs args;
|
||||
args.addSection(1);
|
||||
args.setDest(android::os::DEST_EXPLICIT);
|
||||
sp<ReportRequest> r = new ReportRequest(args, l, -1);
|
||||
reporter->batch.add(r);
|
||||
|
||||
ASSERT_EQ(Reporter::REPORT_FINISHED, reporter->runReport());
|
||||
auto metadata = reporter->batch.metadata();
|
||||
EXPECT_EQ(IncidentMetadata_Destination_EXPLICIT, metadata.dest());
|
||||
EXPECT_EQ(1, metadata.request_size());
|
||||
EXPECT_TRUE(metadata.use_dropbox());
|
||||
EXPECT_EQ(0, metadata.sections_size());
|
||||
}
|
||||
@@ -18,6 +18,7 @@
|
||||
|
||||
#include <android-base/file.h>
|
||||
#include <android-base/test_utils.h>
|
||||
#include <android/os/IncidentReportArgs.h>
|
||||
#include <frameworks/base/libs/incident/proto/android/os/header.pb.h>
|
||||
#include <gmock/gmock.h>
|
||||
#include <gtest/gtest.h>
|
||||
@@ -89,6 +90,18 @@ TEST(SectionTest, HeaderSection) {
|
||||
EXPECT_THAT(content, StrEq("\n\x05\x12\x03pup"));
|
||||
}
|
||||
|
||||
TEST(SectionTest, MetadataSection) {
|
||||
MetadataSection ms;
|
||||
ReportRequestSet requests;
|
||||
|
||||
requests.setMainFd(STDOUT_FILENO);
|
||||
requests.sectionStats(1)->set_success(true);
|
||||
|
||||
CaptureStdout();
|
||||
ASSERT_EQ(NO_ERROR, ms.Execute(&requests));
|
||||
EXPECT_THAT(GetCapturedStdout(), StrEq("\x12\b\x18\x1\"\x4\b\x1\x10\x1"));
|
||||
}
|
||||
|
||||
TEST(SectionTest, FileSection) {
|
||||
TemporaryFile tf;
|
||||
FileSection fs(REVERSE_PARSER, tf.path);
|
||||
@@ -243,8 +256,9 @@ TEST(SectionTest, TestMultipleRequests) {
|
||||
|
||||
IncidentReportArgs args1, args2, args3;
|
||||
args1.setAll(true);
|
||||
args1.setDest(0); // LOCAL
|
||||
args2.setAll(true); // default to explicit
|
||||
args1.setDest(android::os::DEST_LOCAL);
|
||||
args2.setAll(true);
|
||||
args2.setDest(android::os::DEST_EXPLICIT);
|
||||
sp<SimpleListener> l = new SimpleListener();
|
||||
requests.add(new ReportRequest(args1, l, output1.fd));
|
||||
requests.add(new ReportRequest(args2, l, output2.fd));
|
||||
@@ -283,10 +297,12 @@ TEST(SectionTest, TestMultipleRequestsBySpec) {
|
||||
|
||||
ASSERT_TRUE(WriteStringToFile(VARINT_FIELD_1 + STRING_FIELD_2 + FIX64_FIELD_3, input.path));
|
||||
|
||||
IncidentReportArgs args1, args2, args3, args4;
|
||||
IncidentReportArgs args1, args2, args3;
|
||||
args1.setAll(true);
|
||||
args1.setDest(android::os::DEST_EXPLICIT);
|
||||
args2.setAll(true);
|
||||
args4.setAll(true);
|
||||
args2.setDest(android::os::DEST_EXPLICIT);
|
||||
args3.setAll(true);
|
||||
sp<SimpleListener> l = new SimpleListener();
|
||||
requests.add(new ReportRequest(args1, l, output1.fd));
|
||||
requests.add(new ReportRequest(args2, l, output2.fd));
|
||||
@@ -307,7 +323,8 @@ TEST(SectionTest, TestMultipleRequestsBySpec) {
|
||||
EXPECT_TRUE(ReadFileToString(output2.path, &content));
|
||||
EXPECT_THAT(content, StrEq(string("\x02") + c + expect));
|
||||
|
||||
// because args3 doesn't set section, so it should receive nothing
|
||||
// output3 has only auto field
|
||||
c = (char) STRING_FIELD_2.size();
|
||||
EXPECT_TRUE(ReadFileToString(output3.path, &content));
|
||||
EXPECT_THAT(content, StrEq(""));
|
||||
EXPECT_THAT(content, StrEq(string("\x02") + c + STRING_FIELD_2));
|
||||
}
|
||||
@@ -46,18 +46,12 @@ import "frameworks/base/core/proto/android/service/usb.proto";
|
||||
import "frameworks/base/core/proto/android/util/event_log_tags.proto";
|
||||
import "frameworks/base/core/proto/android/util/log.proto";
|
||||
import "frameworks/base/libs/incident/proto/android/os/header.proto";
|
||||
import "frameworks/base/libs/incident/proto/android/os/metadata.proto";
|
||||
import "frameworks/base/libs/incident/proto/android/privacy.proto";
|
||||
import "frameworks/base/libs/incident/proto/android/section.proto";
|
||||
|
||||
package android.os;
|
||||
|
||||
// This field contains internal metadata associated with an incident report,
|
||||
// such as the section ids and privacy policy specs from caller as well as how long
|
||||
// and how many bytes a section takes, etc.
|
||||
message IncidentMetadata {
|
||||
|
||||
}
|
||||
|
||||
// privacy field options must not be set at this level because all
|
||||
// the sections are able to be controlled and configured by section ids.
|
||||
// Instead privacy field options need to be configured in each section proto message.
|
||||
|
||||
@@ -33,6 +33,7 @@ LOCAL_SRC_FILES := \
|
||||
../../core/java/android/os/IIncidentManager.aidl \
|
||||
../../core/java/android/os/IIncidentReportStatusListener.aidl \
|
||||
proto/android/os/header.proto \
|
||||
proto/android/os/metadata.proto \
|
||||
src/IncidentReportArgs.cpp
|
||||
|
||||
LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
|
||||
|
||||
63
libs/incident/proto/android/os/metadata.proto
Normal file
63
libs/incident/proto/android/os/metadata.proto
Normal file
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright (C) 2018 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.
|
||||
*/
|
||||
|
||||
syntax = "proto2";
|
||||
option java_multiple_files = true;
|
||||
|
||||
package android.os;
|
||||
|
||||
// This field contains internal metadata associated with an incident report,
|
||||
// such as the section ids and privacy policy specs from caller as well as how long
|
||||
// and how many bytes a section takes, etc.
|
||||
message IncidentMetadata {
|
||||
|
||||
// privacy level of the incident report.
|
||||
enum Destination {
|
||||
AUTOMATIC = 0;
|
||||
EXPLICIT = 1;
|
||||
LOCAL = 2;
|
||||
}
|
||||
optional Destination dest = 1;
|
||||
|
||||
optional int32 request_size = 2;
|
||||
|
||||
optional bool use_dropbox = 3;
|
||||
|
||||
// stats of each section taken in this incident report.
|
||||
message SectionStats {
|
||||
// section id.
|
||||
optional int32 id = 1;
|
||||
// if the section is successfully taken.
|
||||
optional bool success = 2;
|
||||
// number of bytes in the report after filtering.
|
||||
optional int32 report_size_bytes = 3;
|
||||
// the total duration to execute the section.
|
||||
optional int64 exec_duration_ms = 4;
|
||||
|
||||
// number of bytes dumped from the section directly.
|
||||
optional int32 dump_size_bytes = 5;
|
||||
// duration of the dump takes.
|
||||
optional int64 dump_duration_ms = 6;
|
||||
// true if the section timed out.
|
||||
optional bool timed_out = 7;
|
||||
// true if the section is truncated.
|
||||
optional bool is_truncated = 8;
|
||||
|
||||
// Next Tag: 9
|
||||
}
|
||||
repeated SectionStats sections = 4;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user