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                 if (android::base::ReadFdToString(fd, content)) {
633                     return true;
634                 }
635                 close(fd);
636             }
637         }
638     }
639     return false;
640 }
641 
hasIdenticalConfig(const ConfigKey & key,const vector<uint8_t> & config)642 bool StorageManager::hasIdenticalConfig(const ConfigKey& key,
643                                         const vector<uint8_t>& config) {
644     string content;
645     if (StorageManager::readConfigFromDisk(key, &content)) {
646         vector<uint8_t> vec(content.begin(), content.end());
647         if (vec == config) {
648             return true;
649         }
650     }
651     return false;
652 }
653 
sortFiles(vector<FileInfo> * fileNames)654 void StorageManager::sortFiles(vector<FileInfo>* fileNames) {
655     // Reverse sort to effectively remove from the back (oldest entries).
656     // This will sort files in reverse-chronological order. Local history files have lower
657     // priority than regular data files.
658     sort(fileNames->begin(), fileNames->end(), [](FileInfo& lhs, FileInfo& rhs) {
659         // first consider if the file is a local history
660         if (lhs.mIsHistory && !rhs.mIsHistory) {
661             return false;
662         } else if (rhs.mIsHistory && !lhs.mIsHistory) {
663             return true;
664         }
665 
666         // then consider the age.
667         if (lhs.mFileAgeSec < rhs.mFileAgeSec) {
668             return true;
669         } else if (lhs.mFileAgeSec > rhs.mFileAgeSec) {
670             return false;
671         }
672 
673         // then good luck.... use string::compare
674         return lhs.mFileName.compare(rhs.mFileName) > 0;
675     });
676 }
677 
trimToFit(const char * path,bool parseTimestampOnly)678 void StorageManager::trimToFit(const char* path, bool parseTimestampOnly) {
679     unique_ptr<DIR, decltype(&closedir)> dir(opendir(path), closedir);
680     if (dir == NULL) {
681         VLOG("Path %s does not exist", path);
682         return;
683     }
684     dirent* de;
685     int totalFileSize = 0;
686     vector<FileInfo> fileNames;
687     auto nowSec = getWallClockSec();
688     while ((de = readdir(dir.get()))) {
689         char* name = de->d_name;
690         if (name[0] == '.') continue;
691 
692         FileName output;
693         string file_name;
694         if (parseTimestampOnly) {
695             file_name = StringPrintf("%s/%s", path, name);
696             output.mTimestampSec = StrToInt64(strtok(name, "_"));
697             output.mIsHistory = false;
698         } else {
699             parseFileName(name, &output);
700             file_name = output.getFullFileName(path);
701         }
702         if (output.mTimestampSec == -1) continue;
703 
704         // Check for timestamp and delete if it's too old.
705         long fileAge = nowSec - output.mTimestampSec;
706         if (fileAge > StatsdStats::kMaxAgeSecond ||
707             (output.mIsHistory && fileAge > StatsdStats::kMaxLocalHistoryAgeSecond)) {
708             deleteFile(file_name.c_str());
709             continue;
710         }
711 
712         ifstream file(file_name.c_str(), ifstream::in | ifstream::binary);
713         int fileSize = 0;
714         if (file.is_open()) {
715             file.seekg(0, ios::end);
716             fileSize = file.tellg();
717             file.close();
718             totalFileSize += fileSize;
719         }
720         fileNames.emplace_back(file_name, output.mIsHistory, fileSize, fileAge);
721     }
722 
723     if (fileNames.size() > StatsdStats::kMaxFileNumber ||
724         totalFileSize > StatsdStats::kMaxFileSize) {
725         sortFiles(&fileNames);
726     }
727 
728     // Start removing files from oldest to be under the limit.
729     while (fileNames.size() > 0 && (fileNames.size() > StatsdStats::kMaxFileNumber ||
730                                     totalFileSize > StatsdStats::kMaxFileSize)) {
731         totalFileSize -= fileNames.at(fileNames.size() - 1).mFileSizeBytes;
732         deleteFile(fileNames.at(fileNames.size() - 1).mFileName.c_str());
733         fileNames.pop_back();
734     }
735 }
736 
printStats(int outFd)737 void StorageManager::printStats(int outFd) {
738     printDirStats(outFd, STATS_SERVICE_DIR);
739     printDirStats(outFd, STATS_DATA_DIR);
740 }
741 
printDirStats(int outFd,const char * path)742 void StorageManager::printDirStats(int outFd, const char* path) {
743     dprintf(outFd, "Printing stats of %s\n", path);
744     unique_ptr<DIR, decltype(&closedir)> dir(opendir(path), closedir);
745     if (dir == NULL) {
746         VLOG("Path %s does not exist", path);
747         return;
748     }
749     dirent* de;
750     int fileCount = 0;
751     int totalFileSize = 0;
752     while ((de = readdir(dir.get()))) {
753         char* name = de->d_name;
754         if (name[0] == '.') {
755             continue;
756         }
757         FileName output;
758         parseFileName(name, &output);
759         if (output.mTimestampSec == -1) continue;
760         dprintf(outFd, "\t #%d, Last updated: %lld, UID: %d, Config ID: %lld, %s", fileCount + 1,
761                 (long long)output.mTimestampSec, output.mUid, (long long)output.mConfigId,
762                 (output.mIsHistory ? "local history" : ""));
763         string file_name = output.getFullFileName(path);
764         ifstream file(file_name.c_str(), ifstream::in | ifstream::binary);
765         if (file.is_open()) {
766             file.seekg(0, ios::end);
767             int fileSize = file.tellg();
768             file.close();
769             dprintf(outFd, ", File Size: %d bytes", fileSize);
770             totalFileSize += fileSize;
771         }
772         dprintf(outFd, "\n");
773         fileCount++;
774     }
775     dprintf(outFd, "\tTotal number of files: %d, Total size of files: %d bytes.\n", fileCount,
776             totalFileSize);
777 }
778 
779 }  // namespace statsd
780 }  // namespace os
781 }  // namespace android
782