Loading Android.bp +42 −5 Original line number Diff line number Diff line Loading @@ -721,6 +721,7 @@ java_defaults { "frameworks/av/camera/aidl", "frameworks/av/media/libaudioclient/aidl", "frameworks/native/aidl/gui", "frameworks/native/libs/incidentcompanion/binder", "system/core/storaged/binder", "system/vold/binder", "system/gsid/aidl", Loading Loading @@ -968,7 +969,10 @@ java_library { output_params: ["store_unknown_fields=true"], include_dirs: ["external/protobuf/src"], }, exclude_srcs: [ "core/proto/android/privacy.proto", "core/proto/android/section.proto", ], sdk_version: "current", srcs: [ "core/proto/**/*.proto", Loading @@ -988,6 +992,10 @@ java_library { "core/proto/**/*.proto", "libs/incident/proto/android/os/**/*.proto", ], exclude_srcs: [ "core/proto/android/privacy.proto", "core/proto/android/section.proto", ], // Protos have lots of MissingOverride and similar. errorprone: { javacflags: ["-XepDisableAllChecks"], Loading @@ -995,9 +1003,9 @@ java_library { } // ==== c++ proto device library ============================== cc_library { name: "libplatformprotos", host_supported: true, cc_defaults { name: "libplatformprotos-defaults", proto: { export_proto_headers: true, include_dirs: ["external/protobuf/src"], Loading @@ -1011,8 +1019,13 @@ cc_library { srcs: [ "core/proto/**/*.proto", "libs/incident/**/*.proto", ], } cc_library { name: "libplatformprotos", defaults: ["libplatformprotos-defaults"], host_supported: true, target: { host: { Loading @@ -1024,6 +1037,9 @@ cc_library { proto: { type: "lite", }, shared_libs: [ "libprotobuf-cpp-lite", ], shared: { enabled: false, }, Loading @@ -1031,6 +1047,26 @@ cc_library { }, } // This is the full proto version of libplatformprotos. It may only // be used by test code that is not shipped on the device. cc_library { name: "libplatformprotos-test", defaults: ["libplatformprotos-defaults"], host_supported: false, target: { android: { proto: { type: "full", }, shared: { enabled: false, }, }, }, } gensrcs { name: "gen-platform-proto-constants", depfile: true, Loading Loading @@ -1068,6 +1104,7 @@ gensrcs { output_extension: "proto.h", } subdirs = [ "cmds/*", "core/*", Loading cmds/incident/Android.bp +4 −0 Original line number Diff line number Diff line Loading @@ -29,6 +29,10 @@ cc_binary { "libincident", ], static_libs: [ "libplatformprotos", ], cflags: [ "-Wall", "-Werror", Loading cmds/incident/main.cpp +80 −15 Original line number Diff line number Diff line Loading @@ -68,6 +68,7 @@ Status StatusListener::onReportSectionStatus(int32_t section, int32_t status) { fprintf(stderr, "section %d status %d\n", section, status); ALOGD("section %d status %d\n", section, status); return Status::ok(); } Loading @@ -75,6 +76,7 @@ Status StatusListener::onReportServiceStatus(const String16& service, int32_t status) { fprintf(stderr, "service '%s' status %d\n", String8(service).string(), status); ALOGD("service '%s' status %d\n", String8(service).string(), status); return Status::ok(); } Loading @@ -82,6 +84,7 @@ Status StatusListener::onReportFinished() { fprintf(stderr, "done\n"); ALOGD("done\n"); exit(0); return Status::ok(); } Loading @@ -90,6 +93,7 @@ Status StatusListener::onReportFailed() { fprintf(stderr, "failed\n"); ALOGD("failed\n"); exit(1); return Status::ok(); } Loading Loading @@ -146,24 +150,49 @@ find_section(const char* name) // ================================================================================ static int get_dest(const char* arg) get_privacy_policy(const char* arg) { if (strcmp(arg, "L") == 0 || strcmp(arg, "LOCAL") == 0) { return DEST_LOCAL; return PRIVACY_POLICY_LOCAL; } if (strcmp(arg, "E") == 0 || strcmp(arg, "EXPLICIT") == 0) { return DEST_EXPLICIT; return PRIVACY_POLICY_EXPLICIT; } if (strcmp(arg, "A") == 0 || strcmp(arg, "AUTO") == 0 || strcmp(arg, "AUTOMATIC") == 0) { return DEST_AUTOMATIC; return PRIVACY_POLICY_AUTOMATIC; } return -1; // return the default value } // ================================================================================ static bool parse_receiver_arg(const string& arg, string* pkg, string* cls) { if (arg.length() == 0) { return true; } size_t slash = arg.find('/'); if (slash == string::npos) { return false; } if (slash == 0 || slash == arg.length() - 1) { return false; } if (arg.find('/', slash+1) != string::npos) { return false; } pkg->assign(arg, 0, slash); cls->assign(arg, slash+1); if ((*cls)[0] == '.') { *cls = (*pkg) + (*cls); } return true; } // ================================================================================ static void usage(FILE* out) Loading @@ -173,10 +202,13 @@ usage(FILE* out) fprintf(out, "Takes an incident report.\n"); fprintf(out, "\n"); fprintf(out, "OPTIONS\n"); fprintf(out, " -l list available sections\n"); fprintf(out, " -p privacy spec, LOCAL, EXPLICIT or AUTOMATIC. Default AUTOMATIC.\n"); fprintf(out, "\n"); fprintf(out, "and one of these destinations:\n"); fprintf(out, " -b (default) print the report to stdout (in proto format)\n"); fprintf(out, " -d send the report into dropbox\n"); fprintf(out, " -l list available sections\n"); fprintf(out, " -p privacy spec, LOCAL, EXPLICIT or AUTOMATIC\n"); fprintf(out, " -s PKG/CLS send broadcast to the broadcast receiver.\n"); fprintf(out, "\n"); fprintf(out, " SECTION the field numbers of the incident report fields to include\n"); fprintf(out, "\n"); Loading @@ -187,12 +219,13 @@ main(int argc, char** argv) { Status status; IncidentReportArgs args; enum { DEST_DROPBOX, DEST_STDOUT } destination = DEST_STDOUT; int dest = -1; // default enum { DEST_UNSET, DEST_DROPBOX, DEST_STDOUT, DEST_BROADCAST } destination = DEST_UNSET; int privacyPolicy = PRIVACY_POLICY_AUTOMATIC; string receiverArg; // Parse the args int opt; while ((opt = getopt(argc, argv, "bhdlp:")) != -1) { while ((opt = getopt(argc, argv, "bhdlp:s:")) != -1) { switch (opt) { case 'h': usage(stdout); Loading @@ -201,13 +234,29 @@ main(int argc, char** argv) section_list(stdout); return 0; case 'b': if (!(destination == DEST_UNSET || destination == DEST_STDOUT)) { usage(stderr); return 1; } destination = DEST_STDOUT; break; case 'd': if (!(destination == DEST_UNSET || destination == DEST_DROPBOX)) { usage(stderr); return 1; } destination = DEST_DROPBOX; break; case 'p': dest = get_dest(optarg); privacyPolicy = get_privacy_policy(optarg); break; case 's': if (destination != DEST_UNSET) { usage(stderr); return 1; } destination = DEST_BROADCAST; receiverArg = optarg; break; default: usage(stderr); Loading @@ -215,6 +264,17 @@ main(int argc, char** argv) } } string pkg; string cls; if (parse_receiver_arg(receiverArg, &pkg, &cls)) { args.setReceiverPkg(pkg); args.setReceiverCls(cls); } else { fprintf(stderr, "badly formatted -s package/class option: %s\n\n", receiverArg.c_str()); usage(stderr); return 1; } if (optind == argc) { args.setAll(true); } else { Loading @@ -236,7 +296,7 @@ main(int argc, char** argv) } } } args.setDest(dest); args.setPrivacyPolicy(privacyPolicy); // Start the thread pool. sp<ProcessState> ps(ProcessState::self()); Loading Loading @@ -272,12 +332,17 @@ main(int argc, char** argv) //IPCThreadState::self()->joinThreadPool(); while (true) { int amt = splice(fds[0], NULL, STDOUT_FILENO, NULL, 4096, 0); fprintf(stderr, "spliced %d bytes\n", amt); uint8_t buf[4096]; ssize_t amt = TEMP_FAILURE_RETRY(read(fds[0], buf, sizeof(buf))); if (amt < 0) { return errno; break; } else if (amt == 0) { return 0; break; } ssize_t wamt = TEMP_FAILURE_RETRY(write(STDOUT_FILENO, buf, amt)); if (wamt != amt) { return errno; } } } else { Loading cmds/incidentd/Android.bp +27 −3 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ cc_binary { srcs: [ "src/**/*.cpp", "src/**/*.proto", ":incidentd_section_list", ], Loading @@ -43,6 +44,10 @@ cc_binary { local_include_dirs: ["src"], generated_headers: ["gen-platform-proto-constants"], proto: { type: "lite", }, shared_libs: [ "libbase", "libbinder", Loading @@ -56,6 +61,11 @@ cc_binary { "libprotobuf-cpp-lite", ], static_libs: [ "libincidentcompanion", "libplatformprotos", ], init_rc: ["incidentd.rc"], } Loading @@ -72,6 +82,7 @@ cc_test { "-Wall", "-Wno-unused-variable", "-Wunused-parameter", "-g", // Allow implicit fallthrough in IncidentService.cpp:85 until it is fixed. "-Wno-error=implicit-fallthrough", Loading @@ -82,21 +93,26 @@ cc_test { srcs: [ "tests/**/*.cpp", "src/PrivacyBuffer.cpp", "tests/**/*.proto", "src/FdBuffer.cpp", "src/Privacy.cpp", "src/PrivacyFilter.cpp", "src/Reporter.cpp", "src/Section.cpp", "src/Throttler.cpp", "src/WorkDirectory.cpp", "src/incidentd_util.cpp", "src/proto_util.cpp", "src/report_directory.cpp", "src/**/*.proto", ], data: ["testdata/**/*"], static_libs: [ "libgmock", "libplatformprotos", "libincidentcompanion", "libplatformprotos-test", ], shared_libs: [ "libbase", Loading @@ -105,11 +121,19 @@ cc_test { "libdumputils", "libincident", "liblog", "libprotobuf-cpp-lite", "libprotobuf-cpp-full", "libprotoutil", "libservices", "libutils", ], target: { android: { proto: { type: "full", }, }, }, } genrule { Loading cmds/incidentd/src/Broadcaster.cpp 0 → 100644 +440 −0 Original line number Diff line number Diff line /* * 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 <android/os/DropBoxManager.h> #include <binder/IServiceManager.h> namespace android { namespace os { namespace incidentd { using android::os::IIncidentCompanion; using binder::Status; // ============================================================ Broadcaster::ConsentListener::ConsentListener(const sp<Broadcaster>& 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>& workDirectory) :mReportHandler(), mWorkDirectory(workDirectory) { } void Broadcaster::setHandler(const sp<ReportHandler>& handler) { mReportHandler = handler; } void Broadcaster::reset() { unique_lock<mutex> 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<mutex> lock(mLock); map<ReportId,ReportStatus>::const_iterator found = mHistory.find(ReportId(id, pkg, cls)); if (found != mHistory.end()) { if (found->second.listener != nullptr) { sp<IIncidentCompanion> ics = get_incident_companion(); if (ics != nullptr) { ics->cancelAuthorization(found->second.listener); } } mHistory.erase(found); } } void Broadcaster::clearPackageBroadcasts(const string& pkg) { unique_lock<mutex> lock(mLock); map<ReportId,ReportStatus>::iterator it = mHistory.begin(); while (it != mHistory.end()) { if (it->first.pkg == pkg) { if (it->second.listener != nullptr) { sp<IIncidentCompanion> 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<sp<ReportFile>> files; mWorkDirectory->getReports(&files, 0); //lastSent); // Don't send multiple broadcasts to the same receiver. set<ReportId> reportReadyBroadcasts; for (const sp<ReportFile>& 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<mutex> lock(mLock); mLastSent = timestamp; } int64_t Broadcaster::get_last_sent() { unique_lock<mutex> lock(mLock); return mLastSent; } /* void Broadcaster::printReportStatuses() const { ALOGD("mHistory {"); for (map<ReportId,ReportStatus>::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<mutex> lock(mLock); map<ReportId,ReportStatus>::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<ConsentListener>& listener) { unique_lock<mutex> 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<mutex> lock(mLock); map<ReportId,ReportStatus>::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<mutex> 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<IIncidentCompanion> ics = get_incident_companion(); if (ics == nullptr) { return NAME_NOT_FOUND; } sp<ConsentListener> 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<ReportFile> 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<ReportFile> 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<IIncidentCompanion> 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<ReportFile>& file, const IncidentReportArgs& args) { status_t err; sp<DropBoxManager> 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; } // Start a thread to write the data to dropbox. int readFd = -1; err = file->startFilteringData(&readFd, args); if (err != NO_ERROR) { return err; } // 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<IIncidentCompanion> Broadcaster::get_incident_companion() { sp<IBinder> binder = defaultServiceManager()->getService(String16("incidentcompanion")); if (binder == nullptr) { ALOGI("Can not find IIncidentCompanion service to send broadcast. Will try again later."); return nullptr; } sp<IIncidentCompanion> ics = interface_cast<IIncidentCompanion>(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 Loading
Android.bp +42 −5 Original line number Diff line number Diff line Loading @@ -721,6 +721,7 @@ java_defaults { "frameworks/av/camera/aidl", "frameworks/av/media/libaudioclient/aidl", "frameworks/native/aidl/gui", "frameworks/native/libs/incidentcompanion/binder", "system/core/storaged/binder", "system/vold/binder", "system/gsid/aidl", Loading Loading @@ -968,7 +969,10 @@ java_library { output_params: ["store_unknown_fields=true"], include_dirs: ["external/protobuf/src"], }, exclude_srcs: [ "core/proto/android/privacy.proto", "core/proto/android/section.proto", ], sdk_version: "current", srcs: [ "core/proto/**/*.proto", Loading @@ -988,6 +992,10 @@ java_library { "core/proto/**/*.proto", "libs/incident/proto/android/os/**/*.proto", ], exclude_srcs: [ "core/proto/android/privacy.proto", "core/proto/android/section.proto", ], // Protos have lots of MissingOverride and similar. errorprone: { javacflags: ["-XepDisableAllChecks"], Loading @@ -995,9 +1003,9 @@ java_library { } // ==== c++ proto device library ============================== cc_library { name: "libplatformprotos", host_supported: true, cc_defaults { name: "libplatformprotos-defaults", proto: { export_proto_headers: true, include_dirs: ["external/protobuf/src"], Loading @@ -1011,8 +1019,13 @@ cc_library { srcs: [ "core/proto/**/*.proto", "libs/incident/**/*.proto", ], } cc_library { name: "libplatformprotos", defaults: ["libplatformprotos-defaults"], host_supported: true, target: { host: { Loading @@ -1024,6 +1037,9 @@ cc_library { proto: { type: "lite", }, shared_libs: [ "libprotobuf-cpp-lite", ], shared: { enabled: false, }, Loading @@ -1031,6 +1047,26 @@ cc_library { }, } // This is the full proto version of libplatformprotos. It may only // be used by test code that is not shipped on the device. cc_library { name: "libplatformprotos-test", defaults: ["libplatformprotos-defaults"], host_supported: false, target: { android: { proto: { type: "full", }, shared: { enabled: false, }, }, }, } gensrcs { name: "gen-platform-proto-constants", depfile: true, Loading Loading @@ -1068,6 +1104,7 @@ gensrcs { output_extension: "proto.h", } subdirs = [ "cmds/*", "core/*", Loading
cmds/incident/Android.bp +4 −0 Original line number Diff line number Diff line Loading @@ -29,6 +29,10 @@ cc_binary { "libincident", ], static_libs: [ "libplatformprotos", ], cflags: [ "-Wall", "-Werror", Loading
cmds/incident/main.cpp +80 −15 Original line number Diff line number Diff line Loading @@ -68,6 +68,7 @@ Status StatusListener::onReportSectionStatus(int32_t section, int32_t status) { fprintf(stderr, "section %d status %d\n", section, status); ALOGD("section %d status %d\n", section, status); return Status::ok(); } Loading @@ -75,6 +76,7 @@ Status StatusListener::onReportServiceStatus(const String16& service, int32_t status) { fprintf(stderr, "service '%s' status %d\n", String8(service).string(), status); ALOGD("service '%s' status %d\n", String8(service).string(), status); return Status::ok(); } Loading @@ -82,6 +84,7 @@ Status StatusListener::onReportFinished() { fprintf(stderr, "done\n"); ALOGD("done\n"); exit(0); return Status::ok(); } Loading @@ -90,6 +93,7 @@ Status StatusListener::onReportFailed() { fprintf(stderr, "failed\n"); ALOGD("failed\n"); exit(1); return Status::ok(); } Loading Loading @@ -146,24 +150,49 @@ find_section(const char* name) // ================================================================================ static int get_dest(const char* arg) get_privacy_policy(const char* arg) { if (strcmp(arg, "L") == 0 || strcmp(arg, "LOCAL") == 0) { return DEST_LOCAL; return PRIVACY_POLICY_LOCAL; } if (strcmp(arg, "E") == 0 || strcmp(arg, "EXPLICIT") == 0) { return DEST_EXPLICIT; return PRIVACY_POLICY_EXPLICIT; } if (strcmp(arg, "A") == 0 || strcmp(arg, "AUTO") == 0 || strcmp(arg, "AUTOMATIC") == 0) { return DEST_AUTOMATIC; return PRIVACY_POLICY_AUTOMATIC; } return -1; // return the default value } // ================================================================================ static bool parse_receiver_arg(const string& arg, string* pkg, string* cls) { if (arg.length() == 0) { return true; } size_t slash = arg.find('/'); if (slash == string::npos) { return false; } if (slash == 0 || slash == arg.length() - 1) { return false; } if (arg.find('/', slash+1) != string::npos) { return false; } pkg->assign(arg, 0, slash); cls->assign(arg, slash+1); if ((*cls)[0] == '.') { *cls = (*pkg) + (*cls); } return true; } // ================================================================================ static void usage(FILE* out) Loading @@ -173,10 +202,13 @@ usage(FILE* out) fprintf(out, "Takes an incident report.\n"); fprintf(out, "\n"); fprintf(out, "OPTIONS\n"); fprintf(out, " -l list available sections\n"); fprintf(out, " -p privacy spec, LOCAL, EXPLICIT or AUTOMATIC. Default AUTOMATIC.\n"); fprintf(out, "\n"); fprintf(out, "and one of these destinations:\n"); fprintf(out, " -b (default) print the report to stdout (in proto format)\n"); fprintf(out, " -d send the report into dropbox\n"); fprintf(out, " -l list available sections\n"); fprintf(out, " -p privacy spec, LOCAL, EXPLICIT or AUTOMATIC\n"); fprintf(out, " -s PKG/CLS send broadcast to the broadcast receiver.\n"); fprintf(out, "\n"); fprintf(out, " SECTION the field numbers of the incident report fields to include\n"); fprintf(out, "\n"); Loading @@ -187,12 +219,13 @@ main(int argc, char** argv) { Status status; IncidentReportArgs args; enum { DEST_DROPBOX, DEST_STDOUT } destination = DEST_STDOUT; int dest = -1; // default enum { DEST_UNSET, DEST_DROPBOX, DEST_STDOUT, DEST_BROADCAST } destination = DEST_UNSET; int privacyPolicy = PRIVACY_POLICY_AUTOMATIC; string receiverArg; // Parse the args int opt; while ((opt = getopt(argc, argv, "bhdlp:")) != -1) { while ((opt = getopt(argc, argv, "bhdlp:s:")) != -1) { switch (opt) { case 'h': usage(stdout); Loading @@ -201,13 +234,29 @@ main(int argc, char** argv) section_list(stdout); return 0; case 'b': if (!(destination == DEST_UNSET || destination == DEST_STDOUT)) { usage(stderr); return 1; } destination = DEST_STDOUT; break; case 'd': if (!(destination == DEST_UNSET || destination == DEST_DROPBOX)) { usage(stderr); return 1; } destination = DEST_DROPBOX; break; case 'p': dest = get_dest(optarg); privacyPolicy = get_privacy_policy(optarg); break; case 's': if (destination != DEST_UNSET) { usage(stderr); return 1; } destination = DEST_BROADCAST; receiverArg = optarg; break; default: usage(stderr); Loading @@ -215,6 +264,17 @@ main(int argc, char** argv) } } string pkg; string cls; if (parse_receiver_arg(receiverArg, &pkg, &cls)) { args.setReceiverPkg(pkg); args.setReceiverCls(cls); } else { fprintf(stderr, "badly formatted -s package/class option: %s\n\n", receiverArg.c_str()); usage(stderr); return 1; } if (optind == argc) { args.setAll(true); } else { Loading @@ -236,7 +296,7 @@ main(int argc, char** argv) } } } args.setDest(dest); args.setPrivacyPolicy(privacyPolicy); // Start the thread pool. sp<ProcessState> ps(ProcessState::self()); Loading Loading @@ -272,12 +332,17 @@ main(int argc, char** argv) //IPCThreadState::self()->joinThreadPool(); while (true) { int amt = splice(fds[0], NULL, STDOUT_FILENO, NULL, 4096, 0); fprintf(stderr, "spliced %d bytes\n", amt); uint8_t buf[4096]; ssize_t amt = TEMP_FAILURE_RETRY(read(fds[0], buf, sizeof(buf))); if (amt < 0) { return errno; break; } else if (amt == 0) { return 0; break; } ssize_t wamt = TEMP_FAILURE_RETRY(write(STDOUT_FILENO, buf, amt)); if (wamt != amt) { return errno; } } } else { Loading
cmds/incidentd/Android.bp +27 −3 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ cc_binary { srcs: [ "src/**/*.cpp", "src/**/*.proto", ":incidentd_section_list", ], Loading @@ -43,6 +44,10 @@ cc_binary { local_include_dirs: ["src"], generated_headers: ["gen-platform-proto-constants"], proto: { type: "lite", }, shared_libs: [ "libbase", "libbinder", Loading @@ -56,6 +61,11 @@ cc_binary { "libprotobuf-cpp-lite", ], static_libs: [ "libincidentcompanion", "libplatformprotos", ], init_rc: ["incidentd.rc"], } Loading @@ -72,6 +82,7 @@ cc_test { "-Wall", "-Wno-unused-variable", "-Wunused-parameter", "-g", // Allow implicit fallthrough in IncidentService.cpp:85 until it is fixed. "-Wno-error=implicit-fallthrough", Loading @@ -82,21 +93,26 @@ cc_test { srcs: [ "tests/**/*.cpp", "src/PrivacyBuffer.cpp", "tests/**/*.proto", "src/FdBuffer.cpp", "src/Privacy.cpp", "src/PrivacyFilter.cpp", "src/Reporter.cpp", "src/Section.cpp", "src/Throttler.cpp", "src/WorkDirectory.cpp", "src/incidentd_util.cpp", "src/proto_util.cpp", "src/report_directory.cpp", "src/**/*.proto", ], data: ["testdata/**/*"], static_libs: [ "libgmock", "libplatformprotos", "libincidentcompanion", "libplatformprotos-test", ], shared_libs: [ "libbase", Loading @@ -105,11 +121,19 @@ cc_test { "libdumputils", "libincident", "liblog", "libprotobuf-cpp-lite", "libprotobuf-cpp-full", "libprotoutil", "libservices", "libutils", ], target: { android: { proto: { type: "full", }, }, }, } genrule { Loading
cmds/incidentd/src/Broadcaster.cpp 0 → 100644 +440 −0 Original line number Diff line number Diff line /* * 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 <android/os/DropBoxManager.h> #include <binder/IServiceManager.h> namespace android { namespace os { namespace incidentd { using android::os::IIncidentCompanion; using binder::Status; // ============================================================ Broadcaster::ConsentListener::ConsentListener(const sp<Broadcaster>& 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>& workDirectory) :mReportHandler(), mWorkDirectory(workDirectory) { } void Broadcaster::setHandler(const sp<ReportHandler>& handler) { mReportHandler = handler; } void Broadcaster::reset() { unique_lock<mutex> 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<mutex> lock(mLock); map<ReportId,ReportStatus>::const_iterator found = mHistory.find(ReportId(id, pkg, cls)); if (found != mHistory.end()) { if (found->second.listener != nullptr) { sp<IIncidentCompanion> ics = get_incident_companion(); if (ics != nullptr) { ics->cancelAuthorization(found->second.listener); } } mHistory.erase(found); } } void Broadcaster::clearPackageBroadcasts(const string& pkg) { unique_lock<mutex> lock(mLock); map<ReportId,ReportStatus>::iterator it = mHistory.begin(); while (it != mHistory.end()) { if (it->first.pkg == pkg) { if (it->second.listener != nullptr) { sp<IIncidentCompanion> 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<sp<ReportFile>> files; mWorkDirectory->getReports(&files, 0); //lastSent); // Don't send multiple broadcasts to the same receiver. set<ReportId> reportReadyBroadcasts; for (const sp<ReportFile>& 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<mutex> lock(mLock); mLastSent = timestamp; } int64_t Broadcaster::get_last_sent() { unique_lock<mutex> lock(mLock); return mLastSent; } /* void Broadcaster::printReportStatuses() const { ALOGD("mHistory {"); for (map<ReportId,ReportStatus>::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<mutex> lock(mLock); map<ReportId,ReportStatus>::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<ConsentListener>& listener) { unique_lock<mutex> 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<mutex> lock(mLock); map<ReportId,ReportStatus>::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<mutex> 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<IIncidentCompanion> ics = get_incident_companion(); if (ics == nullptr) { return NAME_NOT_FOUND; } sp<ConsentListener> 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<ReportFile> 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<ReportFile> 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<IIncidentCompanion> 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<ReportFile>& file, const IncidentReportArgs& args) { status_t err; sp<DropBoxManager> 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; } // Start a thread to write the data to dropbox. int readFd = -1; err = file->startFilteringData(&readFd, args); if (err != NO_ERROR) { return err; } // 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<IIncidentCompanion> Broadcaster::get_incident_companion() { sp<IBinder> binder = defaultServiceManager()->getService(String16("incidentcompanion")); if (binder == nullptr) { ALOGI("Can not find IIncidentCompanion service to send broadcast. Will try again later."); return nullptr; } sp<IIncidentCompanion> ics = interface_cast<IIncidentCompanion>(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