blob: 9b48a02c7f781bade3562dc5838413a6507fe23f [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
Tej Singh484524a2018-02-01 15:10:05 -080017#define DEBUG false // STOPSHIP if true
yro947fbce2017-11-15 22:50:23 -080018#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"
Yangster-mac330af582018-02-08 15:24:38 -080023#include "stats_log_util.h"
yro947fbce2017-11-15 22:50:23 -080024
25#include <android-base/file.h>
26#include <dirent.h>
yro98a28502018-01-18 17:00:14 -080027#include <private/android_filesystem_config.h>
28#include <fstream>
29#include <iostream>
yro947fbce2017-11-15 22:50:23 -080030
yro947fbce2017-11-15 22:50:23 -080031namespace android {
32namespace os {
33namespace statsd {
34
Yao Chenf09569f2017-12-13 17:00:51 -080035using android::util::FIELD_COUNT_REPEATED;
36using android::util::FIELD_TYPE_MESSAGE;
37using std::map;
38
Jeff Hamiltonfa2f91c2019-03-22 00:25:02 -040039/**
40 * NOTE: these directories are protected by SELinux, any changes here must also update
41 * the SELinux policies.
42 */
yro98a28502018-01-18 17:00:14 -080043#define STATS_DATA_DIR "/data/misc/stats-data"
yro03faf092017-12-12 00:17:50 -080044#define STATS_SERVICE_DIR "/data/misc/stats-service"
Chenjie Yu6b1667c2019-01-18 10:09:33 -080045#define TRAIN_INFO_DIR "/data/misc/train-info"
Jeff Hamiltonfa2f91c2019-03-22 00:25:02 -040046#define TRAIN_INFO_PATH "/data/misc/train-info/train-info.bin"
47
48// Magic word at the start of the train info file, change this if changing the file format
49const uint32_t TRAIN_INFO_FILE_MAGIC = 0xff7447ff;
yro947fbce2017-11-15 22:50:23 -080050
51// for ConfigMetricsReportList
52const int FIELD_ID_REPORTS = 2;
53
Chenjie Yu6b1667c2019-01-18 10:09:33 -080054std::mutex StorageManager::sTrainInfoMutex;
55
yro947fbce2017-11-15 22:50:23 -080056using android::base::StringPrintf;
57using std::unique_ptr;
58
Yao Chen9a43b4f2019-04-10 10:43:20 -070059struct FileName {
60 int64_t mTimestampSec;
61 int mUid;
62 int64_t mConfigId;
63 bool mIsHistory;
64 string getFullFileName(const char* path) {
65 return StringPrintf("%s/%lld_%d_%lld%s", path, (long long)mTimestampSec, (int)mUid,
66 (long long)mConfigId, (mIsHistory ? "_history" : ""));
67 };
68};
69
70string StorageManager::getDataFileName(long wallClockSec, int uid, int64_t id) {
71 return StringPrintf("%s/%ld_%d_%lld", STATS_DATA_DIR, wallClockSec, uid,
72 (long long)id);
73}
74
75string StorageManager::getDataHistoryFileName(long wallClockSec, int uid, int64_t id) {
76 return StringPrintf("%s/%ld_%d_%lld_history", STATS_DATA_DIR, wallClockSec, uid,
77 (long long)id);
78}
79
80// Returns array of int64_t which contains timestamp in seconds, uid,
81// configID and whether the file is a local history file.
82static void parseFileName(char* name, FileName* output) {
83 int64_t result[3];
yro98a28502018-01-18 17:00:14 -080084 int index = 0;
85 char* substr = strtok(name, "_");
86 while (substr != nullptr && index < 3) {
87 result[index] = StrToInt64(substr);
88 index++;
89 substr = strtok(nullptr, "_");
90 }
91 // When index ends before hitting 3, file name is corrupted. We
92 // intentionally put -1 at index 0 to indicate the error to caller.
Yao Chen5bfffb52018-06-21 16:58:51 -070093 // TODO(b/110563137): consider removing files with unexpected name format.
yro98a28502018-01-18 17:00:14 -080094 if (index < 3) {
95 result[0] = -1;
96 }
yro98a28502018-01-18 17:00:14 -080097
Yao Chen9a43b4f2019-04-10 10:43:20 -070098 output->mTimestampSec = result[0];
99 output->mUid = result[1];
100 output->mConfigId = result[2];
101 // check if the file is a local history.
102 output->mIsHistory = (substr != nullptr && strcmp("history", substr) == 0);
yro98a28502018-01-18 17:00:14 -0800103}
104
yro947fbce2017-11-15 22:50:23 -0800105void StorageManager::writeFile(const char* file, const void* buffer, int numBytes) {
106 int fd = open(file, O_WRONLY | O_CREAT | O_CLOEXEC, S_IRUSR | S_IWUSR);
Stefan Lafonc7bdc622017-11-27 12:54:21 -0800107 if (fd == -1) {
yro947fbce2017-11-15 22:50:23 -0800108 VLOG("Attempt to access %s but failed", file);
109 return;
110 }
yro98a28502018-01-18 17:00:14 -0800111 trimToFit(STATS_SERVICE_DIR);
112 trimToFit(STATS_DATA_DIR);
yro947fbce2017-11-15 22:50:23 -0800113
Yao Chen9a43b4f2019-04-10 10:43:20 -0700114 if (android::base::WriteFully(fd, buffer, numBytes)) {
yro947fbce2017-11-15 22:50:23 -0800115 VLOG("Successfully wrote %s", file);
116 } else {
Yao Chen9a43b4f2019-04-10 10:43:20 -0700117 ALOGE("Failed to write %s", file);
yro947fbce2017-11-15 22:50:23 -0800118 }
yro98a28502018-01-18 17:00:14 -0800119
Yao Chen9a43b4f2019-04-10 10:43:20 -0700120 int result = fchown(fd, AID_STATSD, AID_STATSD);
yro98a28502018-01-18 17:00:14 -0800121 if (result) {
122 VLOG("Failed to chown %s to statsd", file);
123 }
124
yro947fbce2017-11-15 22:50:23 -0800125 close(fd);
126}
127
Muhammad Qureshif4ca8242019-03-01 09:20:15 -0800128bool StorageManager::writeTrainInfo(int64_t trainVersionCode, const std::string& trainName,
Jeff Hamiltonfa2f91c2019-03-22 00:25:02 -0400129 int32_t status, const std::vector<int64_t>& experimentIds) {
Chenjie Yu6b1667c2019-01-18 10:09:33 -0800130 std::lock_guard<std::mutex> lock(sTrainInfoMutex);
131
132 deleteAllFiles(TRAIN_INFO_DIR);
133
Jeff Hamiltonfa2f91c2019-03-22 00:25:02 -0400134 int fd = open(TRAIN_INFO_PATH, O_WRONLY | O_CREAT | O_CLOEXEC, S_IRUSR | S_IWUSR);
Chenjie Yu6b1667c2019-01-18 10:09:33 -0800135 if (fd == -1) {
Jeff Hamiltonfa2f91c2019-03-22 00:25:02 -0400136 VLOG("Attempt to access %s but failed", TRAIN_INFO_PATH);
Chenjie Yu6b1667c2019-01-18 10:09:33 -0800137 return false;
138 }
139
Muhammad Qureshif4ca8242019-03-01 09:20:15 -0800140 size_t result;
141
Jeff Hamiltonfa2f91c2019-03-22 00:25:02 -0400142 // Write the magic word
143 result = write(fd, &TRAIN_INFO_FILE_MAGIC, sizeof(TRAIN_INFO_FILE_MAGIC));
144 if (result != sizeof(TRAIN_INFO_FILE_MAGIC)) {
145 VLOG("Failed to wrtie train info magic");
146 close(fd);
147 return false;
148 }
149
150 // Write the train version
151 const size_t trainVersionCodeByteCount = sizeof(trainVersionCode);
152 result = write(fd, &trainVersionCode, trainVersionCodeByteCount);
153 if (result != trainVersionCodeByteCount) {
154 VLOG("Failed to wrtie train version code");
155 close(fd);
156 return false;
157 }
158
Muhammad Qureshif4ca8242019-03-01 09:20:15 -0800159 // Write # of bytes in trainName to file
160 const size_t trainNameSize = trainName.size();
161 const size_t trainNameSizeByteCount = sizeof(trainNameSize);
162 result = write(fd, (uint8_t*)&trainNameSize, trainNameSizeByteCount);
163 if (result != trainNameSizeByteCount) {
Jeff Hamiltonfa2f91c2019-03-22 00:25:02 -0400164 VLOG("Failed to write train name size");
Tej Singhd3e063f2019-03-13 15:23:10 -0700165 close(fd);
Muhammad Qureshif4ca8242019-03-01 09:20:15 -0800166 return false;
167 }
168
169 // Write trainName to file
170 result = write(fd, trainName.c_str(), trainNameSize);
171 if (result != trainNameSize) {
Jeff Hamiltonfa2f91c2019-03-22 00:25:02 -0400172 VLOG("Failed to write train name");
Tej Singhd3e063f2019-03-13 15:23:10 -0700173 close(fd);
Muhammad Qureshif4ca8242019-03-01 09:20:15 -0800174 return false;
175 }
176
177 // Write status to file
178 const size_t statusByteCount = sizeof(status);
179 result = write(fd, (uint8_t*)&status, statusByteCount);
180 if (result != statusByteCount) {
Jeff Hamiltonfa2f91c2019-03-22 00:25:02 -0400181 VLOG("Failed to write status");
Tej Singhd3e063f2019-03-13 15:23:10 -0700182 close(fd);
183 return false;
184 }
185
Jeff Hamiltonfa2f91c2019-03-22 00:25:02 -0400186 // Write experiment id count to file.
187 const size_t experimentIdsCount = experimentIds.size();
188 const size_t experimentIdsCountByteCount = sizeof(experimentIdsCount);
189 result = write(fd, (uint8_t*) &experimentIdsCount, experimentIdsCountByteCount);
190 if (result != experimentIdsCountByteCount) {
191 VLOG("Failed to write experiment id count");
Tej Singhd3e063f2019-03-13 15:23:10 -0700192 close(fd);
Muhammad Qureshif4ca8242019-03-01 09:20:15 -0800193 return false;
194 }
195
196 // Write experimentIds to file
Jeff Hamiltonfa2f91c2019-03-22 00:25:02 -0400197 for (size_t i = 0; i < experimentIdsCount; i++) {
198 const int64_t experimentId = experimentIds[i];
199 const size_t experimentIdByteCount = sizeof(experimentId);
200 result = write(fd, &experimentId, experimentIdByteCount);
201 if (result == experimentIdByteCount) {
202 VLOG("Successfully wrote experiment IDs");
203 } else {
204 VLOG("Failed to write experiment ids");
205 close(fd);
206 return false;
207 }
Chenjie Yu6b1667c2019-01-18 10:09:33 -0800208 }
209
210 result = fchown(fd, AID_STATSD, AID_STATSD);
211 if (result) {
Jeff Hamiltonfa2f91c2019-03-22 00:25:02 -0400212 VLOG("Failed to chown train info file to statsd");
Tej Singhd3e063f2019-03-13 15:23:10 -0700213 close(fd);
Chenjie Yu6b1667c2019-01-18 10:09:33 -0800214 return false;
215 }
216
217 close(fd);
218 return true;
219}
220
Chenjie Yu97dbb202019-02-13 16:42:04 -0800221bool StorageManager::readTrainInfo(InstallTrainInfo& trainInfo) {
Chenjie Yu6b1667c2019-01-18 10:09:33 -0800222 std::lock_guard<std::mutex> lock(sTrainInfoMutex);
223
Jeff Hamiltonfa2f91c2019-03-22 00:25:02 -0400224 int fd = open(TRAIN_INFO_PATH, O_RDONLY | O_CLOEXEC);
225 if (fd == -1) {
226 VLOG("Failed to open train-info.bin");
Chenjie Yu6b1667c2019-01-18 10:09:33 -0800227 return false;
228 }
229
Jeff Hamiltonfa2f91c2019-03-22 00:25:02 -0400230 // Read the magic word
231 uint32_t magic;
232 size_t result = read(fd, &magic, sizeof(magic));
233 if (result != sizeof(magic)) {
234 VLOG("Failed to read train info magic");
Chenjie Yu6b1667c2019-01-18 10:09:33 -0800235 close(fd);
Jeff Hamiltonfa2f91c2019-03-22 00:25:02 -0400236 return false;
Chenjie Yu6b1667c2019-01-18 10:09:33 -0800237 }
Jeff Hamiltonfa2f91c2019-03-22 00:25:02 -0400238
239 if (magic != TRAIN_INFO_FILE_MAGIC) {
240 VLOG("Train info magic was 0x%08x, expected 0x%08x", magic, TRAIN_INFO_FILE_MAGIC);
241 close(fd);
242 return false;
243 }
244
245 // Read the train version code
246 const size_t trainVersionCodeByteCount(sizeof(trainInfo.trainVersionCode));
247 result = read(fd, &trainInfo.trainVersionCode, trainVersionCodeByteCount);
248 if (result != trainVersionCodeByteCount) {
249 VLOG("Failed to read train version code from train info file");
250 close(fd);
251 return false;
252 }
253
254 // Read # of bytes taken by trainName in the file.
255 size_t trainNameSize;
256 result = read(fd, &trainNameSize, sizeof(size_t));
257 if (result != sizeof(size_t)) {
258 VLOG("Failed to read train name size from train info file");
259 close(fd);
260 return false;
261 }
262
263 // Read trainName
264 trainInfo.trainName.resize(trainNameSize);
265 result = read(fd, trainInfo.trainName.data(), trainNameSize);
266 if (result != trainNameSize) {
267 VLOG("Failed to read train name from train info file");
268 close(fd);
269 return false;
270 }
271
272 // Read status
273 const size_t statusByteCount = sizeof(trainInfo.status);
274 result = read(fd, &trainInfo.status, statusByteCount);
275 if (result != statusByteCount) {
276 VLOG("Failed to read train status from train info file");
277 close(fd);
278 return false;
279 }
280
281 // Read experiment ids count.
282 size_t experimentIdsCount;
283 result = read(fd, &experimentIdsCount, sizeof(size_t));
284 if (result != sizeof(size_t)) {
285 VLOG("Failed to read train experiment id count from train info file");
286 close(fd);
287 return false;
288 }
289
290 // Read experimentIds
291 for (size_t i = 0; i < experimentIdsCount; i++) {
292 int64_t experimentId;
293 result = read(fd, &experimentId, sizeof(experimentId));
294 if (result != sizeof(experimentId)) {
295 VLOG("Failed to read train experiment id from train info file");
296 close(fd);
297 return false;
298 }
299 trainInfo.experimentIds.push_back(experimentId);
300 }
301
302 // Expect to be at EOF.
303 char c;
304 result = read(fd, &c, 1);
305 if (result != 0) {
306 VLOG("Failed to read train info from file. Did not get expected EOF.");
307 close(fd);
308 return false;
309 }
310
311 VLOG("Read train info file successful");
312 close(fd);
313 return true;
Chenjie Yu6b1667c2019-01-18 10:09:33 -0800314}
315
yro947fbce2017-11-15 22:50:23 -0800316void StorageManager::deleteFile(const char* file) {
317 if (remove(file) != 0) {
318 VLOG("Attempt to delete %s but is not found", file);
319 } else {
320 VLOG("Successfully deleted %s", file);
321 }
322}
323
324void StorageManager::deleteAllFiles(const char* path) {
325 unique_ptr<DIR, decltype(&closedir)> dir(opendir(path), closedir);
326 if (dir == NULL) {
327 VLOG("Directory does not exist: %s", path);
328 return;
329 }
330
331 dirent* de;
332 while ((de = readdir(dir.get()))) {
333 char* name = de->d_name;
334 if (name[0] == '.') continue;
335 deleteFile(StringPrintf("%s/%s", path, name).c_str());
336 }
337}
338
yroe5f82922018-01-22 18:37:27 -0800339void StorageManager::deleteSuffixedFiles(const char* path, const char* suffix) {
yro947fbce2017-11-15 22:50:23 -0800340 unique_ptr<DIR, decltype(&closedir)> dir(opendir(path), closedir);
341 if (dir == NULL) {
342 VLOG("Directory does not exist: %s", path);
343 return;
344 }
345
346 dirent* de;
347 while ((de = readdir(dir.get()))) {
348 char* name = de->d_name;
yroe5f82922018-01-22 18:37:27 -0800349 if (name[0] == '.') {
yro947fbce2017-11-15 22:50:23 -0800350 continue;
351 }
yroe5f82922018-01-22 18:37:27 -0800352 size_t nameLen = strlen(name);
353 size_t suffixLen = strlen(suffix);
354 if (suffixLen <= nameLen && strncmp(name + nameLen - suffixLen, suffix, suffixLen) == 0) {
355 deleteFile(StringPrintf("%s/%s", path, name).c_str());
356 }
yro947fbce2017-11-15 22:50:23 -0800357 }
358}
359
360void StorageManager::sendBroadcast(const char* path,
361 const std::function<void(const ConfigKey&)>& sendBroadcast) {
362 unique_ptr<DIR, decltype(&closedir)> dir(opendir(path), closedir);
363 if (dir == NULL) {
364 VLOG("no stats-data directory on disk");
365 return;
366 }
367
368 dirent* de;
369 while ((de = readdir(dir.get()))) {
370 char* name = de->d_name;
371 if (name[0] == '.') continue;
372 VLOG("file %s", name);
373
Yao Chen9a43b4f2019-04-10 10:43:20 -0700374 FileName output;
375 parseFileName(name, &output);
376 if (output.mTimestampSec == -1 || output.mIsHistory) continue;
377 sendBroadcast(ConfigKey((int)output.mUid, output.mConfigId));
yro947fbce2017-11-15 22:50:23 -0800378 }
379}
380
David Chen48944902018-05-03 10:29:11 -0700381bool StorageManager::hasConfigMetricsReport(const ConfigKey& key) {
382 unique_ptr<DIR, decltype(&closedir)> dir(opendir(STATS_DATA_DIR), closedir);
383 if (dir == NULL) {
384 VLOG("Path %s does not exist", STATS_DATA_DIR);
385 return false;
386 }
387
388 string suffix = StringPrintf("%d_%lld", key.GetUid(), (long long)key.GetId());
389
390 dirent* de;
391 while ((de = readdir(dir.get()))) {
392 char* name = de->d_name;
393 if (name[0] == '.') continue;
394
395 size_t nameLen = strlen(name);
396 size_t suffixLen = suffix.length();
397 if (suffixLen <= nameLen &&
398 strncmp(name + nameLen - suffixLen, suffix.c_str(), suffixLen) == 0) {
399 // Check again that the file name is parseable.
Yao Chen9a43b4f2019-04-10 10:43:20 -0700400 FileName output;
401 parseFileName(name, &output);
402 if (output.mTimestampSec == -1 || output.mIsHistory) continue;
David Chen48944902018-05-03 10:29:11 -0700403 return true;
404 }
405 }
406 return false;
407}
408
Yao Chen9a43b4f2019-04-10 10:43:20 -0700409void StorageManager::appendConfigMetricsReport(const ConfigKey& key, ProtoOutputStream* proto,
410 bool erase_data, bool isAdb) {
yro98a28502018-01-18 17:00:14 -0800411 unique_ptr<DIR, decltype(&closedir)> dir(opendir(STATS_DATA_DIR), closedir);
Yao Chen72506222017-11-25 15:33:09 -0800412 if (dir == NULL) {
yro98a28502018-01-18 17:00:14 -0800413 VLOG("Path %s does not exist", STATS_DATA_DIR);
yro947fbce2017-11-15 22:50:23 -0800414 return;
415 }
416
417 dirent* de;
418 while ((de = readdir(dir.get()))) {
419 char* name = de->d_name;
Yao Chen9a43b4f2019-04-10 10:43:20 -0700420 string fileName(name);
yro947fbce2017-11-15 22:50:23 -0800421 if (name[0] == '.') continue;
Yao Chen9a43b4f2019-04-10 10:43:20 -0700422 FileName output;
423 parseFileName(name, &output);
yro947fbce2017-11-15 22:50:23 -0800424
Yao Chen9a43b4f2019-04-10 10:43:20 -0700425 if (output.mTimestampSec == -1 || (output.mIsHistory && !isAdb) ||
426 output.mUid != key.GetUid() || output.mConfigId != key.GetId()) {
427 continue;
428 }
yro4beccbe2018-03-15 19:42:05 -0700429
Yao Chen9a43b4f2019-04-10 10:43:20 -0700430 auto fullPathName = StringPrintf("%s/%s", STATS_DATA_DIR, fileName.c_str());
431 int fd = open(fullPathName.c_str(), O_RDONLY | O_CLOEXEC);
432 if (fd != -1) {
433 string content;
434 if (android::base::ReadFdToString(fd, &content)) {
435 proto->write(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_REPORTS,
436 content.c_str(), content.size());
yro947fbce2017-11-15 22:50:23 -0800437 }
Yao Chen9a43b4f2019-04-10 10:43:20 -0700438 close(fd);
439 } else {
440 ALOGE("file cannot be opened");
441 }
yro947fbce2017-11-15 22:50:23 -0800442
Yao Chen9a43b4f2019-04-10 10:43:20 -0700443 if (erase_data) {
444 remove(fullPathName.c_str());
Yao Chen5c10cb42019-06-03 14:45:54 -0700445 } else if (!output.mIsHistory && !isAdb) {
Yao Chen9a43b4f2019-04-10 10:43:20 -0700446 // This means a real data owner has called to get this data. But the config says it
447 // wants to keep a local history. So now this file must be renamed as a history file.
448 // So that next time, when owner calls getData() again, this data won't be uploaded
449 // again. rename returns 0 on success
450 if (rename(fullPathName.c_str(), (fullPathName + "_history").c_str())) {
451 ALOGE("Failed to rename file %s", fullPathName.c_str());
Bookatzc71d9012018-12-19 12:28:38 -0800452 }
yro4beccbe2018-03-15 19:42:05 -0700453 }
yro947fbce2017-11-15 22:50:23 -0800454 }
455}
456
Yangster-mac932ecec2018-02-01 10:23:52 -0800457bool StorageManager::readFileToString(const char* file, string* content) {
458 int fd = open(file, O_RDONLY | O_CLOEXEC);
459 bool res = false;
460 if (fd != -1) {
461 if (android::base::ReadFdToString(fd, content)) {
462 res = true;
463 } else {
464 VLOG("Failed to read file %s\n", file);
465 }
466 close(fd);
467 }
468 return res;
469}
470
Yao Chenf09569f2017-12-13 17:00:51 -0800471void StorageManager::readConfigFromDisk(map<ConfigKey, StatsdConfig>& configsMap) {
yro947fbce2017-11-15 22:50:23 -0800472 unique_ptr<DIR, decltype(&closedir)> dir(opendir(STATS_SERVICE_DIR), closedir);
473 if (dir == NULL) {
474 VLOG("no default config on disk");
475 return;
476 }
yro98a28502018-01-18 17:00:14 -0800477 trimToFit(STATS_SERVICE_DIR);
yro947fbce2017-11-15 22:50:23 -0800478
479 dirent* de;
480 while ((de = readdir(dir.get()))) {
481 char* name = de->d_name;
482 if (name[0] == '.') continue;
yro947fbce2017-11-15 22:50:23 -0800483
Yao Chen9a43b4f2019-04-10 10:43:20 -0700484 FileName output;
485 parseFileName(name, &output);
486 if (output.mTimestampSec == -1) continue;
487 string file_name = output.getFullFileName(STATS_SERVICE_DIR);
yro947fbce2017-11-15 22:50:23 -0800488 int fd = open(file_name.c_str(), O_RDONLY | O_CLOEXEC);
489 if (fd != -1) {
490 string content;
491 if (android::base::ReadFdToString(fd, &content)) {
492 StatsdConfig config;
493 if (config.ParseFromString(content)) {
Yao Chen9a43b4f2019-04-10 10:43:20 -0700494 configsMap[ConfigKey(output.mUid, output.mConfigId)] = config;
495 VLOG("map key uid=%lld|configID=%lld", (long long)output.mUid,
496 (long long)output.mConfigId);
yro947fbce2017-11-15 22:50:23 -0800497 }
498 }
499 close(fd);
500 }
501 }
502}
503
Yangster-macb142cc82018-03-30 15:22:08 -0700504bool StorageManager::readConfigFromDisk(const ConfigKey& key, StatsdConfig* config) {
505 string content;
506 return config != nullptr &&
507 StorageManager::readConfigFromDisk(key, &content) && config->ParseFromString(content);
508}
509
510bool StorageManager::readConfigFromDisk(const ConfigKey& key, string* content) {
yro44907652018-03-12 20:44:05 -0700511 unique_ptr<DIR, decltype(&closedir)> dir(opendir(STATS_SERVICE_DIR),
512 closedir);
513 if (dir == NULL) {
514 VLOG("Directory does not exist: %s", STATS_SERVICE_DIR);
515 return false;
516 }
517
Yao Chenfef48cb2018-03-21 14:43:14 -0700518 string suffix = StringPrintf("%d_%lld", key.GetUid(), (long long)key.GetId());
yro44907652018-03-12 20:44:05 -0700519 dirent* de;
520 while ((de = readdir(dir.get()))) {
521 char* name = de->d_name;
522 if (name[0] == '.') {
523 continue;
524 }
525 size_t nameLen = strlen(name);
Yao Chenfef48cb2018-03-21 14:43:14 -0700526 size_t suffixLen = suffix.length();
yro44907652018-03-12 20:44:05 -0700527 // There can be at most one file that matches this suffix (config key).
528 if (suffixLen <= nameLen &&
Yao Chenfef48cb2018-03-21 14:43:14 -0700529 strncmp(name + nameLen - suffixLen, suffix.c_str(), suffixLen) == 0) {
yro44907652018-03-12 20:44:05 -0700530 int fd = open(StringPrintf("%s/%s", STATS_SERVICE_DIR, name).c_str(),
531 O_RDONLY | O_CLOEXEC);
532 if (fd != -1) {
Yangster-macb142cc82018-03-30 15:22:08 -0700533 if (android::base::ReadFdToString(fd, content)) {
534 return true;
yro44907652018-03-12 20:44:05 -0700535 }
yro4beccbe2018-03-15 19:42:05 -0700536 close(fd);
yro44907652018-03-12 20:44:05 -0700537 }
538 }
539 }
540 return false;
541}
542
Yangster-macb142cc82018-03-30 15:22:08 -0700543bool StorageManager::hasIdenticalConfig(const ConfigKey& key,
544 const vector<uint8_t>& config) {
545 string content;
546 if (StorageManager::readConfigFromDisk(key, &content)) {
547 vector<uint8_t> vec(content.begin(), content.end());
548 if (vec == config) {
549 return true;
550 }
551 }
552 return false;
553}
554
Yao Chen9a43b4f2019-04-10 10:43:20 -0700555void StorageManager::sortFiles(vector<FileInfo>* fileNames) {
556 // Reverse sort to effectively remove from the back (oldest entries).
557 // This will sort files in reverse-chronological order. Local history files have lower
558 // priority than regular data files.
559 sort(fileNames->begin(), fileNames->end(), [](FileInfo& lhs, FileInfo& rhs) {
560 // first consider if the file is a local history
561 if (lhs.mIsHistory && !rhs.mIsHistory) {
562 return false;
563 } else if (rhs.mIsHistory && !lhs.mIsHistory) {
564 return true;
565 }
566
567 // then consider the age.
568 if (lhs.mFileAgeSec < rhs.mFileAgeSec) {
569 return true;
570 } else if (lhs.mFileAgeSec > rhs.mFileAgeSec) {
571 return false;
572 }
573
574 // then good luck.... use string::compare
575 return lhs.mFileName.compare(rhs.mFileName) > 0;
576 });
577}
578
yro98a28502018-01-18 17:00:14 -0800579void StorageManager::trimToFit(const char* path) {
580 unique_ptr<DIR, decltype(&closedir)> dir(opendir(path), closedir);
581 if (dir == NULL) {
582 VLOG("Path %s does not exist", path);
583 return;
584 }
585 dirent* de;
586 int totalFileSize = 0;
Yao Chen9a43b4f2019-04-10 10:43:20 -0700587 vector<FileInfo> fileNames;
588 auto nowSec = getWallClockSec();
yro98a28502018-01-18 17:00:14 -0800589 while ((de = readdir(dir.get()))) {
590 char* name = de->d_name;
591 if (name[0] == '.') continue;
592
Yao Chen9a43b4f2019-04-10 10:43:20 -0700593 FileName output;
594 parseFileName(name, &output);
595 if (output.mTimestampSec == -1) continue;
596 string file_name = output.getFullFileName(path);
yro98a28502018-01-18 17:00:14 -0800597
598 // Check for timestamp and delete if it's too old.
Yao Chen9a43b4f2019-04-10 10:43:20 -0700599 long fileAge = nowSec - output.mTimestampSec;
600 if (fileAge > StatsdStats::kMaxAgeSecond ||
601 (output.mIsHistory && fileAge > StatsdStats::kMaxLocalHistoryAgeSecond)) {
yro98a28502018-01-18 17:00:14 -0800602 deleteFile(file_name.c_str());
Yao Chen9a43b4f2019-04-10 10:43:20 -0700603 continue;
yro98a28502018-01-18 17:00:14 -0800604 }
605
yro98a28502018-01-18 17:00:14 -0800606 ifstream file(file_name.c_str(), ifstream::in | ifstream::binary);
Yao Chen9a43b4f2019-04-10 10:43:20 -0700607 int fileSize = 0;
yro98a28502018-01-18 17:00:14 -0800608 if (file.is_open()) {
609 file.seekg(0, ios::end);
Yao Chen9a43b4f2019-04-10 10:43:20 -0700610 fileSize = file.tellg();
yro98a28502018-01-18 17:00:14 -0800611 file.close();
612 totalFileSize += fileSize;
613 }
Yao Chen9a43b4f2019-04-10 10:43:20 -0700614 fileNames.emplace_back(file_name, output.mIsHistory, fileSize, fileAge);
yro98a28502018-01-18 17:00:14 -0800615 }
616
617 if (fileNames.size() > StatsdStats::kMaxFileNumber ||
618 totalFileSize > StatsdStats::kMaxFileSize) {
Yao Chen9a43b4f2019-04-10 10:43:20 -0700619 sortFiles(&fileNames);
yro98a28502018-01-18 17:00:14 -0800620 }
621
622 // Start removing files from oldest to be under the limit.
623 while (fileNames.size() > 0 && (fileNames.size() > StatsdStats::kMaxFileNumber ||
624 totalFileSize > StatsdStats::kMaxFileSize)) {
Yao Chen9a43b4f2019-04-10 10:43:20 -0700625 totalFileSize -= fileNames.at(fileNames.size() - 1).mFileSizeBytes;
626 deleteFile(fileNames.at(fileNames.size() - 1).mFileName.c_str());
yro98a28502018-01-18 17:00:14 -0800627 fileNames.pop_back();
628 }
629}
630
Yao Chena80e5c02018-09-04 13:55:29 -0700631void StorageManager::printStats(int outFd) {
632 printDirStats(outFd, STATS_SERVICE_DIR);
633 printDirStats(outFd, STATS_DATA_DIR);
yro665208d2018-03-13 18:08:09 -0700634}
635
Yao Chena80e5c02018-09-04 13:55:29 -0700636void StorageManager::printDirStats(int outFd, const char* path) {
637 dprintf(outFd, "Printing stats of %s\n", path);
yro665208d2018-03-13 18:08:09 -0700638 unique_ptr<DIR, decltype(&closedir)> dir(opendir(path), closedir);
639 if (dir == NULL) {
640 VLOG("Path %s does not exist", path);
641 return;
642 }
643 dirent* de;
644 int fileCount = 0;
645 int totalFileSize = 0;
646 while ((de = readdir(dir.get()))) {
647 char* name = de->d_name;
648 if (name[0] == '.') {
649 continue;
650 }
Yao Chen9a43b4f2019-04-10 10:43:20 -0700651 FileName output;
652 parseFileName(name, &output);
653 if (output.mTimestampSec == -1) continue;
654 dprintf(outFd, "\t #%d, Last updated: %lld, UID: %d, Config ID: %lld, %s", fileCount + 1,
655 (long long)output.mTimestampSec, output.mUid, (long long)output.mConfigId,
656 (output.mIsHistory ? "local history" : ""));
657 string file_name = output.getFullFileName(path);
yro665208d2018-03-13 18:08:09 -0700658 ifstream file(file_name.c_str(), ifstream::in | ifstream::binary);
659 if (file.is_open()) {
660 file.seekg(0, ios::end);
661 int fileSize = file.tellg();
662 file.close();
Yao Chena80e5c02018-09-04 13:55:29 -0700663 dprintf(outFd, ", File Size: %d bytes", fileSize);
yro665208d2018-03-13 18:08:09 -0700664 totalFileSize += fileSize;
665 }
Yao Chena80e5c02018-09-04 13:55:29 -0700666 dprintf(outFd, "\n");
yro665208d2018-03-13 18:08:09 -0700667 fileCount++;
668 }
Yao Chena80e5c02018-09-04 13:55:29 -0700669 dprintf(outFd, "\tTotal number of files: %d, Total size of files: %d bytes.\n", fileCount,
670 totalFileSize);
yro665208d2018-03-13 18:08:09 -0700671}
672
yro947fbce2017-11-15 22:50:23 -0800673} // namespace statsd
674} // namespace os
675} // namespace android