diff options
Diffstat (limited to 'cmds/incidentd/src/IncidentService.cpp')
-rw-r--r-- | cmds/incidentd/src/IncidentService.cpp | 306 |
1 files changed, 226 insertions, 80 deletions
diff --git a/cmds/incidentd/src/IncidentService.cpp b/cmds/incidentd/src/IncidentService.cpp index f8fb4a676ba0..4ba31b45e81c 100644 --- a/cmds/incidentd/src/IncidentService.cpp +++ b/cmds/incidentd/src/IncidentService.cpp @@ -19,7 +19,7 @@ #include "IncidentService.h" #include "FdBuffer.h" -#include "PrivacyBuffer.h" +#include "PrivacyFilter.h" #include "Reporter.h" #include "incidentd_util.h" #include "section_list.h" @@ -35,9 +35,12 @@ #include <unistd.h> -enum { WHAT_RUN_REPORT = 1, WHAT_SEND_BACKLOG_TO_DROPBOX = 2 }; +enum { + WHAT_TAKE_REPORT = 1, + WHAT_SEND_BROADCASTS = 2 +}; -#define DEFAULT_BACKLOG_DELAY_NS (1000000000LL) +#define DEFAULT_DELAY_NS (1000000000LL) #define DEFAULT_BYTES_SIZE_LIMIT (20 * 1024 * 1024) // 20MB #define DEFAULT_REFACTORY_PERIOD_MS (24 * 60 * 60 * 1000) // 1 Day @@ -53,6 +56,7 @@ namespace android { namespace os { namespace incidentd { +String16 const APPROVE_INCIDENT_REPORTS("android.permission.APPROVE_INCIDENT_REPORTS"); String16 const DUMP_PERMISSION("android.permission.DUMP"); String16 const USAGE_STATS_PERMISSION("android.permission.PACKAGE_USAGE_STATS"); @@ -60,7 +64,14 @@ static Status checkIncidentPermissions(const IncidentReportArgs& args) { uid_t callingUid = IPCThreadState::self()->getCallingUid(); pid_t callingPid = IPCThreadState::self()->getCallingPid(); if (callingUid == AID_ROOT || callingUid == AID_SHELL) { - // root doesn't have permission.DUMP if don't do this! + // Root and shell are ok. + return Status::ok(); + } + + if (checkCallingPermission(APPROVE_INCIDENT_REPORTS)) { + // Permission controller (this is a singleton permission that is always granted + // exactly for PermissionController) is allowed to access incident reports + // so it can show the user info about what they are approving. return Status::ok(); } @@ -81,8 +92,8 @@ static Status checkIncidentPermissions(const IncidentReportArgs& args) { } // checking calling request uid permission. - switch (args.dest()) { - case DEST_LOCAL: + switch (args.getPrivacyPolicy()) { + case PRIVACY_POLICY_LOCAL: if (callingUid != AID_SHELL && callingUid != AID_ROOT) { ALOGW("Calling pid %d and uid %d does not have permission to get local data.", callingPid, callingUid); @@ -91,7 +102,7 @@ static Status checkIncidentPermissions(const IncidentReportArgs& args) { "Calling process does not have permission to get local data."); } break; - case DEST_EXPLICIT: + case PRIVACY_POLICY_EXPLICIT: if (callingUid != AID_SHELL && callingUid != AID_ROOT && callingUid != AID_STATSD && callingUid != AID_SYSTEM) { ALOGW("Calling pid %d and uid %d does not have permission to get explicit data.", @@ -105,78 +116,79 @@ static Status checkIncidentPermissions(const IncidentReportArgs& args) { return Status::ok(); } -// ================================================================================ -ReportRequestQueue::ReportRequestQueue() {} - -ReportRequestQueue::~ReportRequestQueue() {} - -void ReportRequestQueue::addRequest(const sp<ReportRequest>& request) { - unique_lock<mutex> lock(mLock); - mQueue.push_back(request); -} - -sp<ReportRequest> ReportRequestQueue::getNextRequest() { - unique_lock<mutex> lock(mLock); - if (mQueue.empty()) { - return NULL; - } else { - sp<ReportRequest> front(mQueue.front()); - mQueue.pop_front(); - return front; - } +static string build_uri(const string& pkg, const string& cls, const string& id) { + return "build_uri not implemented " + pkg + "/" + cls + "/" + id; } // ================================================================================ -ReportHandler::ReportHandler(const sp<Looper>& handlerLooper, const sp<ReportRequestQueue>& queue, - const sp<Throttler>& throttler) - : mBacklogDelay(DEFAULT_BACKLOG_DELAY_NS), - mHandlerLooper(handlerLooper), - mQueue(queue), - mThrottler(throttler) {} +ReportHandler::ReportHandler(const sp<WorkDirectory>& workDirectory, + const sp<Broadcaster>& broadcaster, const sp<Looper>& handlerLooper, + const sp<Throttler>& throttler) + :mLock(), + mWorkDirectory(workDirectory), + mBroadcaster(broadcaster), + mHandlerLooper(handlerLooper), + mBacklogDelay(DEFAULT_DELAY_NS), + mThrottler(throttler), + mBatch(new ReportBatch()) { +} -ReportHandler::~ReportHandler() {} +ReportHandler::~ReportHandler() { +} void ReportHandler::handleMessage(const Message& message) { switch (message.what) { - case WHAT_RUN_REPORT: - run_report(); + case WHAT_TAKE_REPORT: + take_report(); break; - case WHAT_SEND_BACKLOG_TO_DROPBOX: - send_backlog_to_dropbox(); + case WHAT_SEND_BROADCASTS: + send_broadcasts(); break; } } -void ReportHandler::scheduleRunReport(const sp<ReportRequest>& request) { - mQueue->addRequest(request); - mHandlerLooper->removeMessages(this, WHAT_RUN_REPORT); - mHandlerLooper->sendMessage(this, Message(WHAT_RUN_REPORT)); +void ReportHandler::schedulePersistedReport(const IncidentReportArgs& args) { + mBatch->addPersistedReport(args); + mHandlerLooper->removeMessages(this, WHAT_TAKE_REPORT); + mHandlerLooper->sendMessage(this, Message(WHAT_TAKE_REPORT)); +} + +void ReportHandler::scheduleStreamingReport(const IncidentReportArgs& args, + const sp<IIncidentReportStatusListener>& listener, int streamFd) { + mBatch->addStreamingReport(args, listener, streamFd); + mHandlerLooper->removeMessages(this, WHAT_TAKE_REPORT); + mHandlerLooper->sendMessage(this, Message(WHAT_TAKE_REPORT)); } -void ReportHandler::scheduleSendBacklogToDropbox() { +void ReportHandler::scheduleSendBacklog() { unique_lock<mutex> lock(mLock); - mBacklogDelay = DEFAULT_BACKLOG_DELAY_NS; - schedule_send_backlog_to_dropbox_locked(); + mBacklogDelay = DEFAULT_DELAY_NS; + schedule_send_broadcasts_locked(); } -void ReportHandler::schedule_send_backlog_to_dropbox_locked() { - mHandlerLooper->removeMessages(this, WHAT_SEND_BACKLOG_TO_DROPBOX); - mHandlerLooper->sendMessageDelayed(mBacklogDelay, this, Message(WHAT_SEND_BACKLOG_TO_DROPBOX)); +void ReportHandler::schedule_send_broadcasts_locked() { + mHandlerLooper->removeMessages(this, WHAT_SEND_BROADCASTS); + mHandlerLooper->sendMessageDelayed(mBacklogDelay, this, Message(WHAT_SEND_BROADCASTS)); } -void ReportHandler::run_report() { - sp<Reporter> reporter = new Reporter(); +void ReportHandler::take_report() { + // Cycle the batch + sp<ReportBatch> batch; + { + unique_lock<mutex> lock(mLock); + batch = mBatch; + mBatch = new ReportBatch(); + } - // Merge all of the requests into one that has all of the - // requested fields. - while (true) { - sp<ReportRequest> request = mQueue->getNextRequest(); - if (request == NULL) { - break; - } - reporter->batch.add(request); + if (batch->empty()) { + // Nothing to do. + return; } + sp<Reporter> reporter = new Reporter(mWorkDirectory, batch); + + // TODO: Do we really want to clear the reports if we throttle? Should we only throttle + // requests going to dropbox? How do we reconcile throttling with testing? if (mThrottler->shouldThrottle()) { ALOGW("RunReport got throttled."); return; @@ -185,46 +197,76 @@ void ReportHandler::run_report() { // 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. // TODO: We should further rate-limit the reports to no more than N per time-period. + // TODO: Move this inside reporter. size_t reportByteSize = 0; - Reporter::run_report_status_t reportStatus = reporter->runReport(&reportByteSize); + reporter->runReport(&reportByteSize); + mThrottler->addReportSize(reportByteSize); - if (reportStatus == Reporter::REPORT_NEEDS_DROPBOX) { + + // Kick off the next steps, one of which is to send any new or otherwise remaining + // approvals, and one of which is to send any new or remaining broadcasts. + { unique_lock<mutex> lock(mLock); - schedule_send_backlog_to_dropbox_locked(); + schedule_send_broadcasts_locked(); } } -void ReportHandler::send_backlog_to_dropbox() { - if (Reporter::upload_backlog() == Reporter::REPORT_NEEDS_DROPBOX) { +void ReportHandler::send_broadcasts() { + Broadcaster::broadcast_status_t result = mBroadcaster->sendBroadcasts(); + if (result == Broadcaster::BROADCASTS_FINISHED) { + // We're done. + unique_lock<mutex> lock(mLock); + mBacklogDelay = DEFAULT_DELAY_NS; + } else if (result == Broadcaster::BROADCASTS_REPEAT) { + // It worked, but there are more. + unique_lock<mutex> lock(mLock); + mBacklogDelay = DEFAULT_DELAY_NS; + schedule_send_broadcasts_locked(); + } else if (result == Broadcaster::BROADCASTS_BACKOFF) { // There was a failure. Exponential backoff. unique_lock<mutex> lock(mLock); mBacklogDelay *= 2; ALOGI("Error sending to dropbox. Trying again in %lld minutes", (mBacklogDelay / (1000000000LL * 60))); - schedule_send_backlog_to_dropbox_locked(); - } else { - mBacklogDelay = DEFAULT_BACKLOG_DELAY_NS; + schedule_send_broadcasts_locked(); } } // ================================================================================ -IncidentService::IncidentService(const sp<Looper>& handlerLooper) - : mQueue(new ReportRequestQueue()), - mThrottler(new Throttler(DEFAULT_BYTES_SIZE_LIMIT, DEFAULT_REFACTORY_PERIOD_MS)) { - mHandler = new ReportHandler(handlerLooper, mQueue, mThrottler); +IncidentService::IncidentService(const sp<Looper>& handlerLooper) { + mThrottler = new Throttler(DEFAULT_BYTES_SIZE_LIMIT, DEFAULT_REFACTORY_PERIOD_MS); + mWorkDirectory = new WorkDirectory(); + mBroadcaster = new Broadcaster(mWorkDirectory); + mHandler = new ReportHandler(mWorkDirectory, mBroadcaster, handlerLooper, + mThrottler); + mBroadcaster->setHandler(mHandler); } IncidentService::~IncidentService() {} Status IncidentService::reportIncident(const IncidentReportArgs& args) { - ALOGI("reportIncident"); + // TODO: Validate that the privacy policy is one of the real ones. + // If it isn't, clamp it to the next more restrictive real one. + // TODO: This function should reject the LOCAL privacy policy. + // Those have to stream. + + // TODO: Check that the broadcast recevier has the proper permissions + // TODO: Maybe we should consider relaxing the permissions if it's going to + // dropbox, but definitely not if it's going to the broadcaster. Status status = checkIncidentPermissions(args); if (!status.isOk()) { return status; } - mHandler->scheduleRunReport(new ReportRequest(args, NULL, -1)); + // If they didn't specify a component, use dropbox. + IncidentReportArgs argsCopy(args); + if (argsCopy.receiverPkg().length() == 0 && argsCopy.receiverCls().length() == 0) { + argsCopy.setReceiverPkg(DROPBOX_SENTINEL.getPackageName()); + argsCopy.setReceiverCls(DROPBOX_SENTINEL.getClassName()); + } + + mHandler->schedulePersistedReport(argsCopy); return Status::ok(); } @@ -232,19 +274,29 @@ Status IncidentService::reportIncident(const IncidentReportArgs& args) { Status IncidentService::reportIncidentToStream(const IncidentReportArgs& args, const sp<IIncidentReportStatusListener>& listener, const unique_fd& stream) { - ALOGI("reportIncidentToStream"); + // TODO: Validate that the privacy policy is one of the real ones. + // If it isn't, clamp it to the next more restrictive real one. - Status status = checkIncidentPermissions(args); + // TODO: Only shell should be able to do a LOCAL privacy policy report. + + // Streaming reports can not also be broadcast. + IncidentReportArgs argsCopy(args); + argsCopy.setReceiverPkg(""); + argsCopy.setReceiverCls(""); + + Status status = checkIncidentPermissions(argsCopy); if (!status.isOk()) { return status; } + + // 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->scheduleRunReport(new ReportRequest(args, listener, fd)); + mHandler->scheduleStreamingReport(argsCopy, listener, fd); return Status::ok(); } @@ -256,7 +308,92 @@ Status IncidentService::systemRunning() { } // When system_server is up and running, schedule the dropbox task to run. - mHandler->scheduleSendBacklogToDropbox(); + mBroadcaster->reset(); + mHandler->scheduleSendBacklog(); + + return Status::ok(); +} + +Status IncidentService::getIncidentReportList(const String16& pkg16, const String16& cls16, + vector<String16>* result) { + status_t err; + const string pkg(String8(pkg16).string()); + const string cls(String8(cls16).string()); + + // List the reports + vector<sp<ReportFile>> all; + err = mWorkDirectory->getReports(&all, 0); + if (err != NO_ERROR) { + return Status::fromStatusT(err); + } + + // Find the ones that match pkg and cls. + for (sp<ReportFile>& file: all) { + err = file->loadEnvelope(); + if (err != NO_ERROR) { + continue; + } + const ReportFileProto& envelope = file->getEnvelope(); + size_t reportCount = envelope.report_size(); + for (int reportIndex = 0; reportIndex < reportCount; reportIndex++) { + const ReportFileProto_Report& report = envelope.report(reportIndex); + if (pkg == report.pkg() && cls == report.cls()) { + result->push_back(String16(build_uri(pkg, cls, file->getId()).c_str())); + break; + } + } + } + + return Status::ok(); +} + +Status IncidentService::getIncidentReport(const String16& pkg16, const String16& cls16, + const String16& id16, IncidentManager::IncidentReport* result) { + status_t err; + + const string pkg(String8(pkg16).string()); + const string cls(String8(cls16).string()); + const string id(String8(id16).string()); + + IncidentReportArgs args; + sp<ReportFile> file = mWorkDirectory->getReport(pkg, cls, id, &args); + if (file != nullptr) { + int fd; + err = file->startFilteringData(&fd, args); + if (err != 0) { + ALOGW("Error reading data file that we think should exist: %s", + file->getDataFileName().c_str()); + return Status::ok(); + } + + result->setTimestampNs(file->getTimestampNs()); + result->setPrivacyPolicy(file->getEnvelope().privacy_policy()); + result->takeFileDescriptor(fd); + } + + return Status::ok(); +} + +Status IncidentService::deleteIncidentReports(const String16& pkg16, const String16& cls16, + const String16& id16) { + const string pkg(String8(pkg16).string()); + const string cls(String8(cls16).string()); + const string id(String8(id16).string()); + + sp<ReportFile> file = mWorkDirectory->getReport(pkg, cls, id, nullptr); + if (file != nullptr) { + mWorkDirectory->commit(file, pkg, cls); + } + mBroadcaster->clearBroadcasts(pkg, cls, id); + + return Status::ok(); +} + +Status IncidentService::deleteAllIncidentReports(const String16& pkg16) { + const string pkg(String8(pkg16).string()); + + mWorkDirectory->commitAll(pkg); + mBroadcaster->clearPackageBroadcasts(pkg); return Status::ok(); } @@ -354,7 +491,7 @@ status_t IncidentService::cmd_help(FILE* out) { static void printPrivacy(const Privacy* p, FILE* out, String8 indent) { if (p == NULL) return; - fprintf(out, "%sid:%d, type:%d, dest:%d\n", indent.string(), p->field_id, p->type, p->dest); + fprintf(out, "%sid:%d, type:%d, dest:%d\n", indent.string(), p->field_id, p->type, p->policy); if (p->children == NULL) return; for (int i = 0; p->children[i] != NULL; i++) { // NULL-terminated. printPrivacy(p->children[i], out, indent + " "); @@ -362,6 +499,8 @@ static void printPrivacy(const Privacy* p, FILE* out, String8 indent) { } status_t IncidentService::cmd_privacy(FILE* in, FILE* out, FILE* err, Vector<String8>& args) { + (void)in; + const int argCount = args.size(); if (argCount >= 3) { String8 opt = args[1]; @@ -376,6 +515,7 @@ status_t IncidentService::cmd_privacy(FILE* in, FILE* out, FILE* err, Vector<Str if (opt == "print") { printPrivacy(p, out, String8("")); } else if (opt == "parse") { + /* FdBuffer buf; status_t error = buf.read(fileno(in), 60000); if (error != NO_ERROR) { @@ -383,15 +523,17 @@ status_t IncidentService::cmd_privacy(FILE* in, FILE* out, FILE* err, Vector<Str return error; } fprintf(err, "Read %zu bytes\n", buf.size()); - PrivacyBuffer pBuf(p, buf.data()); + PrivacyFilter pBuf(p, buf.data()); PrivacySpec spec = PrivacySpec::new_spec(argCount > 3 ? atoi(args[3]) : -1); error = pBuf.strip(spec); if (error != NO_ERROR) { - fprintf(err, "Error strip pii fields with spec %d\n", spec.dest); + fprintf(err, "Error strip pii fields with spec %d\n", spec.policy); return error; } return pBuf.flush(fileno(out)); + */ + return -1; } } else { return cmd_help(out); @@ -408,7 +550,7 @@ status_t IncidentService::dump(int fd, const Vector<String16>& args) { ALOGD("Dump incident proto"); IncidentReportArgs incidentArgs; - incidentArgs.setDest(DEST_EXPLICIT); + incidentArgs.setPrivacyPolicy(PRIVACY_POLICY_EXPLICIT); int skipped[] = SKIPPED_SECTIONS; for (const Section** section = SECTION_LIST; *section; section++) { const int id = (*section)->id; @@ -421,12 +563,16 @@ status_t IncidentService::dump(int fd, const Vector<String16>& args) { 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; } - mHandler->scheduleRunReport(new ReportRequest(incidentArgs, NULL, fd1)); + // 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; } |