Update StatsLogProcessor to handle BinaryPushStateChanged

Also changed StatsLog to call write() instead of the hard coded function
in StatsService.

Test: gts-tradefed run gts-dev --module GtsStatsdHostTestCases
Change-Id: I26171fa4cfc877e1e179b74ec8076d964aff8548
diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp
index 879b3c3..bde15a5 100644
--- a/cmds/statsd/src/StatsLogProcessor.cpp
+++ b/cmds/statsd/src/StatsLogProcessor.cpp
@@ -20,13 +20,17 @@
 #include "StatsLogProcessor.h"
 
 #include <android-base/file.h>
+#include <cutils/multiuser.h>
 #include <frameworks/base/cmds/statsd/src/active_config_list.pb.h>
+#include <frameworks/base/cmds/statsd/src/experiment_ids.pb.h>
 
 #include "android-base/stringprintf.h"
 #include "atoms_info.h"
 #include "external/StatsPullerManager.h"
 #include "guardrail/StatsdStats.h"
+#include "logd/LogEvent.h"
 #include "metrics/CountMetricProducer.h"
+#include "StatsService.h"
 #include "state/StateManager.h"
 #include "stats_log_util.h"
 #include "stats_util.h"
@@ -68,6 +72,10 @@
 // for ActiveConfigList
 const int FIELD_ID_ACTIVE_CONFIG_LIST_CONFIG = 1;
 
+// for permissions checks
+constexpr const char* kPermissionDump = "android.permission.DUMP";
+constexpr const char* kPermissionUsage = "android.permission.PACKAGE_USAGE_STATS";
+
 #define NS_PER_HOUR 3600 * NS_PER_SEC
 
 #define STATS_ACTIVE_METRIC_DIR "/data/misc/stats-active-metric"
@@ -181,6 +189,115 @@
     }
 }
 
+void StatsLogProcessor::onBinaryPushStateChangedEventLocked(LogEvent* event) {
+    pid_t pid = event->GetPid();
+    uid_t uid = event->GetUid();
+    if (!checkPermissionForIds(kPermissionDump, pid, uid) ||
+        !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));
+#ifdef NEW_ENCODING_SCHEME
+    std::vector<uint8_t> trainExperimentIdBytes =
+        event->GetStorage(7 /*experiment ids field id*/, &err4);
+#else
+    string trainExperimentIdString = event->GetString(7 /*experiment ids field id*/, &err4);
+#endif
+    if (err != NO_ERROR || err2 != NO_ERROR || err3 != NO_ERROR || err4 != NO_ERROR) {
+        ALOGE("Failed to parse fields in binary push state changed log event");
+        return;
+    }
+    ExperimentIds trainExperimentIds;
+#ifdef NEW_ENCODING_SCHEME
+    if (!trainExperimentIds.ParseFromArray(trainExperimentIdBytes.data(),
+                                           trainExperimentIdBytes.size())) {
+#else
+    if (!trainExperimentIds.ParseFromString(trainExperimentIdString)) {
+#endif
+        ALOGE("Failed to parse experimentids in binary push state changed.");
+        return;
+    }
+    vector<int64_t> experimentIdVector = {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);
+
+    std::vector<uint8_t> trainExperimentIdProto;
+    writeExperimentIdsToProto(experimentIdVector, &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);
+#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);
+}
+
+void StatsLogProcessor::getAndUpdateTrainInfoOnDisk(int32_t state,
+                                         int64_t* trainVersionCode,
+                                         string* trainName,
+                                         std::vector<int64_t>* experimentIds) {
+    bool readTrainInfoSuccess = false;
+    InstallTrainInfo trainInfoOnDisk;
+    readTrainInfoSuccess = StorageManager::readTrainInfo(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.
+            resetExperimentIds = true;
+        }
+
+        // Reset if we received a different experiment id.
+        if (!experimentIds->empty() &&
+                (trainInfoOnDisk.experimentIds.empty() ||
+                 experimentIds->at(0) != trainInfoOnDisk.experimentIds[0])) {
+            resetExperimentIds = true;
+        }
+    }
+
+    // Find the right experiment IDs
+    if (!resetExperimentIds && readTrainInfoSuccess) {
+        *experimentIds = trainInfoOnDisk.experimentIds;
+    }
+
+    if (!experimentIds->empty()) {
+        int64_t firstId = experimentIds->at(0);
+        switch (state) {
+            case android::util::BINARY_PUSH_STATE_CHANGED__STATE__INSTALL_SUCCESS:
+                experimentIds->push_back(firstId + 1);
+                break;
+            case android::util::BINARY_PUSH_STATE_CHANGED__STATE__INSTALLER_ROLLBACK_INITIATED:
+                experimentIds->push_back(firstId + 2);
+                break;
+            case android::util::BINARY_PUSH_STATE_CHANGED__STATE__INSTALLER_ROLLBACK_SUCCESS:
+                experimentIds->push_back(firstId + 3);
+                break;
+        }
+    }
+
+    StorageManager::writeTrainInfo(*trainVersionCode, *trainName, state, *experimentIds);
+}
+
+
 void StatsLogProcessor::resetConfigs() {
     std::lock_guard<std::mutex> lock(mMetricsMutex);
     resetConfigsLocked(getElapsedRealtimeNs());
@@ -201,6 +318,12 @@
 void StatsLogProcessor::OnLogEvent(LogEvent* event, int64_t elapsedRealtimeNs) {
     std::lock_guard<std::mutex> lock(mMetricsMutex);
 
+    // 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);
+    }
+
 #ifdef VERY_VERBOSE_PRINTING
     if (mPrintAllLogs) {
         ALOGI("%s", event->ToString().c_str());