Home | History | Annotate | Download | only in src
      1 /*
      2  * Copyright (C) 2019 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 #include "Log.h"
     18 
     19 #include "WorkDirectory.h"
     20 
     21 #include "proto_util.h"
     22 #include "PrivacyFilter.h"
     23 
     24 #include <google/protobuf/io/zero_copy_stream_impl.h>
     25 #include <private/android_filesystem_config.h>
     26 
     27 #include <iomanip>
     28 #include <map>
     29 #include <sstream>
     30 #include <thread>
     31 #include <vector>
     32 
     33 #include <sys/stat.h>
     34 #include <time.h>
     35 #include <unistd.h>
     36 #include <inttypes.h>
     37 
     38 namespace android {
     39 namespace os {
     40 namespace incidentd {
     41 
     42 using std::thread;
     43 using google::protobuf::MessageLite;
     44 using google::protobuf::RepeatedPtrField;
     45 using google::protobuf::io::FileInputStream;
     46 using google::protobuf::io::FileOutputStream;
     47 
     48 /**
     49  * Turn off to skip removing files for debugging.
     50  */
     51 static const bool DO_UNLINK = true;
     52 
     53 /**
     54  * File extension for envelope files.
     55  */
     56 static const string EXTENSION_ENVELOPE(".envelope");
     57 
     58 /**
     59  * File extension for data files.
     60  */
     61 static const string EXTENSION_DATA(".data");
     62 
     63 /**
     64  * Send these reports to dropbox.
     65  */
     66 const ComponentName DROPBOX_SENTINEL("android", "DROPBOX");
     67 
     68 /** metadata field id in IncidentProto */
     69 const int FIELD_ID_INCIDENT_METADATA = 2;
     70 
     71 /**
     72  * Read a protobuf from disk into the message.
     73  */
     74 static status_t read_proto(MessageLite* msg, const string& filename) {
     75     int fd = open(filename.c_str(), O_RDONLY | O_CLOEXEC);
     76     if (fd < 0) {
     77         return -errno;
     78     }
     79 
     80     FileInputStream stream(fd);
     81     stream.SetCloseOnDelete(fd);
     82 
     83     if (!msg->ParseFromZeroCopyStream(&stream)) {
     84         return BAD_VALUE;
     85     }
     86 
     87     return stream.GetErrno();
     88 }
     89 
     90 /**
     91  * Write a protobuf to disk.
     92  */
     93 static status_t write_proto(const MessageLite& msg, const string& filename) {
     94     int fd = open(filename.c_str(), O_CREAT | O_TRUNC | O_RDWR | O_CLOEXEC, 0660);
     95     if (fd < 0) {
     96         return -errno;
     97     }
     98 
     99     FileOutputStream stream(fd);
    100     stream.SetCloseOnDelete(fd);
    101 
    102     if (!msg.SerializeToZeroCopyStream(&stream)) {
    103         ALOGW("write_proto: error writing to %s", filename.c_str());
    104         return BAD_VALUE;
    105     }
    106 
    107     return stream.GetErrno();
    108 }
    109 
    110 static string strip_extension(const string& filename) {
    111     return filename.substr(0, filename.find('.'));
    112 }
    113 
    114 static bool ends_with(const string& str, const string& ending) {
    115     if (str.length() >= ending.length()) {
    116         return str.compare(str.length()-ending.length(), ending.length(), ending) == 0;
    117     } else {
    118         return false;
    119     }
    120 }
    121 
    122 // Returns true if it was a valid timestamp.
    123 static bool parse_timestamp_ns(const string& id, int64_t* result) {
    124     char* endptr;
    125     *result = strtoll(id.c_str(), &endptr, 10);
    126     return id.length() != 0 && *endptr == '\0';
    127 }
    128 
    129 static bool has_section(const ReportFileProto_Report& report, int section) {
    130     const size_t sectionCount = report.section_size();
    131     for (int i = 0; i < sectionCount; i++) {
    132         if (report.section(i) == section) {
    133             return true;
    134         }
    135     }
    136     return false;
    137 }
    138 
    139 status_t create_directory(const char* directory) {
    140     struct stat st;
    141     status_t err = NO_ERROR;
    142     char* dir = strdup(directory);
    143 
    144     // Skip first slash
    145     char* d = dir + 1;
    146 
    147     // Create directories, assigning them to the system user
    148     bool last = false;
    149     while (!last) {
    150         d = strchr(d, '/');
    151         if (d != NULL) {
    152             *d = '\0';
    153         } else {
    154             last = true;
    155         }
    156         if (stat(dir, &st) == 0) {
    157             if (!S_ISDIR(st.st_mode)) {
    158                 err = ALREADY_EXISTS;
    159                 goto done;
    160             }
    161         } else {
    162             ALOGE("No such directory %s, something wrong.", dir);
    163             err = -1;
    164             goto done;
    165         }
    166         if (!last) {
    167             *d++ = '/';
    168         }
    169     }
    170 
    171     // Ensure that the final directory is owned by the system with 0770. If it isn't
    172     // we won't write into it.
    173     if (stat(directory, &st) != 0) {
    174         ALOGE("No incident reports today. Can't stat: %s", directory);
    175         err = -errno;
    176         goto done;
    177     }
    178     if ((st.st_mode & 0777) != 0770) {
    179         ALOGE("No incident reports today. Mode is %0o on report directory %s", st.st_mode,
    180               directory);
    181         err = BAD_VALUE;
    182         goto done;
    183     }
    184     if (st.st_uid != AID_INCIDENTD || st.st_gid != AID_INCIDENTD) {
    185         ALOGE("No incident reports today. Owner is %d and group is %d on report directory %s",
    186               st.st_uid, st.st_gid, directory);
    187         err = BAD_VALUE;
    188         goto done;
    189     }
    190 
    191 done:
    192     free(dir);
    193     return err;
    194 }
    195 
    196 void log_envelope(const ReportFileProto& envelope) {
    197     ALOGD("Envelope: {");
    198     for (int i=0; i<envelope.report_size(); i++) {
    199         ALOGD("  report {");
    200         ALOGD("    pkg=%s", envelope.report(i).pkg().c_str());
    201         ALOGD("    cls=%s", envelope.report(i).cls().c_str());
    202         ALOGD("    share_approved=%d", envelope.report(i).share_approved());
    203         ALOGD("    privacy_policy=%d", envelope.report(i).privacy_policy());
    204         ALOGD("    all_sections=%d", envelope.report(i).all_sections());
    205         for (int j=0; j<envelope.report(i).section_size(); j++) {
    206             ALOGD("    section[%d]=%d", j, envelope.report(i).section(j));
    207         }
    208         ALOGD("  }");
    209     }
    210     ALOGD("  data_file=%s", envelope.data_file().c_str());
    211     ALOGD("  privacy_policy=%d", envelope.privacy_policy());
    212     ALOGD("  data_file_size=%lld", envelope.data_file_size());
    213     ALOGD("  completed=%d", envelope.completed());
    214     ALOGD("}");
    215 }
    216 
    217 // ================================================================================
    218 struct WorkDirectoryEntry {
    219     WorkDirectoryEntry();
    220     explicit WorkDirectoryEntry(const WorkDirectoryEntry& that);
    221     ~WorkDirectoryEntry();
    222 
    223     string envelope;
    224     string data;
    225     int64_t timestampNs;
    226     off_t size;
    227 };
    228 
    229 WorkDirectoryEntry::WorkDirectoryEntry()
    230         :envelope(),
    231          data(),
    232          size(0) {
    233 }
    234 
    235 WorkDirectoryEntry::WorkDirectoryEntry(const WorkDirectoryEntry& that)
    236         :envelope(that.envelope),
    237          data(that.data),
    238          size(that.size) {
    239 }
    240 
    241 WorkDirectoryEntry::~WorkDirectoryEntry() {
    242 }
    243 
    244 // ================================================================================
    245 ReportFile::ReportFile(const sp<WorkDirectory>& workDirectory, int64_t timestampNs,
    246             const string& envelopeFileName, const string& dataFileName)
    247         :mWorkDirectory(workDirectory),
    248          mTimestampNs(timestampNs),
    249          mEnvelopeFileName(envelopeFileName),
    250          mDataFileName(dataFileName),
    251          mEnvelope(),
    252          mDataFd(-1),
    253          mError(NO_ERROR) {
    254     // might get overwritten when we read but that's ok
    255     mEnvelope.set_data_file(mDataFileName);
    256 }
    257 
    258 ReportFile::~ReportFile() {
    259     if (mDataFd >= 0) {
    260         close(mDataFd);
    261     }
    262 }
    263 
    264 int64_t ReportFile::getTimestampNs() const {
    265     return mTimestampNs;
    266 }
    267 
    268 void ReportFile::addReport(const IncidentReportArgs& args) {
    269     // There is only one report per component.  Merge into an existing one if necessary.
    270     ReportFileProto_Report* report;
    271     const int reportCount = mEnvelope.report_size();
    272     int i = 0;
    273     for (; i < reportCount; i++) {
    274         report = mEnvelope.mutable_report(i);
    275         if (report->pkg() == args.receiverPkg() && report->cls() == args.receiverCls()) {
    276             if (args.getPrivacyPolicy() < report->privacy_policy()) {
    277                 // Lower privacy policy (less restrictive) wins.
    278                 report->set_privacy_policy(args.getPrivacyPolicy());
    279             }
    280             report->set_all_sections(report->all_sections() | args.all());
    281             for (int section: args.sections()) {
    282                 if (!has_section(*report, section)) {
    283                     report->add_section(section);
    284                 }
    285             }
    286             break;
    287         }
    288     }
    289     if (i >= reportCount) {
    290         report = mEnvelope.add_report();
    291         report->set_pkg(args.receiverPkg());
    292         report->set_cls(args.receiverCls());
    293         report->set_privacy_policy(args.getPrivacyPolicy());
    294         report->set_all_sections(args.all());
    295         for (int section: args.sections()) {
    296             report->add_section(section);
    297         }
    298     }
    299 
    300     for (const vector<uint8_t>& header: args.headers()) {
    301         report->add_header(header.data(), header.size());
    302     }
    303 }
    304 
    305 void ReportFile::removeReport(const string& pkg, const string& cls) {
    306     RepeatedPtrField<ReportFileProto_Report>* reports = mEnvelope.mutable_report();
    307     const int reportCount = reports->size();
    308     for (int i = 0; i < reportCount; i++) {
    309         const ReportFileProto_Report& r = reports->Get(i);
    310         if (r.pkg() == pkg && r.cls() == cls) {
    311             reports->DeleteSubrange(i, 1);
    312             return;
    313         }
    314     }
    315 }
    316 
    317 void ReportFile::removeReports(const string& pkg) {
    318     RepeatedPtrField<ReportFileProto_Report>* reports = mEnvelope.mutable_report();
    319     const int reportCount = reports->size();
    320     for (int i = reportCount-1; i >= 0; i--) {
    321         const ReportFileProto_Report& r = reports->Get(i);
    322         if (r.pkg() == pkg) {
    323             reports->DeleteSubrange(i, 1);
    324         }
    325     }
    326 }
    327 
    328 void ReportFile::setMetadata(const IncidentMetadata& metadata) {
    329     *mEnvelope.mutable_metadata() = metadata;
    330 }
    331 
    332 void ReportFile::markCompleted() {
    333     mEnvelope.set_completed(true);
    334 }
    335 
    336 status_t ReportFile::markApproved(const string& pkg, const string& cls) {
    337     size_t const reportCount = mEnvelope.report_size();
    338     for (int reportIndex = 0; reportIndex < reportCount; reportIndex++) {
    339         ReportFileProto_Report* report = mEnvelope.mutable_report(reportIndex);
    340         if (report->pkg() == pkg && report->cls() == cls) {
    341             report->set_share_approved(true);
    342             return NO_ERROR;
    343         }
    344     }
    345     return NAME_NOT_FOUND;
    346 }
    347 
    348 void ReportFile::setMaxPersistedPrivacyPolicy(int persistedPrivacyPolicy) {
    349     mEnvelope.set_privacy_policy(persistedPrivacyPolicy);
    350 }
    351 
    352 status_t ReportFile::saveEnvelope() {
    353     return save_envelope_impl(true);
    354 }
    355 
    356 status_t ReportFile::trySaveEnvelope() {
    357     return save_envelope_impl(false);
    358 }
    359 
    360 status_t ReportFile::loadEnvelope() {
    361     return load_envelope_impl(true);
    362 }
    363 
    364 status_t ReportFile::tryLoadEnvelope() {
    365     return load_envelope_impl(false);
    366 }
    367 
    368 const ReportFileProto& ReportFile::getEnvelope() {
    369     return mEnvelope;
    370 }
    371 
    372 status_t ReportFile::startWritingDataFile() {
    373     if (mDataFd >= 0) {
    374         ALOGW("ReportFile::startWritingDataFile called with the file already open: %s",
    375                 mDataFileName.c_str());
    376         return ALREADY_EXISTS;
    377     }
    378     mDataFd = open(mDataFileName.c_str(), O_CREAT | O_TRUNC | O_RDWR | O_CLOEXEC, 0660);
    379     if (mDataFd < 0) {
    380         return -errno;
    381     }
    382     return NO_ERROR;
    383 }
    384 
    385 void ReportFile::closeDataFile() {
    386     if (mDataFd >= 0) {
    387         mEnvelope.set_data_file_size(lseek(mDataFd, 0, SEEK_END));
    388         close(mDataFd);
    389         mDataFd = -1;
    390     }
    391 }
    392 
    393 status_t ReportFile::startFilteringData(int writeFd, const IncidentReportArgs& args) {
    394     // Open data file.
    395     int dataFd = open(mDataFileName.c_str(), O_RDONLY | O_CLOEXEC);
    396     if (dataFd < 0) {
    397         ALOGW("Error opening incident report '%s' %s", getDataFileName().c_str(), strerror(-errno));
    398         close(writeFd);
    399         return -errno;
    400     }
    401 
    402     // Check that the size on disk is what we thought we wrote.
    403     struct stat st;
    404     if (fstat(dataFd, &st) != 0) {
    405         ALOGW("Error running fstat incident report '%s' %s", getDataFileName().c_str(),
    406               strerror(-errno));
    407         close(writeFd);
    408         return -errno;
    409     }
    410     if (st.st_size != mEnvelope.data_file_size()) {
    411         ALOGW("File size mismatch. Envelope says %" PRIi64 " bytes but data file is %" PRIi64
    412               " bytes: %s",
    413               (int64_t)mEnvelope.data_file_size(), st.st_size, mDataFileName.c_str());
    414         ALOGW("Removing incident report");
    415         mWorkDirectory->remove(this);
    416         close(writeFd);
    417         return BAD_VALUE;
    418     }
    419 
    420     status_t err;
    421 
    422     for (const auto& report : mEnvelope.report()) {
    423         for (const auto& header : report.header()) {
    424            write_header_section(writeFd,
    425                reinterpret_cast<const uint8_t*>(header.c_str()), header.size());
    426         }
    427     }
    428 
    429     if (mEnvelope.has_metadata()) {
    430         write_section(writeFd, FIELD_ID_INCIDENT_METADATA, mEnvelope.metadata());
    431     }
    432 
    433     err = filter_and_write_report(writeFd, dataFd, mEnvelope.privacy_policy(), args);
    434     if (err != NO_ERROR) {
    435         ALOGW("Error writing incident report '%s' to dropbox: %s", getDataFileName().c_str(),
    436                 strerror(-err));
    437     }
    438 
    439     close(writeFd);
    440     return NO_ERROR;
    441 }
    442 
    443 string ReportFile::getDataFileName() const {
    444     return mDataFileName;
    445 }
    446 
    447 string ReportFile::getEnvelopeFileName() const {
    448     return mEnvelopeFileName;
    449 }
    450 
    451 int ReportFile::getDataFileFd() {
    452     return mDataFd;
    453 }
    454 
    455 void ReportFile::setWriteError(status_t err) {
    456     mError = err;
    457 }
    458 
    459 status_t ReportFile::getWriteError() {
    460     return mError;
    461 }
    462 
    463 string ReportFile::getId() {
    464     return to_string(mTimestampNs);
    465 }
    466 
    467 status_t ReportFile::save_envelope_impl(bool cleanup) {
    468     status_t err;
    469     err = write_proto(mEnvelope, mEnvelopeFileName);
    470     if (err != NO_ERROR) {
    471         // If there was an error writing the envelope, then delete the whole thing.
    472         if (cleanup) {
    473             mWorkDirectory->remove(this);
    474         }
    475         return err;
    476     }
    477     return NO_ERROR;
    478 }
    479 
    480 status_t ReportFile::load_envelope_impl(bool cleanup) {
    481     status_t err;
    482     err = read_proto(&mEnvelope, mEnvelopeFileName);
    483     if (err != NO_ERROR) {
    484         // If there was an error reading the envelope, then delete the whole thing.
    485         if (cleanup) {
    486             mWorkDirectory->remove(this);
    487         }
    488         return err;
    489     }
    490     return NO_ERROR;
    491 }
    492 
    493 
    494 
    495 // ================================================================================
    496 //
    497 
    498 WorkDirectory::WorkDirectory()
    499         :mDirectory("/data/misc/incidents"),
    500          mMaxFileCount(100),
    501          mMaxDiskUsageBytes(100 * 1024 * 1024) {  // Incident reports can take up to 100MB on disk.
    502                                                  // TODO: Should be a flag.
    503     create_directory(mDirectory.c_str());
    504 }
    505 
    506 WorkDirectory::WorkDirectory(const string& dir, int maxFileCount, long maxDiskUsageBytes)
    507         :mDirectory(dir),
    508          mMaxFileCount(maxFileCount),
    509          mMaxDiskUsageBytes(maxDiskUsageBytes) {
    510     create_directory(mDirectory.c_str());
    511 }
    512 
    513 sp<ReportFile> WorkDirectory::createReportFile() {
    514     unique_lock<mutex> lock(mLock);
    515     status_t err;
    516 
    517     clean_directory_locked();
    518 
    519     int64_t timestampNs = make_timestamp_ns_locked();
    520     string envelopeFileName = make_filename(timestampNs, EXTENSION_ENVELOPE);
    521     string dataFileName = make_filename(timestampNs, EXTENSION_DATA);
    522 
    523     sp<ReportFile> result = new ReportFile(this, timestampNs, envelopeFileName, dataFileName);
    524 
    525     err = result->trySaveEnvelope();
    526     if (err != NO_ERROR) {
    527         ALOGW("Can't save envelope file %s: %s", strerror(-errno), envelopeFileName.c_str());
    528         return nullptr;
    529     }
    530 
    531     return result;
    532 }
    533 
    534 status_t WorkDirectory::getReports(vector<sp<ReportFile>>* result, int64_t after) {
    535     unique_lock<mutex> lock(mLock);
    536 
    537     const bool DBG = true;
    538 
    539     if (DBG) {
    540         ALOGD("WorkDirectory::getReports");
    541     }
    542 
    543     map<string,WorkDirectoryEntry> files;
    544     get_directory_contents_locked(&files, after);
    545     for (map<string,WorkDirectoryEntry>::iterator it = files.begin();
    546             it != files.end(); it++) {
    547         sp<ReportFile> reportFile = new ReportFile(this, it->second.timestampNs,
    548                 it->second.envelope, it->second.data);
    549         if (DBG) {
    550             ALOGD("  %s", reportFile->getId().c_str());
    551         }
    552         result->push_back(reportFile);
    553     }
    554     return NO_ERROR;
    555 }
    556 
    557 sp<ReportFile> WorkDirectory::getReport(const string& pkg, const string& cls, const string& id,
    558             IncidentReportArgs* args) {
    559     unique_lock<mutex> lock(mLock);
    560 
    561     status_t err;
    562     int64_t timestampNs;
    563     if (!parse_timestamp_ns(id, &timestampNs)) {
    564         return nullptr;
    565     }
    566 
    567     // Make the ReportFile object, and then see if it's valid and for pkg and cls.
    568     sp<ReportFile> result = new ReportFile(this, timestampNs,
    569             make_filename(timestampNs, EXTENSION_ENVELOPE),
    570             make_filename(timestampNs, EXTENSION_DATA));
    571 
    572     err = result->tryLoadEnvelope();
    573     if (err != NO_ERROR) {
    574         ALOGW("Can't open envelope file for report %s/%s %s", pkg.c_str(), cls.c_str(), id.c_str());
    575         return nullptr;
    576     }
    577 
    578     const ReportFileProto& envelope = result->getEnvelope();
    579     const size_t reportCount = envelope.report_size();
    580     for (int i = 0; i < reportCount; i++) {
    581         const ReportFileProto_Report& report = envelope.report(i);
    582         if (report.pkg() == pkg && report.cls() == cls) {
    583             if (args != nullptr) {
    584                 get_args_from_report(args, report);
    585             }
    586             return result;
    587         }
    588 
    589     }
    590 
    591     return nullptr;
    592 }
    593 
    594 bool WorkDirectory::hasMore(int64_t after) {
    595     unique_lock<mutex> lock(mLock);
    596 
    597     map<string,WorkDirectoryEntry> files;
    598     get_directory_contents_locked(&files, after);
    599     return files.size() > 0;
    600 }
    601 
    602 void WorkDirectory::commit(const sp<ReportFile>& report, const string& pkg, const string& cls) {
    603     status_t err;
    604     ALOGI("Committing report %s for %s/%s", report->getId().c_str(), pkg.c_str(), cls.c_str());
    605 
    606     unique_lock<mutex> lock(mLock);
    607 
    608     // Load the envelope here inside the lock.
    609     err = report->loadEnvelope();
    610 
    611     report->removeReport(pkg, cls);
    612 
    613     delete_files_for_report_if_necessary(report);
    614 }
    615 
    616 void WorkDirectory::commitAll(const string& pkg) {
    617     status_t err;
    618     ALOGI("All reports for %s", pkg.c_str());
    619 
    620     unique_lock<mutex> lock(mLock);
    621 
    622     map<string,WorkDirectoryEntry> files;
    623     get_directory_contents_locked(&files, 0);
    624 
    625     for (map<string,WorkDirectoryEntry>::iterator it = files.begin();
    626             it != files.end(); it++) {
    627         sp<ReportFile> reportFile = new ReportFile(this, it->second.timestampNs,
    628                 it->second.envelope, it->second.data);
    629 
    630         err = reportFile->loadEnvelope();
    631         if (err != NO_ERROR) {
    632             continue;
    633         }
    634 
    635         reportFile->removeReports(pkg);
    636 
    637         delete_files_for_report_if_necessary(reportFile);
    638     }
    639 }
    640 
    641 void WorkDirectory::remove(const sp<ReportFile>& report) {
    642     unique_lock<mutex> lock(mLock);
    643     // Set this to false to leave files around for debugging.
    644     if (DO_UNLINK) {
    645         unlink(report->getDataFileName().c_str());
    646         unlink(report->getEnvelopeFileName().c_str());
    647     }
    648 }
    649 
    650 int64_t WorkDirectory::make_timestamp_ns_locked() {
    651     // Guarantee that we don't have duplicate timestamps.
    652     // This is a little bit lame, but since reports are created on the
    653     // same thread and are kinda slow we'll seldomly actually hit the
    654     // condition.  The bigger risk is the clock getting reset and causing
    655     // a collision.  In that case, we'll just make incident reporting a
    656     // little bit slower.  Nobody will notice if we just loop until we
    657     // have a unique file name.
    658     int64_t timestampNs = 0;
    659     do {
    660         struct timespec spec;
    661         if (timestampNs > 0) {
    662             spec.tv_sec = 0;
    663             spec.tv_nsec = 1;
    664             nanosleep(&spec, nullptr);
    665         }
    666         clock_gettime(CLOCK_REALTIME, &spec);
    667         timestampNs = (spec.tv_sec) * 1000 + spec.tv_nsec;
    668     } while (file_exists_locked(timestampNs));
    669     return timestampNs;
    670 }
    671 
    672 /**
    673  * It is required to hold the lock here so in case someone else adds it
    674  * our result is still correct for the caller.
    675  */
    676 bool WorkDirectory::file_exists_locked(int64_t timestampNs) {
    677     const string filename = make_filename(timestampNs, EXTENSION_ENVELOPE);
    678     struct stat st;
    679     return stat(filename.c_str(), &st) == 0;
    680 }
    681 
    682 string WorkDirectory::make_filename(int64_t timestampNs, const string& extension) {
    683     // Zero pad the timestamp so it can also be alpha sorted.
    684     stringstream result;
    685     result << mDirectory << '/' << setfill('0') << setw(20) << timestampNs << extension;
    686     return result.str();
    687 }
    688 
    689 off_t WorkDirectory::get_directory_contents_locked(map<string,WorkDirectoryEntry>* files,
    690         int64_t after) {
    691     DIR* dir;
    692     struct dirent* entry;
    693 
    694     if ((dir = opendir(mDirectory.c_str())) == NULL) {
    695         ALOGE("Couldn't open incident directory: %s", mDirectory.c_str());
    696         return -1;
    697     }
    698 
    699     string dirbase(mDirectory);
    700     if (mDirectory[dirbase.size() - 1] != '/') dirbase += "/";
    701 
    702     off_t totalSize = 0;
    703 
    704     // Enumerate, count and add up size
    705     while ((entry = readdir(dir)) != NULL) {
    706         if (entry->d_name[0] == '.') {
    707             continue;
    708         }
    709         string entryname = entry->d_name;  // local to this dir
    710         string filename = dirbase + entryname;  // fully qualified
    711 
    712         bool isEnvelope = ends_with(entryname, EXTENSION_ENVELOPE);
    713         bool isData = ends_with(entryname, EXTENSION_DATA);
    714 
    715         // If the file isn't one of our files, just ignore it.  Otherwise,
    716         // sum up the sizes.
    717         if (isEnvelope || isData) {
    718             string timestamp = strip_extension(entryname);
    719 
    720             int64_t timestampNs;
    721             if (!parse_timestamp_ns(timestamp, &timestampNs)) {
    722                 continue;
    723             }
    724 
    725             if (after == 0 || timestampNs > after) {
    726                 struct stat st;
    727                 if (stat(filename.c_str(), &st) != 0) {
    728                     ALOGE("Unable to stat file %s", filename.c_str());
    729                     continue;
    730                 }
    731                 if (!S_ISREG(st.st_mode)) {
    732                     continue;
    733                 }
    734 
    735                 WorkDirectoryEntry& entry = (*files)[timestamp];
    736                 if (isEnvelope) {
    737                     entry.envelope = filename;
    738                 } else if (isData) {
    739                     entry.data = filename;
    740                 }
    741                 entry.timestampNs = timestampNs;
    742                 entry.size += st.st_size;
    743                 totalSize += st.st_size;
    744             }
    745         }
    746     }
    747 
    748     closedir(dir);
    749 
    750     // Now check if there are any data files that don't have envelope files.
    751     // If there are, then just go ahead and delete them now.  Don't wait for
    752     // a cleaning.
    753 
    754     if (DO_UNLINK) {
    755         map<string,WorkDirectoryEntry>::iterator it = files->begin();
    756         while (it != files->end()) {
    757             if (it->second.envelope.length() == 0) {
    758                 unlink(it->second.data.c_str());
    759                 it = files->erase(it);
    760             } else {
    761                 it++;
    762             }
    763         }
    764     }
    765 
    766     return totalSize;
    767 }
    768 
    769 void WorkDirectory::clean_directory_locked() {
    770     DIR* dir;
    771     struct dirent* entry;
    772     struct stat st;
    773 
    774     // Map of filename without extension to the entries about it.  Conveniently,
    775     // this also keeps the list sorted by filename, which is a timestamp.
    776     map<string,WorkDirectoryEntry> files;
    777     off_t totalSize = get_directory_contents_locked(&files, 0);
    778     if (totalSize < 0) {
    779         return;
    780     }
    781     int totalCount = files.size();
    782 
    783     // Count or size is less than max, then we're done.
    784     if (totalSize < mMaxDiskUsageBytes && totalCount < mMaxFileCount) {
    785         return;
    786     }
    787 
    788     // Remove files until we're under our limits.
    789     if (DO_UNLINK) {
    790         for (map<string, WorkDirectoryEntry>::const_iterator it = files.begin();
    791                 it != files.end() && (totalSize >= mMaxDiskUsageBytes
    792                     || totalCount >= mMaxFileCount);
    793                 it++) {
    794             unlink(it->second.envelope.c_str());
    795             unlink(it->second.data.c_str());
    796             totalSize -= it->second.size;
    797             totalCount--;
    798         }
    799     }
    800 }
    801 
    802 void WorkDirectory::delete_files_for_report_if_necessary(const sp<ReportFile>& report) {
    803     if (report->getEnvelope().report_size() == 0) {
    804         ALOGI("Report %s is finished. Deleting from storage.", report->getId().c_str());
    805         if (DO_UNLINK) {
    806             unlink(report->getDataFileName().c_str());
    807             unlink(report->getEnvelopeFileName().c_str());
    808         }
    809     }
    810 }
    811 
    812 // ================================================================================
    813 void get_args_from_report(IncidentReportArgs* out, const ReportFileProto_Report& report) {
    814     out->setPrivacyPolicy(report.privacy_policy());
    815     out->setAll(report.all_sections());
    816     out->setReceiverPkg(report.pkg());
    817     out->setReceiverCls(report.cls());
    818 
    819     const int sectionCount = report.section_size();
    820     for (int i = 0; i < sectionCount; i++) {
    821         out->addSection(report.section(i));
    822     }
    823 
    824     const int headerCount = report.header_size();
    825     for (int i = 0; i < headerCount; i++) {
    826         const string& header  = report.header(i);
    827         vector<uint8_t> vec(header.begin(), header.end());
    828         out->addHeader(vec);
    829     }
    830 }
    831 
    832 
    833 }  // namespace incidentd
    834 }  // namespace os
    835 }  // namespace android
    836 
    837