/* * Copyright (C) 2016 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. */ #include "Log.h" #include "Broadcaster.h" #include "IncidentService.h" #include #include #include namespace android { namespace os { namespace incidentd { using android::os::IIncidentCompanion; using binder::Status; // ============================================================ Broadcaster::ConsentListener::ConsentListener(const sp& broadcaster, const ReportId& reportId) :mBroadcaster(broadcaster), mId(reportId) { } Broadcaster::ConsentListener::~ConsentListener() { } Status Broadcaster::ConsentListener::onReportApproved() { mBroadcaster->report_approved(mId); return Status::ok(); } Status Broadcaster::ConsentListener::onReportDenied() { mBroadcaster->report_denied(mId); return Status::ok(); } // ============================================================ Broadcaster::ReportId::ReportId() :id(), pkg(), cls() { } Broadcaster::ReportId::ReportId(const ReportId& that) :id(that.id), pkg(that.pkg), cls(that.cls) { } Broadcaster::ReportId::ReportId(const string& i, const string& p, const string& c) :id(i), pkg(p), cls(c) { } Broadcaster::ReportId::~ReportId() { } bool Broadcaster::ReportId::operator<(const ReportId& that) const { if (id < that.id) { return true; } if (id > that.id) { return false; } if (pkg < that.pkg) { return true; } if (pkg > that.pkg) { return false; } if (cls < that.cls) { return true; } return false; } // ============================================================ Broadcaster::ReportStatus::ReportStatus() :approval_sent(false), ready_sent(false), listener(nullptr) { } Broadcaster::ReportStatus::ReportStatus(const ReportStatus& that) :approval_sent(that.approval_sent), ready_sent(that.ready_sent), listener(that.listener) { } Broadcaster::ReportStatus::~ReportStatus() { } // ============================================================ Broadcaster::Broadcaster(const sp& workDirectory) :mReportHandler(), mWorkDirectory(workDirectory) { } void Broadcaster::setHandler(const sp& handler) { mReportHandler = handler; } void Broadcaster::reset() { unique_lock lock(mLock); mLastSent = 0; mHistory.clear(); // Could cancel the listeners, but this happens when // the system process crashes, so don't bother. } void Broadcaster::clearBroadcasts(const string& pkg, const string& cls, const string& id) { unique_lock lock(mLock); map::const_iterator found = mHistory.find(ReportId(id, pkg, cls)); if (found != mHistory.end()) { if (found->second.listener != nullptr) { sp ics = get_incident_companion(); if (ics != nullptr) { ics->cancelAuthorization(found->second.listener); } } mHistory.erase(found); } } void Broadcaster::clearPackageBroadcasts(const string& pkg) { unique_lock lock(mLock); map::iterator it = mHistory.begin(); while (it != mHistory.end()) { if (it->first.pkg == pkg) { if (it->second.listener != nullptr) { sp ics = get_incident_companion(); if (ics != nullptr) { ics->cancelAuthorization(it->second.listener); } } it = mHistory.erase(it); } else { it++; } } } Broadcaster::broadcast_status_t Broadcaster::sendBroadcasts() { int err; int64_t lastSent = get_last_sent(); vector> files; mWorkDirectory->getReports(&files, 0); //lastSent); // Don't send multiple broadcasts to the same receiver. set reportReadyBroadcasts; for (const sp& file: files) { err = file->loadEnvelope(); if (err != NO_ERROR) { ALOGW("Error (%s) loading envelope from %s", strerror(-err), file->getEnvelopeFileName().c_str()); continue; } const ReportFileProto& envelope = file->getEnvelope(); if (!envelope.completed()) { ALOGI("Incident report not completed skipping it: %s", file->getEnvelopeFileName().c_str()); continue; } // When one of the broadcast functions in this loop fails, it's almost // certainly because the system process is crashing or has crashed. Rather // than continuing to pound on the system process and potentially make things // worse, we bail right away, return BROADCASTS_BACKOFF, and we will try // again later. In the meantime, if the system process did crash, it might // clear out mHistory, which means we'll be back here again to send the // backlog. size_t reportCount = envelope.report_size(); bool hasApprovalPending = false; for (int reportIndex = 0; reportIndex < reportCount; reportIndex++) { const ReportFileProto_Report& report = envelope.report(reportIndex); status_t err; if (report.privacy_policy() == PRIVACY_POLICY_AUTOMATIC || report.share_approved()) { // It's privacy policy is AUTO, or it's been approved, // so send the actual broadcast. if (!was_ready_sent(file->getId(), report.pkg(), report.cls())) { if (report.pkg() == DROPBOX_SENTINEL.getPackageName() && report.cls() == DROPBOX_SENTINEL.getClassName()) { IncidentReportArgs args; get_args_from_report(&args, report); err = send_to_dropbox(file, args); if (err != NO_ERROR) { return BROADCASTS_BACKOFF; } } else { reportReadyBroadcasts.insert(ReportId(file->getId(), report.pkg(), report.cls())); } } } else { // It's not approved yet, so send the approval. if (!was_approval_sent(file->getId(), report.pkg(), report.cls())) { err = send_approval_broadcasts(file->getId(), report.pkg(), report.cls()); if (err != NO_ERROR) { return BROADCASTS_BACKOFF; } hasApprovalPending = true; } } } lastSent = file->getTimestampNs(); if (!hasApprovalPending) { set_last_sent(lastSent); } } for (const ReportId& report: reportReadyBroadcasts) { err = send_report_ready_broadcasts(report.id, report.pkg, report.cls); if (err != NO_ERROR) { return BROADCASTS_BACKOFF; } } return mWorkDirectory->hasMore(lastSent) ? BROADCASTS_REPEAT : BROADCASTS_FINISHED; } void Broadcaster::set_last_sent(int64_t timestamp) { unique_lock lock(mLock); mLastSent = timestamp; } int64_t Broadcaster::get_last_sent() { unique_lock lock(mLock); return mLastSent; } /* void Broadcaster::printReportStatuses() const { ALOGD("mHistory {"); for (map::const_iterator it = mHistory.begin(); it != mHistory.end(); it++) { ALOGD(" [%s %s] --> [%d %d]", it->first.id.c_str(), it->first.pkg.c_str(), it->second.approval_sent, it->second.ready_sent); } ALOGD("}"); } */ bool Broadcaster::was_approval_sent(const string& id, const string& pkg, const string& cls) { unique_lock lock(mLock); map::const_iterator found = mHistory.find(ReportId(id, pkg, cls)); if (found != mHistory.end()) { return found->second.approval_sent; } return false; } void Broadcaster::set_approval_sent(const string& id, const string& pkg, const string& cls, const sp& listener) { unique_lock lock(mLock); ReportStatus& reportStatus = mHistory[ReportId(id, pkg, cls)]; reportStatus.approval_sent = true; reportStatus.listener = listener; } bool Broadcaster::was_ready_sent(const string& id, const string& pkg, const string& cls) { unique_lock lock(mLock); map::const_iterator found = mHistory.find(ReportId(id, pkg, cls)); if (found != mHistory.end()) { return found->second.ready_sent; } return false; } void Broadcaster::set_ready_sent(const string& id, const string& pkg, const string& cls) { unique_lock lock(mLock); mHistory[ReportId(id, pkg, cls)].ready_sent = true; } status_t Broadcaster::send_approval_broadcasts(const string& id, const string& pkg, const string& cls) { sp ics = get_incident_companion(); if (ics == nullptr) { return NAME_NOT_FOUND; } sp listener = new ConsentListener(this, ReportId(id, pkg, cls)); ALOGI("send_approval_broadcasts for %s %s/%s", id.c_str(), pkg.c_str(), cls.c_str()); Status status = ics->authorizeReport(0, String16(pkg.c_str()), String16(cls.c_str()), String16(id.c_str()), 0, listener); if (!status.isOk()) { // authorizeReport is oneway, so any error is a transaction error. return status.transactionError(); } set_approval_sent(id, pkg, cls, listener); return NO_ERROR; } void Broadcaster::report_approved(const ReportId& reportId) { status_t err; // Kick off broadcaster to do send the ready broadcasts. ALOGI("The user approved the report, so kicking off another broadcast pass. %s %s/%s", reportId.id.c_str(), reportId.pkg.c_str(), reportId.cls.c_str()); sp file = mWorkDirectory->getReport(reportId.pkg, reportId.cls, reportId.id, nullptr); if (file != nullptr) { err = file->loadEnvelope(); if (err != NO_ERROR) { return; } err = file->markApproved(reportId.pkg, reportId.cls); if (err != NO_ERROR) { ALOGI("Couldn't find report that was just approved: %s %s/%s", reportId.id.c_str(), reportId.pkg.c_str(), reportId.cls.c_str()); return; } file->saveEnvelope(); if (err != NO_ERROR) { return; } } mReportHandler->scheduleSendBacklog(); } void Broadcaster::report_denied(const ReportId& reportId) { // The user didn't approve the report, so remove it from the WorkDirectory. ALOGI("The user denied the report, so deleting it. %s %s/%s", reportId.id.c_str(), reportId.pkg.c_str(), reportId.cls.c_str()); sp file = mWorkDirectory->getReport(reportId.pkg, reportId.cls, reportId.id, nullptr); if (file != nullptr) { mWorkDirectory->commit(file, reportId.pkg, reportId.cls); } } status_t Broadcaster::send_report_ready_broadcasts(const string& id, const string& pkg, const string& cls) { sp ics = get_incident_companion(); if (ics == nullptr) { return NAME_NOT_FOUND; } ALOGI("send_report_ready_broadcasts for %s %s/%s", id.c_str(), pkg.c_str(), cls.c_str()); Status status = ics->sendReportReadyBroadcast(String16(pkg.c_str()), String16(cls.c_str())); if (!status.isOk()) { // sendReportReadyBroadcast is oneway, so any error is a transaction error. return status.transactionError(); } set_ready_sent(id, pkg, cls); return NO_ERROR; } status_t Broadcaster::send_to_dropbox(const sp& file, const IncidentReportArgs& args) { status_t err; sp dropbox = new DropBoxManager(); if (dropbox == nullptr) { ALOGW("Can't reach dropbox now, so we won't be able to write the incident report to there"); return NO_ERROR; } int fds[2]; if (pipe(fds) != 0) { ALOGW("Error opening pipe to filter incident report: %s", file->getDataFileName().c_str()); return NO_ERROR; } int readFd = fds[0]; int writeFd = fds[1]; // spawn a thread to write the data. Release the writeFd ownership to the thread. thread th([file, writeFd, args]() { file->startFilteringData(writeFd, args); }); th.detach(); // Takes ownership of readFd. Status status = dropbox->addFile(String16("incident"), readFd, 0); if (!status.isOk()) { // TODO: This may or may not leak the readFd, depending on where it failed. // Not sure how to fix this given the dropbox API. ALOGW("Error sending incident report to dropbox."); return -errno; } // On successful write, tell the working directory that this file is done. mWorkDirectory->commit(file, DROPBOX_SENTINEL.getPackageName(), DROPBOX_SENTINEL.getClassName()); // Don't need to call set_ready_sent, because we just removed it from the ReportFile, // so we'll never hear about it again. return NO_ERROR; } sp Broadcaster::get_incident_companion() { sp binder = defaultServiceManager()->getService(String16("incidentcompanion")); if (binder == nullptr) { ALOGI("Can not find IIncidentCompanion service to send broadcast. Will try again later."); return nullptr; } sp ics = interface_cast(binder); if (ics == nullptr) { ALOGI("The incidentcompanion service is not an IIncidentCompanion. Will try again later."); return nullptr; } return ics; } } // namespace incidentd } // namespace os } // namespace android