blob: 165e57c7374385ec8b4cb585dd22ab7fbfa9662b [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
yro98a28502018-01-18 17:00:14 -080039#define STATS_DATA_DIR "/data/misc/stats-data"
yro03faf092017-12-12 00:17:50 -080040#define STATS_SERVICE_DIR "/data/misc/stats-service"
Chenjie Yu6b1667c2019-01-18 10:09:33 -080041#define TRAIN_INFO_DIR "/data/misc/train-info"
yro947fbce2017-11-15 22:50:23 -080042
43// for ConfigMetricsReportList
44const int FIELD_ID_REPORTS = 2;
45
Chenjie Yu6b1667c2019-01-18 10:09:33 -080046std::mutex StorageManager::sTrainInfoMutex;
47
yro947fbce2017-11-15 22:50:23 -080048using android::base::StringPrintf;
49using std::unique_ptr;
50
yro98a28502018-01-18 17:00:14 -080051// Returns array of int64_t which contains timestamp in seconds, uid, and
52// configID.
53static void parseFileName(char* name, int64_t* result) {
54 int index = 0;
55 char* substr = strtok(name, "_");
56 while (substr != nullptr && index < 3) {
57 result[index] = StrToInt64(substr);
58 index++;
59 substr = strtok(nullptr, "_");
60 }
61 // When index ends before hitting 3, file name is corrupted. We
62 // intentionally put -1 at index 0 to indicate the error to caller.
Yao Chen5bfffb52018-06-21 16:58:51 -070063 // TODO(b/110563137): consider removing files with unexpected name format.
yro98a28502018-01-18 17:00:14 -080064 if (index < 3) {
65 result[0] = -1;
66 }
67}
68
69static string getFilePath(const char* path, int64_t timestamp, int64_t uid, int64_t configID) {
yro50d23f12018-01-25 13:52:20 -080070 return StringPrintf("%s/%lld_%d_%lld", path, (long long)timestamp, (int)uid,
yro98a28502018-01-18 17:00:14 -080071 (long long)configID);
72}
73
yro947fbce2017-11-15 22:50:23 -080074void StorageManager::writeFile(const char* file, const void* buffer, int numBytes) {
75 int fd = open(file, O_WRONLY | O_CREAT | O_CLOEXEC, S_IRUSR | S_IWUSR);
Stefan Lafonc7bdc622017-11-27 12:54:21 -080076 if (fd == -1) {
yro947fbce2017-11-15 22:50:23 -080077 VLOG("Attempt to access %s but failed", file);
78 return;
79 }
yro98a28502018-01-18 17:00:14 -080080 trimToFit(STATS_SERVICE_DIR);
81 trimToFit(STATS_DATA_DIR);
yro947fbce2017-11-15 22:50:23 -080082
83 int result = write(fd, buffer, numBytes);
84 if (result == numBytes) {
85 VLOG("Successfully wrote %s", file);
86 } else {
87 VLOG("Failed to write %s", file);
88 }
yro98a28502018-01-18 17:00:14 -080089
90 result = fchown(fd, AID_STATSD, AID_STATSD);
91 if (result) {
92 VLOG("Failed to chown %s to statsd", file);
93 }
94
yro947fbce2017-11-15 22:50:23 -080095 close(fd);
96}
97
Chenjie Yu6b1667c2019-01-18 10:09:33 -080098bool StorageManager::writeTrainInfo(int64_t trainVersionCode,
99 const std::vector<uint8_t>& experimentIds) {
100 std::lock_guard<std::mutex> lock(sTrainInfoMutex);
101
102 deleteAllFiles(TRAIN_INFO_DIR);
103
104 string file_name = StringPrintf("%s/%lld", TRAIN_INFO_DIR, (long long)trainVersionCode);
105
106 int fd = open(file_name.c_str(), O_WRONLY | O_CREAT | O_CLOEXEC, S_IRUSR | S_IWUSR);
107 if (fd == -1) {
108 VLOG("Attempt to access %s but failed", file_name.c_str());
109 return false;
110 }
111
112 size_t result = write(fd, experimentIds.data(), experimentIds.size());
113 if (result == experimentIds.size()) {
114 VLOG("Successfully wrote %s", file_name.c_str());
115 } else {
116 VLOG("Failed to write %s", file_name.c_str());
117 return false;
118 }
119
120 result = fchown(fd, AID_STATSD, AID_STATSD);
121 if (result) {
122 VLOG("Failed to chown %s to statsd", file_name.c_str());
123 return false;
124 }
125
126 close(fd);
127 return true;
128}
129
Chenjie Yu97dbb202019-02-13 16:42:04 -0800130bool StorageManager::readTrainInfo(InstallTrainInfo& trainInfo) {
Chenjie Yu6b1667c2019-01-18 10:09:33 -0800131 std::lock_guard<std::mutex> lock(sTrainInfoMutex);
132
133 unique_ptr<DIR, decltype(&closedir)> dir(opendir(TRAIN_INFO_DIR), closedir);
134
135 if (dir == NULL) {
136 VLOG("Directory does not exist: %s", TRAIN_INFO_DIR);
137 return false;
138 }
139
140 dirent* de;
141 while ((de = readdir(dir.get()))) {
142 char* name = de->d_name;
143 if (name[0] == '.') {
144 continue;
145 }
146 trainInfo.trainVersionCode = StrToInt64(name);
147 string fullPath = StringPrintf("%s/%s", TRAIN_INFO_DIR, name);
148 int fd = open(fullPath.c_str(), O_RDONLY | O_CLOEXEC);
149 if (fd != -1) {
150 string str;
151 if (android::base::ReadFdToString(fd, &str)) {
152 close(fd);
153 std::copy(str.begin(), str.end(), std::back_inserter(trainInfo.experimentIds));
154 VLOG("Read train info file successful: %s", fullPath.c_str());
155 return true;
156 }
157 }
158 close(fd);
159 }
160 return false;
161}
162
yro947fbce2017-11-15 22:50:23 -0800163void StorageManager::deleteFile(const char* file) {
164 if (remove(file) != 0) {
165 VLOG("Attempt to delete %s but is not found", file);
166 } else {
167 VLOG("Successfully deleted %s", file);
168 }
169}
170
171void StorageManager::deleteAllFiles(const char* path) {
172 unique_ptr<DIR, decltype(&closedir)> dir(opendir(path), closedir);
173 if (dir == NULL) {
174 VLOG("Directory does not exist: %s", path);
175 return;
176 }
177
178 dirent* de;
179 while ((de = readdir(dir.get()))) {
180 char* name = de->d_name;
181 if (name[0] == '.') continue;
182 deleteFile(StringPrintf("%s/%s", path, name).c_str());
183 }
184}
185
yroe5f82922018-01-22 18:37:27 -0800186void StorageManager::deleteSuffixedFiles(const char* path, const char* suffix) {
yro947fbce2017-11-15 22:50:23 -0800187 unique_ptr<DIR, decltype(&closedir)> dir(opendir(path), closedir);
188 if (dir == NULL) {
189 VLOG("Directory does not exist: %s", path);
190 return;
191 }
192
193 dirent* de;
194 while ((de = readdir(dir.get()))) {
195 char* name = de->d_name;
yroe5f82922018-01-22 18:37:27 -0800196 if (name[0] == '.') {
yro947fbce2017-11-15 22:50:23 -0800197 continue;
198 }
yroe5f82922018-01-22 18:37:27 -0800199 size_t nameLen = strlen(name);
200 size_t suffixLen = strlen(suffix);
201 if (suffixLen <= nameLen && strncmp(name + nameLen - suffixLen, suffix, suffixLen) == 0) {
202 deleteFile(StringPrintf("%s/%s", path, name).c_str());
203 }
yro947fbce2017-11-15 22:50:23 -0800204 }
205}
206
207void StorageManager::sendBroadcast(const char* path,
208 const std::function<void(const ConfigKey&)>& sendBroadcast) {
209 unique_ptr<DIR, decltype(&closedir)> dir(opendir(path), closedir);
210 if (dir == NULL) {
211 VLOG("no stats-data directory on disk");
212 return;
213 }
214
215 dirent* de;
216 while ((de = readdir(dir.get()))) {
217 char* name = de->d_name;
218 if (name[0] == '.') continue;
219 VLOG("file %s", name);
220
yro98a28502018-01-18 17:00:14 -0800221 int64_t result[3];
222 parseFileName(name, result);
223 if (result[0] == -1) continue;
224 int64_t uid = result[1];
225 int64_t configID = result[2];
yro947fbce2017-11-15 22:50:23 -0800226
yro98a28502018-01-18 17:00:14 -0800227 sendBroadcast(ConfigKey((int)uid, configID));
yro947fbce2017-11-15 22:50:23 -0800228 }
229}
230
David Chen48944902018-05-03 10:29:11 -0700231bool StorageManager::hasConfigMetricsReport(const ConfigKey& key) {
232 unique_ptr<DIR, decltype(&closedir)> dir(opendir(STATS_DATA_DIR), closedir);
233 if (dir == NULL) {
234 VLOG("Path %s does not exist", STATS_DATA_DIR);
235 return false;
236 }
237
238 string suffix = StringPrintf("%d_%lld", key.GetUid(), (long long)key.GetId());
239
240 dirent* de;
241 while ((de = readdir(dir.get()))) {
242 char* name = de->d_name;
243 if (name[0] == '.') continue;
244
245 size_t nameLen = strlen(name);
246 size_t suffixLen = suffix.length();
247 if (suffixLen <= nameLen &&
248 strncmp(name + nameLen - suffixLen, suffix.c_str(), suffixLen) == 0) {
249 // Check again that the file name is parseable.
250 int64_t result[3];
251 parseFileName(name, result);
252 if (result[0] == -1) continue;
253 return true;
254 }
255 }
256 return false;
257}
258
Bookatzc71d9012018-12-19 12:28:38 -0800259void StorageManager::appendConfigMetricsReport(const ConfigKey& key,
260 ProtoOutputStream* proto,
261 bool erasa_data) {
yro98a28502018-01-18 17:00:14 -0800262 unique_ptr<DIR, decltype(&closedir)> dir(opendir(STATS_DATA_DIR), closedir);
Yao Chen72506222017-11-25 15:33:09 -0800263 if (dir == NULL) {
yro98a28502018-01-18 17:00:14 -0800264 VLOG("Path %s does not exist", STATS_DATA_DIR);
yro947fbce2017-11-15 22:50:23 -0800265 return;
266 }
267
Yao Chenfef48cb2018-03-21 14:43:14 -0700268 string suffix = StringPrintf("%d_%lld", key.GetUid(), (long long)key.GetId());
yro4beccbe2018-03-15 19:42:05 -0700269
yro947fbce2017-11-15 22:50:23 -0800270 dirent* de;
271 while ((de = readdir(dir.get()))) {
272 char* name = de->d_name;
273 if (name[0] == '.') continue;
yro947fbce2017-11-15 22:50:23 -0800274
yro4beccbe2018-03-15 19:42:05 -0700275 size_t nameLen = strlen(name);
Yao Chenfef48cb2018-03-21 14:43:14 -0700276 size_t suffixLen = suffix.length();
yro4beccbe2018-03-15 19:42:05 -0700277 if (suffixLen <= nameLen &&
Yao Chenfef48cb2018-03-21 14:43:14 -0700278 strncmp(name + nameLen - suffixLen, suffix.c_str(), suffixLen) == 0) {
yro4beccbe2018-03-15 19:42:05 -0700279 int64_t result[3];
280 parseFileName(name, result);
281 if (result[0] == -1) continue;
282 int64_t timestamp = result[0];
283 int64_t uid = result[1];
284 int64_t configID = result[2];
285
286 string file_name = getFilePath(STATS_DATA_DIR, timestamp, uid, configID);
287 int fd = open(file_name.c_str(), O_RDONLY | O_CLOEXEC);
288 if (fd != -1) {
289 string content;
290 if (android::base::ReadFdToString(fd, &content)) {
291 proto->write(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_REPORTS,
292 content.c_str(), content.size());
293 }
294 close(fd);
yro947fbce2017-11-15 22:50:23 -0800295 }
yro947fbce2017-11-15 22:50:23 -0800296
Bookatzc71d9012018-12-19 12:28:38 -0800297 if (erasa_data) {
298 remove(file_name.c_str());
299 }
yro4beccbe2018-03-15 19:42:05 -0700300 }
yro947fbce2017-11-15 22:50:23 -0800301 }
302}
303
Yangster-mac932ecec2018-02-01 10:23:52 -0800304bool StorageManager::readFileToString(const char* file, string* content) {
305 int fd = open(file, O_RDONLY | O_CLOEXEC);
306 bool res = false;
307 if (fd != -1) {
308 if (android::base::ReadFdToString(fd, content)) {
309 res = true;
310 } else {
311 VLOG("Failed to read file %s\n", file);
312 }
313 close(fd);
314 }
315 return res;
316}
317
Yao Chenf09569f2017-12-13 17:00:51 -0800318void StorageManager::readConfigFromDisk(map<ConfigKey, StatsdConfig>& configsMap) {
yro947fbce2017-11-15 22:50:23 -0800319 unique_ptr<DIR, decltype(&closedir)> dir(opendir(STATS_SERVICE_DIR), closedir);
320 if (dir == NULL) {
321 VLOG("no default config on disk");
322 return;
323 }
yro98a28502018-01-18 17:00:14 -0800324 trimToFit(STATS_SERVICE_DIR);
yro947fbce2017-11-15 22:50:23 -0800325
326 dirent* de;
327 while ((de = readdir(dir.get()))) {
328 char* name = de->d_name;
329 if (name[0] == '.') continue;
330 VLOG("file %s", name);
331
yro98a28502018-01-18 17:00:14 -0800332 int64_t result[3];
333 parseFileName(name, result);
334 if (result[0] == -1) continue;
335 int64_t timestamp = result[0];
336 int64_t uid = result[1];
337 int64_t configID = result[2];
338 string file_name = getFilePath(STATS_SERVICE_DIR, timestamp, uid, configID);
yro947fbce2017-11-15 22:50:23 -0800339 int fd = open(file_name.c_str(), O_RDONLY | O_CLOEXEC);
340 if (fd != -1) {
341 string content;
342 if (android::base::ReadFdToString(fd, &content)) {
343 StatsdConfig config;
344 if (config.ParseFromString(content)) {
yro6e304ec2018-01-16 21:00:30 -0800345 configsMap[ConfigKey(uid, configID)] = config;
yro98a28502018-01-18 17:00:14 -0800346 VLOG("map key uid=%lld|configID=%lld", (long long)uid, (long long)configID);
yro947fbce2017-11-15 22:50:23 -0800347 }
348 }
349 close(fd);
350 }
351 }
352}
353
Yangster-macb142cc82018-03-30 15:22:08 -0700354bool StorageManager::readConfigFromDisk(const ConfigKey& key, StatsdConfig* config) {
355 string content;
356 return config != nullptr &&
357 StorageManager::readConfigFromDisk(key, &content) && config->ParseFromString(content);
358}
359
360bool StorageManager::readConfigFromDisk(const ConfigKey& key, string* content) {
yro44907652018-03-12 20:44:05 -0700361 unique_ptr<DIR, decltype(&closedir)> dir(opendir(STATS_SERVICE_DIR),
362 closedir);
363 if (dir == NULL) {
364 VLOG("Directory does not exist: %s", STATS_SERVICE_DIR);
365 return false;
366 }
367
Yao Chenfef48cb2018-03-21 14:43:14 -0700368 string suffix = StringPrintf("%d_%lld", key.GetUid(), (long long)key.GetId());
yro44907652018-03-12 20:44:05 -0700369 dirent* de;
370 while ((de = readdir(dir.get()))) {
371 char* name = de->d_name;
372 if (name[0] == '.') {
373 continue;
374 }
375 size_t nameLen = strlen(name);
Yao Chenfef48cb2018-03-21 14:43:14 -0700376 size_t suffixLen = suffix.length();
yro44907652018-03-12 20:44:05 -0700377 // There can be at most one file that matches this suffix (config key).
378 if (suffixLen <= nameLen &&
Yao Chenfef48cb2018-03-21 14:43:14 -0700379 strncmp(name + nameLen - suffixLen, suffix.c_str(), suffixLen) == 0) {
yro44907652018-03-12 20:44:05 -0700380 int fd = open(StringPrintf("%s/%s", STATS_SERVICE_DIR, name).c_str(),
381 O_RDONLY | O_CLOEXEC);
382 if (fd != -1) {
Yangster-macb142cc82018-03-30 15:22:08 -0700383 if (android::base::ReadFdToString(fd, content)) {
384 return true;
yro44907652018-03-12 20:44:05 -0700385 }
yro4beccbe2018-03-15 19:42:05 -0700386 close(fd);
yro44907652018-03-12 20:44:05 -0700387 }
388 }
389 }
390 return false;
391}
392
Yangster-macb142cc82018-03-30 15:22:08 -0700393bool StorageManager::hasIdenticalConfig(const ConfigKey& key,
394 const vector<uint8_t>& config) {
395 string content;
396 if (StorageManager::readConfigFromDisk(key, &content)) {
397 vector<uint8_t> vec(content.begin(), content.end());
398 if (vec == config) {
399 return true;
400 }
401 }
402 return false;
403}
404
yro98a28502018-01-18 17:00:14 -0800405void StorageManager::trimToFit(const char* path) {
406 unique_ptr<DIR, decltype(&closedir)> dir(opendir(path), closedir);
407 if (dir == NULL) {
408 VLOG("Path %s does not exist", path);
409 return;
410 }
411 dirent* de;
412 int totalFileSize = 0;
413 vector<string> fileNames;
414 while ((de = readdir(dir.get()))) {
415 char* name = de->d_name;
416 if (name[0] == '.') continue;
417
418 int64_t result[3];
419 parseFileName(name, result);
420 if (result[0] == -1) continue;
421 int64_t timestamp = result[0];
422 int64_t uid = result[1];
423 int64_t configID = result[2];
424 string file_name = getFilePath(path, timestamp, uid, configID);
425
426 // Check for timestamp and delete if it's too old.
Yangster-mac330af582018-02-08 15:24:38 -0800427 long fileAge = getWallClockSec() - timestamp;
yro98a28502018-01-18 17:00:14 -0800428 if (fileAge > StatsdStats::kMaxAgeSecond) {
429 deleteFile(file_name.c_str());
430 }
431
432 fileNames.push_back(file_name);
433 ifstream file(file_name.c_str(), ifstream::in | ifstream::binary);
434 if (file.is_open()) {
435 file.seekg(0, ios::end);
436 int fileSize = file.tellg();
437 file.close();
438 totalFileSize += fileSize;
439 }
440 }
441
442 if (fileNames.size() > StatsdStats::kMaxFileNumber ||
443 totalFileSize > StatsdStats::kMaxFileSize) {
444 // Reverse sort to effectively remove from the back (oldest entries).
445 // This will sort files in reverse-chronological order.
446 sort(fileNames.begin(), fileNames.end(), std::greater<std::string>());
447 }
448
449 // Start removing files from oldest to be under the limit.
450 while (fileNames.size() > 0 && (fileNames.size() > StatsdStats::kMaxFileNumber ||
451 totalFileSize > StatsdStats::kMaxFileSize)) {
452 string file_name = fileNames.at(fileNames.size() - 1);
453 ifstream file(file_name.c_str(), ifstream::in | ifstream::binary);
454 if (file.is_open()) {
455 file.seekg(0, ios::end);
456 int fileSize = file.tellg();
457 file.close();
458 totalFileSize -= fileSize;
459 }
460
461 deleteFile(file_name.c_str());
462 fileNames.pop_back();
463 }
464}
465
Yao Chena80e5c02018-09-04 13:55:29 -0700466void StorageManager::printStats(int outFd) {
467 printDirStats(outFd, STATS_SERVICE_DIR);
468 printDirStats(outFd, STATS_DATA_DIR);
yro665208d2018-03-13 18:08:09 -0700469}
470
Yao Chena80e5c02018-09-04 13:55:29 -0700471void StorageManager::printDirStats(int outFd, const char* path) {
472 dprintf(outFd, "Printing stats of %s\n", path);
yro665208d2018-03-13 18:08:09 -0700473 unique_ptr<DIR, decltype(&closedir)> dir(opendir(path), closedir);
474 if (dir == NULL) {
475 VLOG("Path %s does not exist", path);
476 return;
477 }
478 dirent* de;
479 int fileCount = 0;
480 int totalFileSize = 0;
481 while ((de = readdir(dir.get()))) {
482 char* name = de->d_name;
483 if (name[0] == '.') {
484 continue;
485 }
486 int64_t result[3];
487 parseFileName(name, result);
488 if (result[0] == -1) continue;
489 int64_t timestamp = result[0];
490 int64_t uid = result[1];
491 int64_t configID = result[2];
Yao Chena80e5c02018-09-04 13:55:29 -0700492 dprintf(outFd, "\t #%d, Last updated: %lld, UID: %d, Config ID: %lld", fileCount + 1,
493 (long long)timestamp, (int)uid, (long long)configID);
yro665208d2018-03-13 18:08:09 -0700494 string file_name = getFilePath(path, timestamp, uid, configID);
495 ifstream file(file_name.c_str(), ifstream::in | ifstream::binary);
496 if (file.is_open()) {
497 file.seekg(0, ios::end);
498 int fileSize = file.tellg();
499 file.close();
Yao Chena80e5c02018-09-04 13:55:29 -0700500 dprintf(outFd, ", File Size: %d bytes", fileSize);
yro665208d2018-03-13 18:08:09 -0700501 totalFileSize += fileSize;
502 }
Yao Chena80e5c02018-09-04 13:55:29 -0700503 dprintf(outFd, "\n");
yro665208d2018-03-13 18:08:09 -0700504 fileCount++;
505 }
Yao Chena80e5c02018-09-04 13:55:29 -0700506 dprintf(outFd, "\tTotal number of files: %d, Total size of files: %d bytes.\n", fileCount,
507 totalFileSize);
yro665208d2018-03-13 18:08:09 -0700508}
509
yro947fbce2017-11-15 22:50:23 -0800510} // namespace statsd
511} // namespace os
512} // namespace android