Add support for multi train logging
Test: atest GtsStatsdHostTestCases
Change-Id: I4ca7089347d6dfab8a33c07acd5ef9c252413ec9
diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp
index bde15a5..6e7f081 100644
--- a/cmds/statsd/src/StatsLogProcessor.cpp
+++ b/cmds/statsd/src/StatsLogProcessor.cpp
@@ -196,17 +196,27 @@
!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 @@
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 @@
// 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