1 /*
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 false // STOPSHIP if true
18 #include "Log.h"
19
20 #include "android-base/stringprintf.h"
21 #include "guardrail/StatsdStats.h"
22 #include "storage/StorageManager.h"
23 #include "stats_log_util.h"
24
25 #include <android-base/file.h>
26 #include <private/android_filesystem_config.h>
27 #include <fstream>
28
29 namespace android {
30 namespace os {
31 namespace statsd {
32
33 using android::util::FIELD_COUNT_REPEATED;
34 using android::util::FIELD_TYPE_MESSAGE;
35 using std::map;
36
37 /**
38 * NOTE: these directories are protected by SELinux, any changes here must also update
39 * the SELinux policies.
40 */
41 #define STATS_DATA_DIR "/data/misc/stats-data"
42 #define STATS_SERVICE_DIR "/data/misc/stats-service"
43 #define TRAIN_INFO_DIR "/data/misc/train-info"
44 #define TRAIN_INFO_PATH "/data/misc/train-info/train-info.bin"
45
46 // Magic word at the start of the train info file, change this if changing the file format
47 const uint32_t TRAIN_INFO_FILE_MAGIC = 0xfb7447bf;
48
49 // for ConfigMetricsReportList
50 const int FIELD_ID_REPORTS = 2;
51
52 std::mutex StorageManager::sTrainInfoMutex;
53
54 using android::base::StringPrintf;
55 using std::unique_ptr;
56
57 struct FileName {
58 int64_t mTimestampSec;
59 int mUid;
60 int64_t mConfigId;
61 bool mIsHistory;
getFullFileNameandroid::os::statsd::FileName62 string getFullFileName(const char* path) {
63 return StringPrintf("%s/%lld_%d_%lld%s", path, (long long)mTimestampSec, (int)mUid,
64 (long long)mConfigId, (mIsHistory ? "_history" : ""));
65 };
66 };
67
getDataFileName(long wallClockSec,int uid,int64_t id)68 string StorageManager::getDataFileName(long wallClockSec, int uid, int64_t id) {
69 return StringPrintf("%s/%ld_%d_%lld", STATS_DATA_DIR, wallClockSec, uid,
70 (long long)id);
71 }
72
getDataHistoryFileName(long wallClockSec,int uid,int64_t id)73 string StorageManager::getDataHistoryFileName(long wallClockSec, int uid, int64_t id) {
74 return StringPrintf("%s/%ld_%d_%lld_history", STATS_DATA_DIR, wallClockSec, uid,
75 (long long)id);
76 }
77
findTrainInfoFileNameLocked(const string & trainName)78 static string findTrainInfoFileNameLocked(const string& trainName) {
79 unique_ptr<DIR, decltype(&closedir)> dir(opendir(TRAIN_INFO_DIR), closedir);
80 if (dir == NULL) {
81 VLOG("Path %s does not exist", TRAIN_INFO_DIR);
82 return "";
83 }
84 dirent* de;
85 while ((de = readdir(dir.get()))) {
86 char* fileName = de->d_name;
87 if (fileName[0] == '.') continue;
88
89 size_t fileNameLength = strlen(fileName);
90 if (fileNameLength >= trainName.length()) {
91 if (0 == strncmp(fileName + fileNameLength - trainName.length(), trainName.c_str(),
92 trainName.length())) {
93 return string(fileName);
94 }
95 }
96 }
97
98 return "";
99 }
100
101 // Returns array of int64_t which contains timestamp in seconds, uid,
102 // configID and whether the file is a local history file.
parseFileName(char * name,FileName * output)103 static void parseFileName(char* name, FileName* output) {
104 int64_t result[3];
105 int index = 0;
106 char* substr = strtok(name, "_");
107 while (substr != nullptr && index < 3) {
108 result[index] = StrToInt64(substr);
109 index++;
110 substr = strtok(nullptr, "_");
111 }
112 // When index ends before hitting 3, file name is corrupted. We
113 // intentionally put -1 at index 0 to indicate the error to caller.
114 // TODO(b/110563137): consider removing files with unexpected name format.
115 if (index < 3) {
116 result[0] = -1;
117 }
118
119 output->mTimestampSec = result[0];
120 output->mUid = result[1];
121 output->mConfigId = result[2];
122 // check if the file is a local history.
123 output->mIsHistory = (substr != nullptr && strcmp("history", substr) == 0);
124 }
125
writeFile(const char * file,const void * buffer,int numBytes)126 void StorageManager::writeFile(const char* file, const void* buffer, int numBytes) {
127 int fd = open(file, O_WRONLY | O_CREAT | O_CLOEXEC, S_IRUSR | S_IWUSR);
128 if (fd == -1) {
129 VLOG("Attempt to access %s but failed", file);
130 return;
131 }
132 trimToFit(STATS_SERVICE_DIR);
133 trimToFit(STATS_DATA_DIR);
134
135 if (android::base::WriteFully(fd, buffer, numBytes)) {
136 VLOG("Successfully wrote %s", file);
137 } else {
138 ALOGE("Failed to write %s", file);
139 }
140
141 int result = fchown(fd, AID_STATSD, AID_STATSD);
142 if (result) {
143 VLOG("Failed to chown %s to statsd", file);
144 }
145
146 close(fd);
147 }
148
writeTrainInfo(const InstallTrainInfo & trainInfo)149 bool StorageManager::writeTrainInfo(const InstallTrainInfo& trainInfo) {
150 std::lock_guard<std::mutex> lock(sTrainInfoMutex);
151
152 if (trainInfo.trainName.empty()) {
153 return false;
154 }
155 deleteSuffixedFiles(TRAIN_INFO_DIR, trainInfo.trainName.c_str());
156
157 std::string fileName =
158 StringPrintf("%s/%ld_%s", TRAIN_INFO_DIR, (long) getWallClockSec(),
159 trainInfo.trainName.c_str());
160
161 int fd = open(fileName.c_str(), O_WRONLY | O_CREAT | O_CLOEXEC, S_IRUSR | S_IWUSR);
162 if (fd == -1) {
163 VLOG("Attempt to access %s but failed", fileName.c_str());
164 return false;
165 }
166
167 size_t result;
168 // Write the magic word
169 result = write(fd, &TRAIN_INFO_FILE_MAGIC, sizeof(TRAIN_INFO_FILE_MAGIC));
170 if (result != sizeof(TRAIN_INFO_FILE_MAGIC)) {
171 VLOG("Failed to wrtie train info magic");
172 close(fd);
173 return false;
174 }
175
176 // Write the train version
177 const size_t trainVersionCodeByteCount = sizeof(trainInfo.trainVersionCode);
178 result = write(fd, &trainInfo.trainVersionCode, trainVersionCodeByteCount);
179 if (result != trainVersionCodeByteCount) {
180 VLOG("Failed to wrtie train version code");
181 close(fd);
182 return false;
183 }
184
185 // Write # of bytes in trainName to file
186 const size_t trainNameSize = trainInfo.trainName.size();
187 const size_t trainNameSizeByteCount = sizeof(trainNameSize);
188 result = write(fd, (uint8_t*)&trainNameSize, trainNameSizeByteCount);
189 if (result != trainNameSizeByteCount) {
190 VLOG("Failed to write train name size");
191 close(fd);
192 return false;
193 }
194
195 // Write trainName to file
196 result = write(fd, trainInfo.trainName.c_str(), trainNameSize);
197 if (result != trainNameSize) {
198 VLOG("Failed to write train name");
199 close(fd);
200 return false;
201 }
202
203 // Write status to file
204 const size_t statusByteCount = sizeof(trainInfo.status);
205 result = write(fd, (uint8_t*)&trainInfo.status, statusByteCount);
206 if (result != statusByteCount) {
207 VLOG("Failed to write status");
208 close(fd);
209 return false;
210 }
211
212 // Write experiment id count to file.
213 const size_t experimentIdsCount = trainInfo.experimentIds.size();
214 const size_t experimentIdsCountByteCount = sizeof(experimentIdsCount);
215 result = write(fd, (uint8_t*) &experimentIdsCount, experimentIdsCountByteCount);
216 if (result != experimentIdsCountByteCount) {
217 VLOG("Failed to write experiment id count");
218 close(fd);
219 return false;
220 }
221
222 // Write experimentIds to file
223 for (size_t i = 0; i < experimentIdsCount; i++) {
224 const int64_t experimentId = trainInfo.experimentIds[i];
225 const size_t experimentIdByteCount = sizeof(experimentId);
226 result = write(fd, &experimentId, experimentIdByteCount);
227 if (result == experimentIdByteCount) {
228 VLOG("Successfully wrote experiment IDs");
229 } else {
230 VLOG("Failed to write experiment ids");
231 close(fd);
232 return false;
233 }
234 }
235
236 // Write bools to file
237 const size_t boolByteCount = sizeof(trainInfo.requiresStaging);
238 result = write(fd, (uint8_t*)&trainInfo.requiresStaging, boolByteCount);
239 if (result != boolByteCount) {
240 VLOG("Failed to write requires staging");
241 close(fd);
242 return false;
243 }
244
245 result = write(fd, (uint8_t*)&trainInfo.rollbackEnabled, boolByteCount);
246 if (result != boolByteCount) {
247 VLOG("Failed to write rollback enabled");
248 close(fd);
249 return false;
250 }
251
252 result = write(fd, (uint8_t*)&trainInfo.requiresLowLatencyMonitor, boolByteCount);
253 if (result != boolByteCount) {
254 VLOG("Failed to write requires log latency monitor");
255 close(fd);
256 return false;
257 }
258
259 close(fd);
260 return true;
261 }
262
readTrainInfo(const std::string & trainName,InstallTrainInfo & trainInfo)263 bool StorageManager::readTrainInfo(const std::string& trainName, InstallTrainInfo& trainInfo) {
264 std::lock_guard<std::mutex> lock(sTrainInfoMutex);
265 return readTrainInfoLocked(trainName, trainInfo);
266 }
267
readTrainInfoLocked(const std::string & trainName,InstallTrainInfo & trainInfo)268 bool StorageManager::readTrainInfoLocked(const std::string& trainName, InstallTrainInfo& trainInfo) {
269 trimToFit(TRAIN_INFO_DIR, /*parseTimestampOnly=*/ true);
270 string fileName = findTrainInfoFileNameLocked(trainName);
271 if (fileName.empty()) {
272 return false;
273 }
274 int fd = open(StringPrintf("%s/%s", TRAIN_INFO_DIR, fileName.c_str()).c_str(), O_RDONLY | O_CLOEXEC);
275 if (fd == -1) {
276 VLOG("Failed to open %s", fileName.c_str());
277 return false;
278 }
279
280 // Read the magic word
281 uint32_t magic;
282 size_t result = read(fd, &magic, sizeof(magic));
283 if (result != sizeof(magic)) {
284 VLOG("Failed to read train info magic");
285 close(fd);
286 return false;
287 }
288
289 if (magic != TRAIN_INFO_FILE_MAGIC) {
290 VLOG("Train info magic was 0x%08x, expected 0x%08x", magic, TRAIN_INFO_FILE_MAGIC);
291 close(fd);
292 return false;
293 }
294
295 // Read the train version code
296 const size_t trainVersionCodeByteCount(sizeof(trainInfo.trainVersionCode));
297 result = read(fd, &trainInfo.trainVersionCode, trainVersionCodeByteCount);
298 if (result != trainVersionCodeByteCount) {
299 VLOG("Failed to read train version code from train info file");
300 close(fd);
301 return false;
302 }
303
304 // Read # of bytes taken by trainName in the file.
305 size_t trainNameSize;
306 result = read(fd, &trainNameSize, sizeof(size_t));
307 if (result != sizeof(size_t)) {
308 VLOG("Failed to read train name size from train info file");
309 close(fd);
310 return false;
311 }
312
313 // Read trainName
314 trainInfo.trainName.resize(trainNameSize);
315 result = read(fd, trainInfo.trainName.data(), trainNameSize);
316 if (result != trainNameSize) {
317 VLOG("Failed to read train name from train info file");
318 close(fd);
319 return false;
320 }
321
322 // Read status
323 const size_t statusByteCount = sizeof(trainInfo.status);
324 result = read(fd, &trainInfo.status, statusByteCount);
325 if (result != statusByteCount) {
326 VLOG("Failed to read train status from train info file");
327 close(fd);
328 return false;
329 }
330
331 // Read experiment ids count.
332 size_t experimentIdsCount;
333 result = read(fd, &experimentIdsCount, sizeof(size_t));
334 if (result != sizeof(size_t)) {
335 VLOG("Failed to read train experiment id count from train info file");
336 close(fd);
337 return false;
338 }
339
340 // Read experimentIds
341 for (size_t i = 0; i < experimentIdsCount; i++) {
342 int64_t experimentId;
343 result = read(fd, &experimentId, sizeof(experimentId));
344 if (result != sizeof(experimentId)) {
345 VLOG("Failed to read train experiment id from train info file");
346 close(fd);
347 return false;
348 }
349 trainInfo.experimentIds.push_back(experimentId);
350 }
351
352 // Read bools
353 const size_t boolByteCount = sizeof(trainInfo.requiresStaging);
354 result = read(fd, &trainInfo.requiresStaging, boolByteCount);
355 if (result != boolByteCount) {
356 VLOG("Failed to read requires requires staging from train info file");
357 close(fd);
358 return false;
359 }
360
361 result = read(fd, &trainInfo.rollbackEnabled, boolByteCount);
362 if (result != boolByteCount) {
363 VLOG("Failed to read requires rollback enabled from train info file");
364 close(fd);
365 return false;
366 }
367
368 result = read(fd, &trainInfo.requiresLowLatencyMonitor, boolByteCount);
369 if (result != boolByteCount) {
370 VLOG("Failed to read requires requires low latency monitor from train info file");
371 close(fd);
372 return false;
373 }
374
375 // Expect to be at EOF.
376 char c;
377 result = read(fd, &c, 1);
378 if (result != 0) {
379 VLOG("Failed to read train info from file. Did not get expected EOF.");
380 close(fd);
381 return false;
382 }
383
384 VLOG("Read train info file successful");
385 close(fd);
386 return true;
387 }
388
readAllTrainInfo()389 vector<InstallTrainInfo> StorageManager::readAllTrainInfo() {
390 std::lock_guard<std::mutex> lock(sTrainInfoMutex);
391 vector<InstallTrainInfo> trainInfoList;
392 unique_ptr<DIR, decltype(&closedir)> dir(opendir(TRAIN_INFO_DIR), closedir);
393 if (dir == NULL) {
394 VLOG("Directory does not exist: %s", TRAIN_INFO_DIR);
395 return trainInfoList;
396 }
397
398 dirent* de;
399 while ((de = readdir(dir.get()))) {
400 char* name = de->d_name;
401 if (name[0] == '.') {
402 continue;
403 }
404
405 InstallTrainInfo trainInfo;
406 bool readSuccess = StorageManager::readTrainInfoLocked(name, trainInfo);
407 if (!readSuccess) {
408 continue;
409 }
410 trainInfoList.push_back(trainInfo);
411 }
412 return trainInfoList;
413 }
414
deleteFile(const char * file)415 void StorageManager::deleteFile(const char* file) {
416 if (remove(file) != 0) {
417 VLOG("Attempt to delete %s but is not found", file);
418 } else {
419 VLOG("Successfully deleted %s", file);
420 }
421 }
422
deleteAllFiles(const char * path)423 void StorageManager::deleteAllFiles(const char* path) {
424 unique_ptr<DIR, decltype(&closedir)> dir(opendir(path), closedir);
425 if (dir == NULL) {
426 VLOG("Directory does not exist: %s", path);
427 return;
428 }
429
430 dirent* de;
431 while ((de = readdir(dir.get()))) {
432 char* name = de->d_name;
433 if (name[0] == '.') continue;
434 deleteFile(StringPrintf("%s/%s", path, name).c_str());
435 }
436 }
437
deleteSuffixedFiles(const char * path,const char * suffix)438 void StorageManager::deleteSuffixedFiles(const char* path, const char* suffix) {
439 unique_ptr<DIR, decltype(&closedir)> dir(opendir(path), closedir);
440 if (dir == NULL) {
441 VLOG("Directory does not exist: %s", path);
442 return;
443 }
444
445 dirent* de;
446 while ((de = readdir(dir.get()))) {
447 char* name = de->d_name;
448 if (name[0] == '.') {
449 continue;
450 }
451 size_t nameLen = strlen(name);
452 size_t suffixLen = strlen(suffix);
453 if (suffixLen <= nameLen && strncmp(name + nameLen - suffixLen, suffix, suffixLen) == 0) {
454 deleteFile(StringPrintf("%s/%s", path, name).c_str());
455 }
456 }
457 }
458
sendBroadcast(const char * path,const std::function<void (const ConfigKey &)> & sendBroadcast)459 void StorageManager::sendBroadcast(const char* path,
460 const std::function<void(const ConfigKey&)>& sendBroadcast) {
461 unique_ptr<DIR, decltype(&closedir)> dir(opendir(path), closedir);
462 if (dir == NULL) {
463 VLOG("no stats-data directory on disk");
464 return;
465 }
466
467 dirent* de;
468 while ((de = readdir(dir.get()))) {
469 char* name = de->d_name;
470 if (name[0] == '.') continue;
471 VLOG("file %s", name);
472
473 FileName output;
474 parseFileName(name, &output);
475 if (output.mTimestampSec == -1 || output.mIsHistory) continue;
476 sendBroadcast(ConfigKey((int)output.mUid, output.mConfigId));
477 }
478 }
479
hasConfigMetricsReport(const ConfigKey & key)480 bool StorageManager::hasConfigMetricsReport(const ConfigKey& key) {
481 unique_ptr<DIR, decltype(&closedir)> dir(opendir(STATS_DATA_DIR), closedir);
482 if (dir == NULL) {
483 VLOG("Path %s does not exist", STATS_DATA_DIR);
484 return false;
485 }
486
487 string suffix = StringPrintf("%d_%lld", key.GetUid(), (long long)key.GetId());
488
489 dirent* de;
490 while ((de = readdir(dir.get()))) {
491 char* name = de->d_name;
492 if (name[0] == '.') continue;
493
494 size_t nameLen = strlen(name);
495 size_t suffixLen = suffix.length();
496 if (suffixLen <= nameLen &&
497 strncmp(name + nameLen - suffixLen, suffix.c_str(), suffixLen) == 0) {
498 // Check again that the file name is parseable.
499 FileName output;
500 parseFileName(name, &output);
501 if (output.mTimestampSec == -1 || output.mIsHistory) continue;
502 return true;
503 }
504 }
505 return false;
506 }
507
appendConfigMetricsReport(const ConfigKey & key,ProtoOutputStream * proto,bool erase_data,bool isAdb)508 void StorageManager::appendConfigMetricsReport(const ConfigKey& key, ProtoOutputStream* proto,
509 bool erase_data, bool isAdb) {
510 unique_ptr<DIR, decltype(&closedir)> dir(opendir(STATS_DATA_DIR), closedir);
511 if (dir == NULL) {
512 VLOG("Path %s does not exist", STATS_DATA_DIR);
513 return;
514 }
515
516 dirent* de;
517 while ((de = readdir(dir.get()))) {
518 char* name = de->d_name;
519 string fileName(name);
520 if (name[0] == '.') continue;
521 FileName output;
522 parseFileName(name, &output);
523
524 if (output.mTimestampSec == -1 || (output.mIsHistory && !isAdb) ||
525 output.mUid != key.GetUid() || output.mConfigId != key.GetId()) {
526 continue;
527 }
528
529 auto fullPathName = StringPrintf("%s/%s", STATS_DATA_DIR, fileName.c_str());
530 int fd = open(fullPathName.c_str(), O_RDONLY | O_CLOEXEC);
531 if (fd != -1) {
532 string content;
533 if (android::base::ReadFdToString(fd, &content)) {
534 proto->write(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_REPORTS,
535 content.c_str(), content.size());
536 }
537 close(fd);
538 } else {
539 ALOGE("file cannot be opened");
540 }
541
542 if (erase_data) {
543 remove(fullPathName.c_str());
544 } else if (!output.mIsHistory && !isAdb) {
545 // This means a real data owner has called to get this data. But the config says it
546 // wants to keep a local history. So now this file must be renamed as a history file.
547 // So that next time, when owner calls getData() again, this data won't be uploaded
548 // again. rename returns 0 on success
549 if (rename(fullPathName.c_str(), (fullPathName + "_history").c_str())) {
550 ALOGE("Failed to rename file %s", fullPathName.c_str());
551 }
552 }
553 }
554 }
555
readFileToString(const char * file,string * content)556 bool StorageManager::readFileToString(const char* file, string* content) {
557 int fd = open(file, O_RDONLY | O_CLOEXEC);
558 bool res = false;
559 if (fd != -1) {
560 if (android::base::ReadFdToString(fd, content)) {
561 res = true;
562 } else {
563 VLOG("Failed to read file %s\n", file);
564 }
565 close(fd);
566 }
567 return res;
568 }
569
readConfigFromDisk(map<ConfigKey,StatsdConfig> & configsMap)570 void StorageManager::readConfigFromDisk(map<ConfigKey, StatsdConfig>& configsMap) {
571 unique_ptr<DIR, decltype(&closedir)> dir(opendir(STATS_SERVICE_DIR), closedir);
572 if (dir == NULL) {
573 VLOG("no default config on disk");
574 return;
575 }
576 trimToFit(STATS_SERVICE_DIR);
577
578 dirent* de;
579 while ((de = readdir(dir.get()))) {
580 char* name = de->d_name;
581 if (name[0] == '.') continue;
582
583 FileName output;
584 parseFileName(name, &output);
585 if (output.mTimestampSec == -1) continue;
586 string file_name = output.getFullFileName(STATS_SERVICE_DIR);
587 int fd = open(file_name.c_str(), O_RDONLY | O_CLOEXEC);
588 if (fd != -1) {
589 string content;
590 if (android::base::ReadFdToString(fd, &content)) {
591 StatsdConfig config;
592 if (config.ParseFromString(content)) {
593 configsMap[ConfigKey(output.mUid, output.mConfigId)] = config;
594 VLOG("map key uid=%lld|configID=%lld", (long long)output.mUid,
595 (long long)output.mConfigId);
596 }
597 }
598 close(fd);
599 }
600 }
601 }
602
readConfigFromDisk(const ConfigKey & key,StatsdConfig * config)603 bool StorageManager::readConfigFromDisk(const ConfigKey& key, StatsdConfig* config) {
604 string content;
605 return config != nullptr &&
606 StorageManager::readConfigFromDisk(key, &content) && config->ParseFromString(content);
607 }
608
readConfigFromDisk(const ConfigKey & key,string * content)609 bool StorageManager::readConfigFromDisk(const ConfigKey& key, string* content) {
610 unique_ptr<DIR, decltype(&closedir)> dir(opendir(STATS_SERVICE_DIR),
611 closedir);
612 if (dir == NULL) {
613 VLOG("Directory does not exist: %s", STATS_SERVICE_DIR);
614 return false;
615 }
616
617 string suffix = StringPrintf("%d_%lld", key.GetUid(), (long long)key.GetId());
618 dirent* de;
619 while ((de = readdir(dir.get()))) {
620 char* name = de->d_name;
621 if (name[0] == '.') {
622 continue;
623 }
624 size_t nameLen = strlen(name);
625 size_t suffixLen = suffix.length();
626 // There can be at most one file that matches this suffix (config key).
627 if (suffixLen <= nameLen &&
628 strncmp(name + nameLen - suffixLen, suffix.c_str(), suffixLen) == 0) {
629 int fd = open(StringPrintf("%s/%s", STATS_SERVICE_DIR, name).c_str(),
630 O_RDONLY | O_CLOEXEC);
631 if (fd != -1) {
632 bool success = android::base::ReadFdToString(fd, content);
633 close(fd);
634 return success;
635 }
636 }
637 }
638 return false;
639 }
640
hasIdenticalConfig(const ConfigKey & key,const vector<uint8_t> & config)641 bool StorageManager::hasIdenticalConfig(const ConfigKey& key,
642 const vector<uint8_t>& config) {
643 string content;
644 if (StorageManager::readConfigFromDisk(key, &content)) {
645 vector<uint8_t> vec(content.begin(), content.end());
646 if (vec == config) {
647 return true;
648 }
649 }
650 return false;
651 }
652
sortFiles(vector<FileInfo> * fileNames)653 void StorageManager::sortFiles(vector<FileInfo>* fileNames) {
654 // Reverse sort to effectively remove from the back (oldest entries).
655 // This will sort files in reverse-chronological order. Local history files have lower
656 // priority than regular data files.
657 sort(fileNames->begin(), fileNames->end(), [](FileInfo& lhs, FileInfo& rhs) {
658 // first consider if the file is a local history
659 if (lhs.mIsHistory && !rhs.mIsHistory) {
660 return false;
661 } else if (rhs.mIsHistory && !lhs.mIsHistory) {
662 return true;
663 }
664
665 // then consider the age.
666 if (lhs.mFileAgeSec < rhs.mFileAgeSec) {
667 return true;
668 } else if (lhs.mFileAgeSec > rhs.mFileAgeSec) {
669 return false;
670 }
671
672 // then good luck.... use string::compare
673 return lhs.mFileName.compare(rhs.mFileName) > 0;
674 });
675 }
676
trimToFit(const char * path,bool parseTimestampOnly)677 void StorageManager::trimToFit(const char* path, bool parseTimestampOnly) {
678 unique_ptr<DIR, decltype(&closedir)> dir(opendir(path), closedir);
679 if (dir == NULL) {
680 VLOG("Path %s does not exist", path);
681 return;
682 }
683 dirent* de;
684 int totalFileSize = 0;
685 vector<FileInfo> fileNames;
686 auto nowSec = getWallClockSec();
687 while ((de = readdir(dir.get()))) {
688 char* name = de->d_name;
689 if (name[0] == '.') continue;
690
691 FileName output;
692 string file_name;
693 if (parseTimestampOnly) {
694 file_name = StringPrintf("%s/%s", path, name);
695 output.mTimestampSec = StrToInt64(strtok(name, "_"));
696 output.mIsHistory = false;
697 } else {
698 parseFileName(name, &output);
699 file_name = output.getFullFileName(path);
700 }
701 if (output.mTimestampSec == -1) continue;
702
703 // Check for timestamp and delete if it's too old.
704 long fileAge = nowSec - output.mTimestampSec;
705 if (fileAge > StatsdStats::kMaxAgeSecond ||
706 (output.mIsHistory && fileAge > StatsdStats::kMaxLocalHistoryAgeSecond)) {
707 deleteFile(file_name.c_str());
708 continue;
709 }
710
711 ifstream file(file_name.c_str(), ifstream::in | ifstream::binary);
712 int fileSize = 0;
713 if (file.is_open()) {
714 file.seekg(0, ios::end);
715 fileSize = file.tellg();
716 file.close();
717 totalFileSize += fileSize;
718 }
719 fileNames.emplace_back(file_name, output.mIsHistory, fileSize, fileAge);
720 }
721
722 if (fileNames.size() > StatsdStats::kMaxFileNumber ||
723 totalFileSize > StatsdStats::kMaxFileSize) {
724 sortFiles(&fileNames);
725 }
726
727 // Start removing files from oldest to be under the limit.
728 while (fileNames.size() > 0 && (fileNames.size() > StatsdStats::kMaxFileNumber ||
729 totalFileSize > StatsdStats::kMaxFileSize)) {
730 totalFileSize -= fileNames.at(fileNames.size() - 1).mFileSizeBytes;
731 deleteFile(fileNames.at(fileNames.size() - 1).mFileName.c_str());
732 fileNames.pop_back();
733 }
734 }
735
printStats(int outFd)736 void StorageManager::printStats(int outFd) {
737 printDirStats(outFd, STATS_SERVICE_DIR);
738 printDirStats(outFd, STATS_DATA_DIR);
739 }
740
printDirStats(int outFd,const char * path)741 void StorageManager::printDirStats(int outFd, const char* path) {
742 dprintf(outFd, "Printing stats of %s\n", path);
743 unique_ptr<DIR, decltype(&closedir)> dir(opendir(path), closedir);
744 if (dir == NULL) {
745 VLOG("Path %s does not exist", path);
746 return;
747 }
748 dirent* de;
749 int fileCount = 0;
750 int totalFileSize = 0;
751 while ((de = readdir(dir.get()))) {
752 char* name = de->d_name;
753 if (name[0] == '.') {
754 continue;
755 }
756 FileName output;
757 parseFileName(name, &output);
758 if (output.mTimestampSec == -1) continue;
759 dprintf(outFd, "\t #%d, Last updated: %lld, UID: %d, Config ID: %lld, %s", fileCount + 1,
760 (long long)output.mTimestampSec, output.mUid, (long long)output.mConfigId,
761 (output.mIsHistory ? "local history" : ""));
762 string file_name = output.getFullFileName(path);
763 ifstream file(file_name.c_str(), ifstream::in | ifstream::binary);
764 if (file.is_open()) {
765 file.seekg(0, ios::end);
766 int fileSize = file.tellg();
767 file.close();
768 dprintf(outFd, ", File Size: %d bytes", fileSize);
769 totalFileSize += fileSize;
770 }
771 dprintf(outFd, "\n");
772 fileCount++;
773 }
774 dprintf(outFd, "\tTotal number of files: %d, Total size of files: %d bytes.\n", fileCount,
775 totalFileSize);
776 }
777
778 } // namespace statsd
779 } // namespace os
780 } // namespace android
781