summaryrefslogtreecommitdiff
path: root/cmds/incidentd/src/WorkDirectory.h
blob: 3c6a2f2b96176c03bf1cace71b9c030ebcc0305d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
/*
 * Copyright (C) 2019 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.
 */

#pragma once

#include <android/content/ComponentName.h>
#include <android/os/IncidentReportArgs.h>
#include <frameworks/base/core/proto/android/os/metadata.pb.h>
#include <frameworks/base/cmds/incidentd/src/report_file.pb.h>

#include <utils/RefBase.h>

#include <mutex>
#include <string>

namespace android {
namespace os {
namespace incidentd {

using android::content::ComponentName;
using android::os::IncidentReportArgs;
using namespace std;

extern const ComponentName DROPBOX_SENTINEL;

class WorkDirectory;
struct WorkDirectoryEntry;

void get_args_from_report(IncidentReportArgs* out, const ReportFileProto_Report& report);

/**
 * A ReportFile object is backed by two files.
 *   - A metadata file, which contains a 
 */
class ReportFile : public virtual RefBase {
public:
    ReportFile(const sp<WorkDirectory>& workDirectory, int64_t timestampNs,
            const string& envelopeFileName, const string& dataFileName);

    virtual ~ReportFile();

    /**
     * Get the timestamp from when this file was added.
     */
    int64_t getTimestampNs() const;

    /**
     * Add an additional report to this ReportFile.
     */
    void addReport(const IncidentReportArgs& args);

    /**
     * Remove the reports for pkg/cls from this file.
     */
    void removeReport(const string& pkg, const string& cls);

    /**
     * Remove all reports for pkg from this file.
     */
    void removeReports(const string& pkg);

    /**
     * Set the metadata for this incident report.
     */
    void setMetadata(const IncidentMetadata& metadata);

    /*
     * Mark this incident report as finished and ready for broadcast.
     */
    void markCompleted();

    /*
     * Mark this incident report as finished and ready for broadcast.
     */
    status_t markApproved(const string& pkg, const string& cls);
    
    /**
     * Set the privacy policy that is being used to pre-filter the data
     * going to disk.
     */
    void setMaxPersistedPrivacyPolicy(int persistedPrivacyPolicy);

    /**
     * Save the metadata (envelope) information about the incident
     * report.  Must be called after addReport, setMetadata markCompleted
     * markApproved to save those changes to disk.
     */
    status_t saveEnvelope();

    /**
     * Like saveEnvelope() but will not clean up if there is an error.
     */
    status_t trySaveEnvelope();

    /**
     * Read the envelope information from disk.  If there was an error, the envelope and
     * data file will be removed.  If the proto can't be loaded, the whole file is deleted.
     */
    status_t loadEnvelope();

    /**
     * Like loadEnvelope() but will not clean up if there is an error.
     */
    status_t tryLoadEnvelope();

    /**
     * Get the envelope information.
     */
    const ReportFileProto& getEnvelope();

    /**
     * Open the file that will contain the contents of the incident report.  Call
     * close() or closeDataFile() on the result of getDataFileFd() when you're done.
     * This is not done automatically in the desctructor.   If there is an error, returns
     * it and you will not get an fd.
     */
    status_t startWritingDataFile();

    /**
     * Close the data file.
     */
    void closeDataFile();

    /**
     * Use the privacy and section configuration from the args parameter to filter data, write
     * to [writeFd] and take the ownership of [writeFd].
     *
     * Note: this call is blocking. When the writeFd is a pipe fd for IPC, caller should make sure
     * it's called on a separate thread so that reader can start to read without waiting for writer
     * to finish writing (which may not happen due to pipe buffer overflow).
     */
    status_t startFilteringData(int writeFd, const IncidentReportArgs& args);

    /**
     * Get the name of the data file on disk.
     */
    string getDataFileName() const;

    /**
     * Get the name of the envelope file on disk.
     */
    string getEnvelopeFileName() const;

    /**
     * Return the file descriptor for the data file, or -1 if it is not
     * currently open.
     */
    int getDataFileFd();

    /**
     * Record that there was an error writing to the data file.
     */
    void setWriteError(status_t err);

    /**
     * Get whether there was previously an error writing to the data file.
     */
    status_t getWriteError();

    /**
     * Get the unique identifier for this file.
     */
    string getId();

private:
    sp<WorkDirectory> mWorkDirectory;
    int64_t mTimestampNs;
    string mEnvelopeFileName;
    string mDataFileName;
    ReportFileProto mEnvelope;
    int mDataFd;
    status_t mError;

    status_t save_envelope_impl(bool cleanup);
    status_t load_envelope_impl(bool cleanup);
};

/**
 * For directory cleanup to work, WorkDirectory must be kept
 * alive for the duration of all of the ReportFiles.  In the real
 * incidentd, WorkDirectory is a singleton.  In tests, it may
 * have a shorter duration.
 */
class WorkDirectory : public virtual RefBase {
public:
    /**
     * Save files to the default location.
     */
    WorkDirectory();

    /**
     * Save files to a specific location (primarily for testing).
     */
    WorkDirectory(const string& dir, int maxFileCount, long maxDiskUsageBytes);

    /**
     * Return a new report file.  Creating this object won't fail, but
     * subsequent actions on the file could, if the disk is full, permissions
     * aren't set correctly, etc.
     */
    sp<ReportFile> createReportFile();

    /**
     * Get the reports that are saved on-disk, with the time after (>) than the
     * given timestamp.  Pass 0 to start at the beginning.  These files
     * will be sorted by timestamp.  The envelope will not have been loaded.
     */
    status_t getReports(vector<sp<ReportFile>>* files, int64_t after);

    /**
     * Get the report with the given package, class and id. Returns nullptr if
     * that can't be found.  The envelope will have been loaded.  Returns the
     * original IncidentReportArgs in *args if args != nullptr.
     */
    sp<ReportFile> getReport(const string& pkg, const string& cls, const string& id,
            IncidentReportArgs* args);

    /**
     * Returns whether there are more reports after the given timestamp.
     */
    bool hasMore(int64_t after);

    /**
     * Confirm that a particular broadcast receiver has received the data.  When all
     * broadcast receivers for a particular report file have finished, the envelope
     * and data files will be deleted.
     */
    void commit(const sp<ReportFile>& report, const string& pkg, const string& cls);

    /**
     * Commit all reports the given package.
     */
    void commitAll(const string& pkg);

    /**
     * Remove the envelope and data file from disk, regardless of whether there are
     * more pending readers or broadcasts, for example in response to an error.
     */
    void remove(const sp<ReportFile>& report);
    
private:
    string mDirectory;
    int mMaxFileCount;
    long mMaxDiskUsageBytes;

    // Held while creating or removing envelope files, which are the file that keeps
    // the directory consistent.
    mutex mLock;

    int64_t make_timestamp_ns_locked();
    bool file_exists_locked(int64_t timestampNs);    
    off_t get_directory_contents_locked(map<string,WorkDirectoryEntry>* files, int64_t after);
    void clean_directory_locked();
    void delete_files_for_report_if_necessary(const sp<ReportFile>& report);

    string make_filename(int64_t timestampNs, const string& extension);
};


}  // namespace incidentd
}  // namespace os
}  // namespace android