| /* |
| * Copyright (C) 2017 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #define DEBUG true // STOPSHIP if true |
| #include "Log.h" |
| |
| #include "storage/StorageManager.h" |
| #include "android-base/stringprintf.h" |
| |
| #include <android-base/file.h> |
| #include <dirent.h> |
| |
| namespace android { |
| namespace os { |
| namespace statsd { |
| |
| using android::util::FIELD_COUNT_REPEATED; |
| using android::util::FIELD_TYPE_MESSAGE; |
| using std::map; |
| |
| #define STATS_SERVICE_DIR "/data/misc/stats-service" |
| |
| // for ConfigMetricsReportList |
| const int FIELD_ID_REPORTS = 2; |
| |
| using android::base::StringPrintf; |
| using std::unique_ptr; |
| |
| void StorageManager::writeFile(const char* file, const void* buffer, int numBytes) { |
| int fd = open(file, O_WRONLY | O_CREAT | O_CLOEXEC, S_IRUSR | S_IWUSR); |
| if (fd == -1) { |
| VLOG("Attempt to access %s but failed", file); |
| return; |
| } |
| |
| int result = write(fd, buffer, numBytes); |
| if (result == numBytes) { |
| VLOG("Successfully wrote %s", file); |
| } else { |
| VLOG("Failed to write %s", file); |
| } |
| close(fd); |
| } |
| |
| void StorageManager::deleteFile(const char* file) { |
| if (remove(file) != 0) { |
| VLOG("Attempt to delete %s but is not found", file); |
| } else { |
| VLOG("Successfully deleted %s", file); |
| } |
| } |
| |
| void StorageManager::deleteAllFiles(const char* path) { |
| unique_ptr<DIR, decltype(&closedir)> dir(opendir(path), closedir); |
| if (dir == NULL) { |
| VLOG("Directory does not exist: %s", path); |
| return; |
| } |
| |
| dirent* de; |
| while ((de = readdir(dir.get()))) { |
| char* name = de->d_name; |
| if (name[0] == '.') continue; |
| deleteFile(StringPrintf("%s/%s", path, name).c_str()); |
| } |
| } |
| |
| void StorageManager::deletePrefixedFiles(const char* path, const char* prefix) { |
| unique_ptr<DIR, decltype(&closedir)> dir(opendir(path), closedir); |
| if (dir == NULL) { |
| VLOG("Directory does not exist: %s", path); |
| return; |
| } |
| |
| dirent* de; |
| while ((de = readdir(dir.get()))) { |
| char* name = de->d_name; |
| if (name[0] == '.' || strncmp(name, prefix, strlen(prefix)) != 0) { |
| continue; |
| } |
| deleteFile(StringPrintf("%s/%s", path, name).c_str()); |
| } |
| } |
| |
| void StorageManager::sendBroadcast(const char* path, |
| const std::function<void(const ConfigKey&)>& sendBroadcast) { |
| unique_ptr<DIR, decltype(&closedir)> dir(opendir(path), closedir); |
| if (dir == NULL) { |
| VLOG("no stats-data directory on disk"); |
| return; |
| } |
| |
| dirent* de; |
| while ((de = readdir(dir.get()))) { |
| char* name = de->d_name; |
| if (name[0] == '.') continue; |
| VLOG("file %s", name); |
| |
| int index = 0; |
| int uid = 0; |
| int64_t configID = 0; |
| char* substr = strtok(name, "-"); |
| // Timestamp lives at index 2 but we skip parsing it as it's not needed. |
| while (substr != nullptr && index < 2) { |
| if (index == 0) { |
| uid = atoi(substr); |
| } else if (index == 1) { |
| configID = StrToInt64(substr); |
| } |
| index++; |
| substr = strtok(nullptr, "-"); |
| } |
| if (index < 2) continue; |
| |
| sendBroadcast(ConfigKey(uid, configID)); |
| } |
| } |
| |
| void StorageManager::appendConfigMetricsReport(const char* path, ProtoOutputStream& proto) { |
| unique_ptr<DIR, decltype(&closedir)> dir(opendir(path), closedir); |
| if (dir == NULL) { |
| VLOG("Path %s does not exist", path); |
| return; |
| } |
| |
| dirent* de; |
| while ((de = readdir(dir.get()))) { |
| char* name = de->d_name; |
| if (name[0] == '.') continue; |
| VLOG("file %s", name); |
| |
| int index = 0; |
| int uid = 0; |
| int64_t configID = 0; |
| int64_t timestamp = 0; |
| char* substr = strtok(name, "-"); |
| while (substr != nullptr && index < 3) { |
| if (index == 0) { |
| uid = atoi(substr); |
| } else if (index == 1) { |
| configID = StrToInt64(substr); |
| } else if (index == 2) { |
| timestamp = atoi(substr); |
| } |
| index++; |
| substr = strtok(nullptr, "-"); |
| } |
| if (index < 3) continue; |
| string file_name = StringPrintf("%s/%d-%lld-%lld", STATS_SERVICE_DIR, uid, |
| (long long)configID, (long long)timestamp); |
| int fd = open(file_name.c_str(), O_RDONLY | O_CLOEXEC); |
| if (fd != -1) { |
| string content; |
| if (android::base::ReadFdToString(fd, &content)) { |
| proto.write(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_REPORTS, |
| content.c_str()); |
| } |
| close(fd); |
| } |
| |
| // Remove file from disk after reading. |
| remove(file_name.c_str()); |
| } |
| } |
| |
| void StorageManager::readConfigFromDisk(map<ConfigKey, StatsdConfig>& configsMap) { |
| unique_ptr<DIR, decltype(&closedir)> dir(opendir(STATS_SERVICE_DIR), closedir); |
| if (dir == NULL) { |
| VLOG("no default config on disk"); |
| return; |
| } |
| |
| dirent* de; |
| while ((de = readdir(dir.get()))) { |
| char* name = de->d_name; |
| if (name[0] == '.') continue; |
| VLOG("file %s", name); |
| |
| int index = 0; |
| int uid = 0; |
| int64_t configID = 0; |
| int64_t timestamp = 0; |
| char* substr = strtok(name, "-"); |
| while (substr != nullptr && index < 3) { |
| if (index == 0) { |
| uid = atoi(substr); |
| } else if (index == 1) { |
| configID = StrToInt64(substr); |
| } else if (index == 2) { |
| timestamp = atoi(substr); |
| } |
| index++; |
| substr = strtok(nullptr, "-"); |
| } |
| if (index < 3) continue; |
| |
| string file_name = StringPrintf("%s/%d-%lld-%lld", STATS_SERVICE_DIR, uid, |
| (long long)configID, (long long)timestamp); |
| int fd = open(file_name.c_str(), O_RDONLY | O_CLOEXEC); |
| if (fd != -1) { |
| string content; |
| if (android::base::ReadFdToString(fd, &content)) { |
| StatsdConfig config; |
| if (config.ParseFromString(content)) { |
| configsMap[ConfigKey(uid, configID)] = config; |
| VLOG("map key uid=%d|configID=%lld", uid, (long long)configID); |
| } |
| } |
| close(fd); |
| } |
| } |
| } |
| |
| } // namespace statsd |
| } // namespace os |
| } // namespace android |