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; 62 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 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 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 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. 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 736 void StorageManager::printStats(int outFd) { 737 printDirStats(outFd, STATS_SERVICE_DIR); 738 printDirStats(outFd, STATS_DATA_DIR); 739 } 740 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