blob: 83b72d966a39e38e86bc3b1f610f056b208c602e [file] [log] [blame]
yro947fbce2017-11-15 22:50:23 -08001/*
2 * Copyright (C) 2017 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#define DEBUG true // STOPSHIP if true
18#include "Log.h"
19
yro947fbce2017-11-15 22:50:23 -080020#include "android-base/stringprintf.h"
yro98a28502018-01-18 17:00:14 -080021#include "guardrail/StatsdStats.h"
22#include "storage/StorageManager.h"
yro947fbce2017-11-15 22:50:23 -080023
24#include <android-base/file.h>
25#include <dirent.h>
yro98a28502018-01-18 17:00:14 -080026#include <private/android_filesystem_config.h>
27#include <fstream>
28#include <iostream>
yro947fbce2017-11-15 22:50:23 -080029
yro947fbce2017-11-15 22:50:23 -080030namespace android {
31namespace os {
32namespace statsd {
33
Yao Chenf09569f2017-12-13 17:00:51 -080034using android::util::FIELD_COUNT_REPEATED;
35using android::util::FIELD_TYPE_MESSAGE;
36using std::map;
37
yro98a28502018-01-18 17:00:14 -080038#define STATS_DATA_DIR "/data/misc/stats-data"
yro03faf092017-12-12 00:17:50 -080039#define STATS_SERVICE_DIR "/data/misc/stats-service"
yro947fbce2017-11-15 22:50:23 -080040
41// for ConfigMetricsReportList
42const int FIELD_ID_REPORTS = 2;
43
44using android::base::StringPrintf;
45using std::unique_ptr;
46
yro98a28502018-01-18 17:00:14 -080047// Returns array of int64_t which contains timestamp in seconds, uid, and
48// configID.
49static void parseFileName(char* name, int64_t* result) {
50 int index = 0;
51 char* substr = strtok(name, "_");
52 while (substr != nullptr && index < 3) {
53 result[index] = StrToInt64(substr);
54 index++;
55 substr = strtok(nullptr, "_");
56 }
57 // When index ends before hitting 3, file name is corrupted. We
58 // intentionally put -1 at index 0 to indicate the error to caller.
59 // TODO: consider removing files with unexpected name format.
60 if (index < 3) {
61 result[0] = -1;
62 }
63}
64
65static string getFilePath(const char* path, int64_t timestamp, int64_t uid, int64_t configID) {
66 return StringPrintf("%s/%lld-%d-%lld", path, (long long)timestamp, (int)uid,
67 (long long)configID);
68}
69
yro947fbce2017-11-15 22:50:23 -080070void StorageManager::writeFile(const char* file, const void* buffer, int numBytes) {
71 int fd = open(file, O_WRONLY | O_CREAT | O_CLOEXEC, S_IRUSR | S_IWUSR);
Stefan Lafonc7bdc622017-11-27 12:54:21 -080072 if (fd == -1) {
yro947fbce2017-11-15 22:50:23 -080073 VLOG("Attempt to access %s but failed", file);
74 return;
75 }
yro98a28502018-01-18 17:00:14 -080076 trimToFit(STATS_SERVICE_DIR);
77 trimToFit(STATS_DATA_DIR);
yro947fbce2017-11-15 22:50:23 -080078
79 int result = write(fd, buffer, numBytes);
80 if (result == numBytes) {
81 VLOG("Successfully wrote %s", file);
82 } else {
83 VLOG("Failed to write %s", file);
84 }
yro98a28502018-01-18 17:00:14 -080085
86 result = fchown(fd, AID_STATSD, AID_STATSD);
87 if (result) {
88 VLOG("Failed to chown %s to statsd", file);
89 }
90
yro947fbce2017-11-15 22:50:23 -080091 close(fd);
92}
93
94void StorageManager::deleteFile(const char* file) {
95 if (remove(file) != 0) {
96 VLOG("Attempt to delete %s but is not found", file);
97 } else {
98 VLOG("Successfully deleted %s", file);
99 }
100}
101
102void StorageManager::deleteAllFiles(const char* path) {
103 unique_ptr<DIR, decltype(&closedir)> dir(opendir(path), closedir);
104 if (dir == NULL) {
105 VLOG("Directory does not exist: %s", path);
106 return;
107 }
108
109 dirent* de;
110 while ((de = readdir(dir.get()))) {
111 char* name = de->d_name;
112 if (name[0] == '.') continue;
113 deleteFile(StringPrintf("%s/%s", path, name).c_str());
114 }
115}
116
yroe5f82922018-01-22 18:37:27 -0800117void StorageManager::deleteSuffixedFiles(const char* path, const char* suffix) {
yro947fbce2017-11-15 22:50:23 -0800118 unique_ptr<DIR, decltype(&closedir)> dir(opendir(path), closedir);
119 if (dir == NULL) {
120 VLOG("Directory does not exist: %s", path);
121 return;
122 }
123
124 dirent* de;
125 while ((de = readdir(dir.get()))) {
126 char* name = de->d_name;
yroe5f82922018-01-22 18:37:27 -0800127 if (name[0] == '.') {
yro947fbce2017-11-15 22:50:23 -0800128 continue;
129 }
yroe5f82922018-01-22 18:37:27 -0800130 size_t nameLen = strlen(name);
131 size_t suffixLen = strlen(suffix);
132 if (suffixLen <= nameLen && strncmp(name + nameLen - suffixLen, suffix, suffixLen) == 0) {
133 deleteFile(StringPrintf("%s/%s", path, name).c_str());
134 }
yro947fbce2017-11-15 22:50:23 -0800135 }
136}
137
138void StorageManager::sendBroadcast(const char* path,
139 const std::function<void(const ConfigKey&)>& sendBroadcast) {
140 unique_ptr<DIR, decltype(&closedir)> dir(opendir(path), closedir);
141 if (dir == NULL) {
142 VLOG("no stats-data directory on disk");
143 return;
144 }
145
146 dirent* de;
147 while ((de = readdir(dir.get()))) {
148 char* name = de->d_name;
149 if (name[0] == '.') continue;
150 VLOG("file %s", name);
151
yro98a28502018-01-18 17:00:14 -0800152 int64_t result[3];
153 parseFileName(name, result);
154 if (result[0] == -1) continue;
155 int64_t uid = result[1];
156 int64_t configID = result[2];
yro947fbce2017-11-15 22:50:23 -0800157
yro98a28502018-01-18 17:00:14 -0800158 sendBroadcast(ConfigKey((int)uid, configID));
yro947fbce2017-11-15 22:50:23 -0800159 }
160}
161
yro98a28502018-01-18 17:00:14 -0800162void StorageManager::appendConfigMetricsReport(ProtoOutputStream& proto) {
163 unique_ptr<DIR, decltype(&closedir)> dir(opendir(STATS_DATA_DIR), closedir);
Yao Chen72506222017-11-25 15:33:09 -0800164 if (dir == NULL) {
yro98a28502018-01-18 17:00:14 -0800165 VLOG("Path %s does not exist", STATS_DATA_DIR);
yro947fbce2017-11-15 22:50:23 -0800166 return;
167 }
168
169 dirent* de;
170 while ((de = readdir(dir.get()))) {
171 char* name = de->d_name;
172 if (name[0] == '.') continue;
173 VLOG("file %s", name);
174
yro98a28502018-01-18 17:00:14 -0800175 int64_t result[3];
176 parseFileName(name, result);
177 if (result[0] == -1) continue;
178 int64_t timestamp = result[0];
179 int64_t uid = result[1];
180 int64_t configID = result[2];
181 string file_name = getFilePath(STATS_DATA_DIR, timestamp, uid, configID);
yro947fbce2017-11-15 22:50:23 -0800182 int fd = open(file_name.c_str(), O_RDONLY | O_CLOEXEC);
183 if (fd != -1) {
184 string content;
185 if (android::base::ReadFdToString(fd, &content)) {
186 proto.write(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_REPORTS,
187 content.c_str());
188 }
189 close(fd);
190 }
191
192 // Remove file from disk after reading.
193 remove(file_name.c_str());
194 }
195}
196
Yao Chenf09569f2017-12-13 17:00:51 -0800197void StorageManager::readConfigFromDisk(map<ConfigKey, StatsdConfig>& configsMap) {
yro947fbce2017-11-15 22:50:23 -0800198 unique_ptr<DIR, decltype(&closedir)> dir(opendir(STATS_SERVICE_DIR), closedir);
199 if (dir == NULL) {
200 VLOG("no default config on disk");
201 return;
202 }
yro98a28502018-01-18 17:00:14 -0800203 trimToFit(STATS_SERVICE_DIR);
yro947fbce2017-11-15 22:50:23 -0800204
205 dirent* de;
206 while ((de = readdir(dir.get()))) {
207 char* name = de->d_name;
208 if (name[0] == '.') continue;
209 VLOG("file %s", name);
210
yro98a28502018-01-18 17:00:14 -0800211 int64_t result[3];
212 parseFileName(name, result);
213 if (result[0] == -1) continue;
214 int64_t timestamp = result[0];
215 int64_t uid = result[1];
216 int64_t configID = result[2];
217 string file_name = getFilePath(STATS_SERVICE_DIR, timestamp, uid, configID);
yro947fbce2017-11-15 22:50:23 -0800218 int fd = open(file_name.c_str(), O_RDONLY | O_CLOEXEC);
219 if (fd != -1) {
220 string content;
221 if (android::base::ReadFdToString(fd, &content)) {
222 StatsdConfig config;
223 if (config.ParseFromString(content)) {
yro6e304ec2018-01-16 21:00:30 -0800224 configsMap[ConfigKey(uid, configID)] = config;
yro98a28502018-01-18 17:00:14 -0800225 VLOG("map key uid=%lld|configID=%lld", (long long)uid, (long long)configID);
yro947fbce2017-11-15 22:50:23 -0800226 }
227 }
228 close(fd);
229 }
230 }
231}
232
yro98a28502018-01-18 17:00:14 -0800233void StorageManager::trimToFit(const char* path) {
234 unique_ptr<DIR, decltype(&closedir)> dir(opendir(path), closedir);
235 if (dir == NULL) {
236 VLOG("Path %s does not exist", path);
237 return;
238 }
239 dirent* de;
240 int totalFileSize = 0;
241 vector<string> fileNames;
242 while ((de = readdir(dir.get()))) {
243 char* name = de->d_name;
244 if (name[0] == '.') continue;
245
246 int64_t result[3];
247 parseFileName(name, result);
248 if (result[0] == -1) continue;
249 int64_t timestamp = result[0];
250 int64_t uid = result[1];
251 int64_t configID = result[2];
252 string file_name = getFilePath(path, timestamp, uid, configID);
253
254 // Check for timestamp and delete if it's too old.
255 long fileAge = time(nullptr) - timestamp;
256 if (fileAge > StatsdStats::kMaxAgeSecond) {
257 deleteFile(file_name.c_str());
258 }
259
260 fileNames.push_back(file_name);
261 ifstream file(file_name.c_str(), ifstream::in | ifstream::binary);
262 if (file.is_open()) {
263 file.seekg(0, ios::end);
264 int fileSize = file.tellg();
265 file.close();
266 totalFileSize += fileSize;
267 }
268 }
269
270 if (fileNames.size() > StatsdStats::kMaxFileNumber ||
271 totalFileSize > StatsdStats::kMaxFileSize) {
272 // Reverse sort to effectively remove from the back (oldest entries).
273 // This will sort files in reverse-chronological order.
274 sort(fileNames.begin(), fileNames.end(), std::greater<std::string>());
275 }
276
277 // Start removing files from oldest to be under the limit.
278 while (fileNames.size() > 0 && (fileNames.size() > StatsdStats::kMaxFileNumber ||
279 totalFileSize > StatsdStats::kMaxFileSize)) {
280 string file_name = fileNames.at(fileNames.size() - 1);
281 ifstream file(file_name.c_str(), ifstream::in | ifstream::binary);
282 if (file.is_open()) {
283 file.seekg(0, ios::end);
284 int fileSize = file.tellg();
285 file.close();
286 totalFileSize -= fileSize;
287 }
288
289 deleteFile(file_name.c_str());
290 fileNames.pop_back();
291 }
292}
293
yro947fbce2017-11-15 22:50:23 -0800294} // namespace statsd
295} // namespace os
296} // namespace android