Add support for multi train logging

Test: atest GtsStatsdHostTestCases
Change-Id: I4ca7089347d6dfab8a33c07acd5ef9c252413ec9
This commit is contained in:
Jonathan Nguyen
2020-02-04 15:54:26 -08:00
committed by Muhammad Qureshi
parent 2305ccf398
commit 703c42f695
13 changed files with 333 additions and 243 deletions

View File

@@ -221,12 +221,6 @@ interface IStatsd {
*/
const int FLAG_REQUIRE_LOW_LATENCY_MONITOR = 0x04;
/**
* Logs an event for watchdog rollbacks.
*/
oneway void sendWatchdogRollbackOccurredAtom(in int rollbackType, in String packageName,
in long packageVersionCode, in int rollbackReason, in String failingPackageName);
/**
* Returns the most recently registered experiment IDs.
*/

View File

@@ -171,52 +171,11 @@ public final class StatsLog {
state,
proto.getBytes(),
0,
0);
0,
false);
return true;
}
/**
* Logs an event for watchdog rollbacks.
*
* @param rollbackType state of the rollback.
* @param packageName package name being rolled back.
* @param packageVersionCode version of the package being rolled back.
* @param rollbackReason reason the package is being rolled back.
* @param failingPackageName the package name causing the failure.
*
* @return True if the log request was sent to statsd.
*
* @hide
*/
@RequiresPermission(allOf = {DUMP, PACKAGE_USAGE_STATS})
public static boolean logWatchdogRollbackOccurred(int rollbackType, String packageName,
long packageVersionCode, int rollbackReason, String failingPackageName) {
synchronized (sLogLock) {
try {
IStatsd service = getIStatsdLocked();
if (service == null) {
if (DEBUG) {
Slog.d(TAG, "Failed to find statsd when logging event");
}
return false;
}
service.sendWatchdogRollbackOccurredAtom(rollbackType, packageName,
packageVersionCode, rollbackReason, failingPackageName);
return true;
} catch (RemoteException e) {
sService = null;
if (DEBUG) {
Slog.d(TAG,
"Failed to connect to StatsCompanionService when logging "
+ "WatchdogRollbackOccurred");
}
return false;
}
}
}
private static IStatsd getIStatsdLocked() throws RemoteException {
if (sService != null) {
return sService;

View File

@@ -196,17 +196,27 @@ void StatsLogProcessor::onBinaryPushStateChangedEventLocked(LogEvent* event) {
!checkPermissionForIds(kPermissionUsage, pid, uid)) {
return;
}
status_t err = NO_ERROR, err2 = NO_ERROR, err3 = NO_ERROR, err4 = NO_ERROR;
string trainName = string(event->GetString(1 /*train name field id*/, &err));
int64_t trainVersionCode = event->GetLong(2 /*train version field id*/, &err2);
int32_t state = int32_t(event->GetLong(6 /*state field id*/, &err3));
// The Get* functions don't modify the status on success, they only write in
// failure statuses, so we can use one status variable for all calls then
// check if it is no longer NO_ERROR.
status_t err = NO_ERROR;
InstallTrainInfo trainInfo;
trainInfo.trainName = string(event->GetString(1 /*train name field id*/, &err));
trainInfo.trainVersionCode = event->GetLong(2 /*train version field id*/, &err);
trainInfo.requiresStaging = event->GetBool(3 /*requires staging field id*/, &err);
trainInfo.rollbackEnabled = event->GetBool(4 /*rollback enabled field id*/, &err);
trainInfo.requiresLowLatencyMonitor =
event->GetBool(5 /*requires low latency monitor field id*/, &err);
trainInfo.status = int32_t(event->GetLong(6 /*state field id*/, &err));
#ifdef NEW_ENCODING_SCHEME
std::vector<uint8_t> trainExperimentIdBytes =
event->GetStorage(7 /*experiment ids field id*/, &err4);
event->GetStorage(7 /*experiment ids field id*/, &err);
#else
string trainExperimentIdString = event->GetString(7 /*experiment ids field id*/, &err4);
string trainExperimentIdString = event->GetString(7 /*experiment ids field id*/, &err);
#endif
if (err != NO_ERROR || err2 != NO_ERROR || err3 != NO_ERROR || err4 != NO_ERROR) {
bool is_rollback = event->GetBool(10 /*is rollback field id*/, &err);
if (err != NO_ERROR) {
ALOGE("Failed to parse fields in binary push state changed log event");
return;
}
@@ -220,83 +230,154 @@ void StatsLogProcessor::onBinaryPushStateChangedEventLocked(LogEvent* event) {
ALOGE("Failed to parse experimentids in binary push state changed.");
return;
}
vector<int64_t> experimentIdVector = {trainExperimentIds.experiment_id().begin(),
trainExperimentIds.experiment_id().end()};
trainInfo.experimentIds = {trainExperimentIds.experiment_id().begin(),
trainExperimentIds.experiment_id().end()};
// Update the train info on disk and get any data the logevent is missing.
getAndUpdateTrainInfoOnDisk(
state, &trainVersionCode, &trainName, &experimentIdVector);
getAndUpdateTrainInfoOnDisk(is_rollback, &trainInfo);
std::vector<uint8_t> trainExperimentIdProto;
writeExperimentIdsToProto(experimentIdVector, &trainExperimentIdProto);
writeExperimentIdsToProto(trainInfo.experimentIds, &trainExperimentIdProto);
int32_t userId = multiuser_get_user_id(uid);
event->updateValue(1 /*train name field id*/, trainName, STRING);
event->updateValue(2 /*train version field id*/, trainVersionCode, LONG);
event->updateValue(2 /*train version field id*/, trainInfo.trainVersionCode, LONG);
#ifdef NEW_ENCODING_SCHEME
event->updateValue(7 /*experiment ids field id*/, trainExperimentIdProto, STORAGE);
#else
event->updateValue(7 /*experiment ids field id*/, trainExperimentIdProto, STRING);
#endif
event->updateValue(8 /*user id field id*/, userId, INT);
if (is_rollback) {
int bit = trainInfo.requiresStaging ? 1 : 0;
event->updateValue(3 /*requires staging field id*/, bit, INT);
bit = trainInfo.rollbackEnabled ? 1 : 0;
event->updateValue(4 /*rollback enabled field id*/, bit, INT);
bit = trainInfo.requiresLowLatencyMonitor ? 1 : 0;
event->updateValue(5 /*requires low latency monitor field id*/, bit, INT);
}
}
void StatsLogProcessor::getAndUpdateTrainInfoOnDisk(int32_t state,
int64_t* trainVersionCode,
string* trainName,
std::vector<int64_t>* experimentIds) {
void StatsLogProcessor::getAndUpdateTrainInfoOnDisk(bool is_rollback,
InstallTrainInfo* trainInfo) {
// If the train name is empty, we don't know which train to attribute the
// event to, so return early.
if (trainInfo->trainName.empty()) {
return;
}
bool readTrainInfoSuccess = false;
InstallTrainInfo trainInfoOnDisk;
readTrainInfoSuccess = StorageManager::readTrainInfo(trainInfoOnDisk);
readTrainInfoSuccess = StorageManager::readTrainInfo(trainInfo->trainName, trainInfoOnDisk);
bool resetExperimentIds = false;
if (readTrainInfoSuccess) {
// Keep the old train version if we received an empty version.
if (*trainVersionCode == -1) {
*trainVersionCode = trainInfoOnDisk.trainVersionCode;
} else if (*trainVersionCode != trainInfoOnDisk.trainVersionCode) {
// Reset experiment ids if we receive a new non-empty train version.
resetExperimentIds = true;
}
// Keep the old train name if we received an empty train name.
if (trainName->size() == 0) {
*trainName = trainInfoOnDisk.trainName;
} else if (*trainName != trainInfoOnDisk.trainName) {
// Reset experiment ids if we received a new valid train name.
if (trainInfo->trainVersionCode == -1) {
trainInfo->trainVersionCode = trainInfoOnDisk.trainVersionCode;
} else if (trainInfo->trainVersionCode != trainInfoOnDisk.trainVersionCode) {
// Reset experiment ids if we receive a new non-empty train version.
resetExperimentIds = true;
}
// Reset if we received a different experiment id.
if (!experimentIds->empty() &&
(trainInfoOnDisk.experimentIds.empty() ||
experimentIds->at(0) != trainInfoOnDisk.experimentIds[0])) {
if (!trainInfo->experimentIds.empty() &&
(trainInfoOnDisk.experimentIds.empty() ||
trainInfo->experimentIds.at(0) != trainInfoOnDisk.experimentIds[0])) {
resetExperimentIds = true;
}
}
// Find the right experiment IDs
if (!resetExperimentIds && readTrainInfoSuccess) {
*experimentIds = trainInfoOnDisk.experimentIds;
if ((!resetExperimentIds || is_rollback) && readTrainInfoSuccess) {
trainInfo->experimentIds = trainInfoOnDisk.experimentIds;
}
if (!experimentIds->empty()) {
int64_t firstId = experimentIds->at(0);
switch (state) {
if (!trainInfo->experimentIds.empty()) {
int64_t firstId = trainInfo->experimentIds.at(0);
switch (trainInfo->status) {
case android::util::BINARY_PUSH_STATE_CHANGED__STATE__INSTALL_SUCCESS:
experimentIds->push_back(firstId + 1);
trainInfo->experimentIds.push_back(firstId + 1);
break;
case android::util::BINARY_PUSH_STATE_CHANGED__STATE__INSTALLER_ROLLBACK_INITIATED:
experimentIds->push_back(firstId + 2);
trainInfo->experimentIds.push_back(firstId + 2);
break;
case android::util::BINARY_PUSH_STATE_CHANGED__STATE__INSTALLER_ROLLBACK_SUCCESS:
experimentIds->push_back(firstId + 3);
trainInfo->experimentIds.push_back(firstId + 3);
break;
}
}
StorageManager::writeTrainInfo(*trainVersionCode, *trainName, state, *experimentIds);
if (is_rollback) {
trainInfo->requiresStaging = trainInfoOnDisk.requiresStaging;
trainInfo->rollbackEnabled = trainInfoOnDisk.rollbackEnabled;
trainInfo->requiresLowLatencyMonitor = trainInfoOnDisk.requiresLowLatencyMonitor;
}
StorageManager::writeTrainInfo(*trainInfo);
}
void StatsLogProcessor::onWatchdogRollbackOccurredLocked(LogEvent* event) {
pid_t pid = event->GetPid();
uid_t uid = event->GetUid();
if (!checkPermissionForIds(kPermissionDump, pid, uid) ||
!checkPermissionForIds(kPermissionUsage, pid, uid)) {
return;
}
// The Get* functions don't modify the status on success, they only write in
// failure statuses, so we can use one status variable for all calls then
// check if it is no longer NO_ERROR.
status_t err = NO_ERROR;
int32_t rollbackType = int32_t(event->GetInt(1 /*rollback type field id*/, &err));
string packageName = string(event->GetString(2 /*package name field id*/, &err));
if (err != NO_ERROR) {
ALOGE("Failed to parse fields in watchdog rollback occurred log event");
return;
}
vector<int64_t> experimentIds =
processWatchdogRollbackOccurred(rollbackType, packageName);
vector<uint8_t> experimentIdProto;
writeExperimentIdsToProto(experimentIds, &experimentIdProto);
#ifdef NEW_ENCODING_SCHEME
event->updateValue(6 /*experiment ids field id*/, experimentIdProto, STORAGE);
#else
event->updateValue(6 /*experiment ids field id*/, experimentIdProto, STRING);
#endif
}
vector<int64_t> StatsLogProcessor::processWatchdogRollbackOccurred(const int32_t rollbackTypeIn,
const string& packageNameIn) {
// If the package name is empty, we can't attribute it to any train, so
// return early.
if (packageNameIn.empty()) {
return vector<int64_t>();
}
bool readTrainInfoSuccess = false;
InstallTrainInfo trainInfoOnDisk;
readTrainInfoSuccess = StorageManager::readTrainInfo(packageNameIn, trainInfoOnDisk);
if (!readTrainInfoSuccess) {
return vector<int64_t>();
}
if (trainInfoOnDisk.experimentIds.empty()) {
return vector<int64_t>();
}
switch (rollbackTypeIn) {
case android::util::WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_INITIATE:
trainInfoOnDisk.experimentIds.push_back(trainInfoOnDisk.experimentIds[0] + 4);
StorageManager::writeTrainInfo(trainInfoOnDisk);
break;
case android::util::WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_SUCCESS:
trainInfoOnDisk.experimentIds.push_back(trainInfoOnDisk.experimentIds[0] + 5);
StorageManager::writeTrainInfo(trainInfoOnDisk);
break;
}
return trainInfoOnDisk.experimentIds;
}
void StatsLogProcessor::resetConfigs() {
std::lock_guard<std::mutex> lock(mMetricsMutex);
@@ -321,7 +402,13 @@ void StatsLogProcessor::OnLogEvent(LogEvent* event, int64_t elapsedRealtimeNs) {
// Hard-coded logic to update train info on disk and fill in any information
// this log event may be missing.
if (event->GetTagId() == android::util::BINARY_PUSH_STATE_CHANGED) {
onBinaryPushStateChangedEventLocked(event);
onBinaryPushStateChangedEventLocked(event);
}
// Hard-coded logic to update experiment ids on disk for certain rollback
// types and fill the rollback atom with experiment ids
if (event->GetTagId() == android::util::WATCHDOG_ROLLBACK_OCCURRED) {
onWatchdogRollbackOccurredLocked(event);
}
#ifdef VERY_VERBOSE_PRINTING

View File

@@ -18,6 +18,7 @@
#include <gtest/gtest_prod.h>
#include "config/ConfigListener.h"
#include "logd/LogEvent.h"
#include "metrics/MetricsManager.h"
#include "packages/UidMap.h"
#include "external/StatsPullerManager.h"
@@ -199,10 +200,18 @@ private:
// Handler over the binary push state changed event.
void onBinaryPushStateChangedEventLocked(LogEvent* event);
// Handler over the watchdog rollback occurred event.
void onWatchdogRollbackOccurredLocked(LogEvent* event);
// Updates train info on disk based on binary push state changed info and
// write disk info into parameters.
void getAndUpdateTrainInfoOnDisk(int32_t state, int64_t* trainVersionCode,
string* trainName, std::vector<int64_t>* experimentIds);
void getAndUpdateTrainInfoOnDisk(bool is_rollback, InstallTrainInfo* trainInfoIn);
// Gets experiment ids on disk for associated train and updates them
// depending on rollback type. Then writes them back to disk and returns
// them.
std::vector<int64_t> processWatchdogRollbackOccurred(const int32_t rollbackTypeIn,
const string& packageName);
// Reset all configs.
void resetConfigsLocked(const int64_t timestampNs);

View File

@@ -1293,81 +1293,24 @@ Status StatsService::unregisterNativePullAtomCallback(int32_t atomTag) {
return Status::ok();
}
Status StatsService::sendWatchdogRollbackOccurredAtom(const int32_t rollbackTypeIn,
const android::String16& packageNameIn,
const int64_t packageVersionCodeIn,
const int32_t rollbackReasonIn,
const android::String16&
failingPackageNameIn) {
// Note: We skip the usage stats op check here since we do not have a package name.
// This is ok since we are overloading the usage_stats permission.
// This method only sends data, it does not receive it.
pid_t pid = IPCThreadState::self()->getCallingPid();
uid_t uid = IPCThreadState::self()->getCallingUid();
// Root, system, and shell always have access
if (uid != AID_ROOT && uid != AID_SYSTEM && uid != AID_SHELL) {
// Caller must be granted these permissions
if (!checkPermission(kPermissionDump)) {
return exception(binder::Status::EX_SECURITY,
StringPrintf("UID %d / PID %d lacks permission %s", uid, pid,
kPermissionDump));
}
if (!checkPermission(kPermissionUsage)) {
return exception(binder::Status::EX_SECURITY,
StringPrintf("UID %d / PID %d lacks permission %s", uid, pid,
kPermissionUsage));
}
}
android::util::stats_write(android::util::WATCHDOG_ROLLBACK_OCCURRED,
rollbackTypeIn, String8(packageNameIn).string(), packageVersionCodeIn,
rollbackReasonIn, String8(failingPackageNameIn).string());
// Fast return to save disk read.
if (rollbackTypeIn != android::util::WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_SUCCESS
&& rollbackTypeIn !=
android::util::WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_INITIATE) {
return Status::ok();
}
bool readTrainInfoSuccess = false;
InstallTrainInfo trainInfoOnDisk;
readTrainInfoSuccess = StorageManager::readTrainInfo(trainInfoOnDisk);
if (!readTrainInfoSuccess) {
return Status::ok();
}
std::vector<int64_t> experimentIds = trainInfoOnDisk.experimentIds;
if (experimentIds.empty()) {
return Status::ok();
}
switch (rollbackTypeIn) {
case android::util::WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_INITIATE:
experimentIds.push_back(experimentIds[0] + 4);
break;
case android::util::WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_SUCCESS:
experimentIds.push_back(experimentIds[0] + 5);
break;
}
StorageManager::writeTrainInfo(trainInfoOnDisk.trainVersionCode, trainInfoOnDisk.trainName,
trainInfoOnDisk.status, experimentIds);
return Status::ok();
}
Status StatsService::getRegisteredExperimentIds(std::vector<int64_t>* experimentIdsOut) {
ENFORCE_UID(AID_SYSTEM);
// TODO: add verifier permission
experimentIdsOut->clear();
// Read the latest train info
InstallTrainInfo trainInfo;
if (!StorageManager::readTrainInfo(trainInfo)) {
vector<InstallTrainInfo> trainInfoList = StorageManager::readAllTrainInfo();
if (trainInfoList.empty()) {
// No train info means no experiment IDs, return an empty list
experimentIdsOut->clear();
return Status::ok();
}
// Copy the experiment IDs to the out vector
experimentIdsOut->assign(trainInfo.experimentIds.begin(), trainInfo.experimentIds.end());
for (InstallTrainInfo& trainInfo : trainInfoList) {
experimentIdsOut->insert(experimentIdsOut->end(),
trainInfo.experimentIds.begin(),
trainInfo.experimentIds.end());
}
return Status::ok();
}

View File

@@ -186,16 +186,6 @@ public:
*/
virtual Status unregisterNativePullAtomCallback(int32_t atomTag) override;
/**
* Binder call to log WatchdogRollbackOccurred atom.
*/
virtual Status sendWatchdogRollbackOccurredAtom(
const int32_t rollbackTypeIn,
const android::String16& packageNameIn,
const int64_t packageVersionCodeIn,
const int32_t rollbackReasonIn,
const android::String16& failingPackageNameIn) override;
/**
* Binder call to get registered experiment IDs.
*/

View File

@@ -1874,6 +1874,8 @@ message WatchdogRollbackOccurred {
// Set by RollbackPackageHealthObserver to be the package that is failing when a rollback
// is initiated. Empty if the package is unknown.
optional string failing_package_name = 5;
optional TrainExperimentIds experiment_ids = 6 [(log_mode) = MODE_BYTES];
}
/**
@@ -3770,6 +3772,8 @@ message BinaryPushStateChanged {
// user id
optional int32 user_id = 8;
optional int32 reason = 9;
// Whether or not this is a rollback event
optional bool is_rollback = 10;
}
/* Test atom, is not logged anywhere */

View File

@@ -37,14 +37,16 @@ TrainInfoPuller::TrainInfoPuller() :
}
bool TrainInfoPuller::PullInternal(vector<shared_ptr<LogEvent>>* data) {
InstallTrainInfo trainInfo;
bool ret = StorageManager::readTrainInfo(trainInfo);
if (!ret) {
ALOGW("Failed to read train info.");
return false;
vector<InstallTrainInfo> trainInfoList =
StorageManager::readAllTrainInfo();
if (trainInfoList.empty()) {
ALOGW("Train info was empty.");
return true;
}
for (InstallTrainInfo& trainInfo : trainInfoList) {
auto event = make_shared<LogEvent>(getWallClockNs(), getElapsedRealtimeNs(), trainInfo);
data->push_back(event);
}
auto event = make_shared<LogEvent>(getWallClockNs(), getElapsedRealtimeNs(), trainInfo);
data->push_back(event);
return true;
}

View File

@@ -56,6 +56,9 @@ struct InstallTrainInfo {
std::string trainName;
int32_t status;
std::vector<int64_t> experimentIds;
bool requiresStaging;
bool rollbackEnabled;
bool requiresLowLatencyMonitor;
};
/**

View File

@@ -44,7 +44,7 @@ using std::map;
#define TRAIN_INFO_PATH "/data/misc/train-info/train-info.bin"
// Magic word at the start of the train info file, change this if changing the file format
const uint32_t TRAIN_INFO_FILE_MAGIC = 0xff7447ff;
const uint32_t TRAIN_INFO_FILE_MAGIC = 0xfb7447bf;
// for ConfigMetricsReportList
const int FIELD_ID_REPORTS = 2;
@@ -75,6 +75,29 @@ string StorageManager::getDataHistoryFileName(long wallClockSec, int uid, int64_
(long long)id);
}
static const char* findTrainInfoFileNameLocked(const string& trainName) {
unique_ptr<DIR, decltype(&closedir)> dir(opendir(TRAIN_INFO_DIR), closedir);
if (dir == NULL) {
VLOG("Path %s does not exist", TRAIN_INFO_DIR);
return nullptr;
}
dirent* de;
while ((de = readdir(dir.get()))) {
char* fileName = de->d_name;
if (fileName[0] == '.') continue;
size_t fileNameLength = strlen(fileName);
if (fileNameLength >= trainName.length()) {
if (0 == strncmp(fileName + fileNameLength - trainName.length(), trainName.c_str(),
trainName.length())) {
return fileName;
}
}
}
return nullptr;
}
// Returns array of int64_t which contains timestamp in seconds, uid,
// configID and whether the file is a local history file.
static void parseFileName(char* name, FileName* output) {
@@ -123,20 +146,25 @@ void StorageManager::writeFile(const char* file, const void* buffer, int numByte
close(fd);
}
bool StorageManager::writeTrainInfo(int64_t trainVersionCode, const std::string& trainName,
int32_t status, const std::vector<int64_t>& experimentIds) {
bool StorageManager::writeTrainInfo(const InstallTrainInfo& trainInfo) {
std::lock_guard<std::mutex> lock(sTrainInfoMutex);
deleteAllFiles(TRAIN_INFO_DIR);
if (trainInfo.trainName.empty()) {
return false;
}
deleteSuffixedFiles(TRAIN_INFO_DIR, trainInfo.trainName.c_str());
int fd = open(TRAIN_INFO_PATH, O_WRONLY | O_CREAT | O_CLOEXEC, S_IRUSR | S_IWUSR);
std::string fileName =
StringPrintf("%s/%ld_%s", TRAIN_INFO_DIR, (long) getWallClockSec(),
trainInfo.trainName.c_str());
int fd = open(fileName.c_str(), O_WRONLY | O_CREAT | O_CLOEXEC, S_IRUSR | S_IWUSR);
if (fd == -1) {
VLOG("Attempt to access %s but failed", TRAIN_INFO_PATH);
VLOG("Attempt to access %s but failed", fileName.c_str());
return false;
}
size_t result;
// Write the magic word
result = write(fd, &TRAIN_INFO_FILE_MAGIC, sizeof(TRAIN_INFO_FILE_MAGIC));
if (result != sizeof(TRAIN_INFO_FILE_MAGIC)) {
@@ -146,8 +174,8 @@ bool StorageManager::writeTrainInfo(int64_t trainVersionCode, const std::string&
}
// Write the train version
const size_t trainVersionCodeByteCount = sizeof(trainVersionCode);
result = write(fd, &trainVersionCode, trainVersionCodeByteCount);
const size_t trainVersionCodeByteCount = sizeof(trainInfo.trainVersionCode);
result = write(fd, &trainInfo.trainVersionCode, trainVersionCodeByteCount);
if (result != trainVersionCodeByteCount) {
VLOG("Failed to wrtie train version code");
close(fd);
@@ -155,7 +183,7 @@ bool StorageManager::writeTrainInfo(int64_t trainVersionCode, const std::string&
}
// Write # of bytes in trainName to file
const size_t trainNameSize = trainName.size();
const size_t trainNameSize = trainInfo.trainName.size();
const size_t trainNameSizeByteCount = sizeof(trainNameSize);
result = write(fd, (uint8_t*)&trainNameSize, trainNameSizeByteCount);
if (result != trainNameSizeByteCount) {
@@ -165,7 +193,7 @@ bool StorageManager::writeTrainInfo(int64_t trainVersionCode, const std::string&
}
// Write trainName to file
result = write(fd, trainName.c_str(), trainNameSize);
result = write(fd, trainInfo.trainName.c_str(), trainNameSize);
if (result != trainNameSize) {
VLOG("Failed to write train name");
close(fd);
@@ -173,8 +201,8 @@ bool StorageManager::writeTrainInfo(int64_t trainVersionCode, const std::string&
}
// Write status to file
const size_t statusByteCount = sizeof(status);
result = write(fd, (uint8_t*)&status, statusByteCount);
const size_t statusByteCount = sizeof(trainInfo.status);
result = write(fd, (uint8_t*)&trainInfo.status, statusByteCount);
if (result != statusByteCount) {
VLOG("Failed to write status");
close(fd);
@@ -182,7 +210,7 @@ bool StorageManager::writeTrainInfo(int64_t trainVersionCode, const std::string&
}
// Write experiment id count to file.
const size_t experimentIdsCount = experimentIds.size();
const size_t experimentIdsCount = trainInfo.experimentIds.size();
const size_t experimentIdsCountByteCount = sizeof(experimentIdsCount);
result = write(fd, (uint8_t*) &experimentIdsCount, experimentIdsCountByteCount);
if (result != experimentIdsCountByteCount) {
@@ -193,7 +221,7 @@ bool StorageManager::writeTrainInfo(int64_t trainVersionCode, const std::string&
// Write experimentIds to file
for (size_t i = 0; i < experimentIdsCount; i++) {
const int64_t experimentId = experimentIds[i];
const int64_t experimentId = trainInfo.experimentIds[i];
const size_t experimentIdByteCount = sizeof(experimentId);
result = write(fd, &experimentId, experimentIdByteCount);
if (result == experimentIdByteCount) {
@@ -205,23 +233,47 @@ bool StorageManager::writeTrainInfo(int64_t trainVersionCode, const std::string&
}
}
result = fchown(fd, AID_STATSD, AID_STATSD);
if (result) {
VLOG("Failed to chown train info file to statsd");
close(fd);
return false;
// Write bools to file
const size_t boolByteCount = sizeof(trainInfo.requiresStaging);
result = write(fd, (uint8_t*)&trainInfo.requiresStaging, boolByteCount);
if (result != boolByteCount) {
VLOG("Failed to write requires staging");
close(fd);
return false;
}
result = write(fd, (uint8_t*)&trainInfo.rollbackEnabled, boolByteCount);
if (result != boolByteCount) {
VLOG("Failed to write rollback enabled");
close(fd);
return false;
}
result = write(fd, (uint8_t*)&trainInfo.requiresLowLatencyMonitor, boolByteCount);
if (result != boolByteCount) {
VLOG("Failed to write requires log latency monitor");
close(fd);
return false;
}
close(fd);
return true;
}
bool StorageManager::readTrainInfo(InstallTrainInfo& trainInfo) {
bool StorageManager::readTrainInfo(const std::string& trainName, InstallTrainInfo& trainInfo) {
std::lock_guard<std::mutex> lock(sTrainInfoMutex);
return readTrainInfoLocked(trainName, trainInfo);
}
int fd = open(TRAIN_INFO_PATH, O_RDONLY | O_CLOEXEC);
bool StorageManager::readTrainInfoLocked(const std::string& trainName, InstallTrainInfo& trainInfo) {
trimToFit(TRAIN_INFO_DIR, /*parseTimestampOnly=*/ true);
const char* fileName = findTrainInfoFileNameLocked(trainName);
if (fileName == nullptr) {
return false;
}
int fd = open(StringPrintf("%s/%s", TRAIN_INFO_DIR, fileName).c_str(), O_RDONLY | O_CLOEXEC);
if (fd == -1) {
VLOG("Failed to open train-info.bin");
VLOG("Failed to open %s", fileName);
return false;
}
@@ -297,6 +349,29 @@ bool StorageManager::readTrainInfo(InstallTrainInfo& trainInfo) {
trainInfo.experimentIds.push_back(experimentId);
}
// Read bools
const size_t boolByteCount = sizeof(trainInfo.requiresStaging);
result = read(fd, &trainInfo.requiresStaging, boolByteCount);
if (result != boolByteCount) {
VLOG("Failed to read requires requires staging from train info file");
close(fd);
return false;
}
result = read(fd, &trainInfo.rollbackEnabled, boolByteCount);
if (result != boolByteCount) {
VLOG("Failed to read requires rollback enabled from train info file");
close(fd);
return false;
}
result = read(fd, &trainInfo.requiresLowLatencyMonitor, boolByteCount);
if (result != boolByteCount) {
VLOG("Failed to read requires requires low latency monitor from train info file");
close(fd);
return false;
}
// Expect to be at EOF.
char c;
result = read(fd, &c, 1);
@@ -311,6 +386,32 @@ bool StorageManager::readTrainInfo(InstallTrainInfo& trainInfo) {
return true;
}
vector<InstallTrainInfo> StorageManager::readAllTrainInfo() {
std::lock_guard<std::mutex> lock(sTrainInfoMutex);
vector<InstallTrainInfo> trainInfoList;
unique_ptr<DIR, decltype(&closedir)> dir(opendir(TRAIN_INFO_DIR), closedir);
if (dir == NULL) {
VLOG("Directory does not exist: %s", TRAIN_INFO_DIR);
return trainInfoList;
}
dirent* de;
while ((de = readdir(dir.get()))) {
char* name = de->d_name;
if (name[0] == '.') {
continue;
}
InstallTrainInfo trainInfo;
bool readSuccess = StorageManager::readTrainInfoLocked(name, trainInfo);
if (!readSuccess) {
continue;
}
trainInfoList.push_back(trainInfo);
}
return trainInfoList;
}
void StorageManager::deleteFile(const char* file) {
if (remove(file) != 0) {
VLOG("Attempt to delete %s but is not found", file);
@@ -574,7 +675,7 @@ void StorageManager::sortFiles(vector<FileInfo>* fileNames) {
});
}
void StorageManager::trimToFit(const char* path) {
void StorageManager::trimToFit(const char* path, bool parseTimestampOnly) {
unique_ptr<DIR, decltype(&closedir)> dir(opendir(path), closedir);
if (dir == NULL) {
VLOG("Path %s does not exist", path);
@@ -589,7 +690,12 @@ void StorageManager::trimToFit(const char* path) {
if (name[0] == '.') continue;
FileName output;
parseFileName(name, &output);
if (parseTimestampOnly) {
output.mTimestampSec = StrToInt64(strtok(name, "_"));
output.mIsHistory = false;
} else {
parseFileName(name, &output);
}
if (output.mTimestampSec == -1) continue;
string file_name = output.getFullFileName(path);

View File

@@ -52,13 +52,22 @@ public:
/**
* Writes train info.
*/
static bool writeTrainInfo(int64_t trainVersionCode, const std::string& trainName,
int32_t status, const std::vector<int64_t>& experimentIds);
static bool writeTrainInfo(const InstallTrainInfo& trainInfo);
/**
* Reads train info.
*/
static bool readTrainInfo(InstallTrainInfo& trainInfo);
static bool readTrainInfo(const std::string& trainName, InstallTrainInfo& trainInfo);
/**
* Reads train info assuming lock is obtained.
*/
static bool readTrainInfoLocked(const std::string& trainName, InstallTrainInfo& trainInfo);
/**
* Reads all train info and returns a vector of train info.
*/
static vector<InstallTrainInfo> readAllTrainInfo();
/**
* Reads the file content to the buffer.
@@ -124,7 +133,7 @@ public:
* Trims files in the provided directory to limit the total size, number of
* files, accumulation of outdated files.
*/
static void trimToFit(const char* dir);
static void trimToFit(const char* dir, bool parseTimestampOnly = false);
/**
* Returns true if there already exists identical configuration on device.

View File

@@ -40,40 +40,12 @@ TEST(StorageManagerTest, TrainInfoReadWriteTest) {
bool result;
result = StorageManager::writeTrainInfo(trainInfo.trainVersionCode, trainInfo.trainName,
trainInfo.status, trainInfo.experimentIds);
result = StorageManager::writeTrainInfo(trainInfo);
EXPECT_TRUE(result);
InstallTrainInfo trainInfoResult;
result = StorageManager::readTrainInfo(trainInfoResult);
EXPECT_TRUE(result);
EXPECT_EQ(trainInfo.trainVersionCode, trainInfoResult.trainVersionCode);
EXPECT_EQ(trainInfo.trainName.size(), trainInfoResult.trainName.size());
EXPECT_EQ(trainInfo.trainName, trainInfoResult.trainName);
EXPECT_EQ(trainInfo.status, trainInfoResult.status);
EXPECT_EQ(trainInfo.experimentIds.size(), trainInfoResult.experimentIds.size());
EXPECT_EQ(trainInfo.experimentIds, trainInfoResult.experimentIds);
}
TEST(StorageManagerTest, TrainInfoReadWriteEmptyTrainNameTest) {
InstallTrainInfo trainInfo;
trainInfo.trainVersionCode = 12345;
trainInfo.trainName = "";
trainInfo.status = 1;
const char* expIds = "test_ids";
trainInfo.experimentIds.assign(expIds, expIds + strlen(expIds));
bool result;
result = StorageManager::writeTrainInfo(trainInfo.trainVersionCode, trainInfo.trainName,
trainInfo.status, trainInfo.experimentIds);
EXPECT_TRUE(result);
InstallTrainInfo trainInfoResult;
result = StorageManager::readTrainInfo(trainInfoResult);
result = StorageManager::readTrainInfo(trainInfo.trainName, trainInfoResult);
EXPECT_TRUE(result);
EXPECT_EQ(trainInfo.trainVersionCode, trainInfoResult.trainVersionCode);
@@ -94,13 +66,12 @@ TEST(StorageManagerTest, TrainInfoReadWriteTrainNameSizeOneTest) {
bool result;
result = StorageManager::writeTrainInfo(trainInfo.trainVersionCode, trainInfo.trainName,
trainInfo.status, trainInfo.experimentIds);
result = StorageManager::writeTrainInfo(trainInfo);
EXPECT_TRUE(result);
InstallTrainInfo trainInfoResult;
result = StorageManager::readTrainInfo(trainInfoResult);
result = StorageManager::readTrainInfo(trainInfo.trainName, trainInfoResult);
EXPECT_TRUE(result);
EXPECT_EQ(trainInfo.trainVersionCode, trainInfoResult.trainVersionCode);

View File

@@ -38,9 +38,9 @@ import android.content.rollback.PackageRollbackInfo;
import android.content.rollback.RollbackInfo;
import android.util.ArraySet;
import android.util.Slog;
import android.util.StatsLog;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.FrameworkStatsLog;
import com.android.server.PackageWatchdog;
import java.util.ArrayList;
@@ -206,12 +206,25 @@ public final class WatchdogRollbackLogger {
+ " rollbackReason: " + rollbackReasonToString(rollbackReason)
+ " failedPackageName: " + failingPackageName);
if (logPackage != null) {
StatsLog.logWatchdogRollbackOccurred(type, logPackage.getPackageName(),
logPackage.getVersionCode(), rollbackReason, failingPackageName);
FrameworkStatsLog.write(
FrameworkStatsLog.WATCHDOG_ROLLBACK_OCCURRED,
type,
logPackage.getPackageName(),
logPackage.getVersionCode(),
rollbackReason,
failingPackageName,
new byte[]{});
} else {
// In the case that the log package is null, still log an empty string as an
// indication that retrieving the logging parent failed.
StatsLog.logWatchdogRollbackOccurred(type, "", 0, rollbackReason, failingPackageName);
FrameworkStatsLog.write(
FrameworkStatsLog.WATCHDOG_ROLLBACK_OCCURRED,
type,
"",
0,
rollbackReason,
failingPackageName,
new byte[]{});
}
}