Persist active metric status across system server

Previously, all metrics/configs would deactivate on system server death.
Now, active status is restored.

Bug: 129717537
Test: bit statsd_test:*
Test: libprotoutil_test:ProtoOutputStream*
Change-Id: Idf372457f60a931a2d00176a5eab58c534a25e41
diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp
index ec02b12..4e0a8eb 100644
--- a/cmds/statsd/src/StatsLogProcessor.cpp
+++ b/cmds/statsd/src/StatsLogProcessor.cpp
@@ -611,11 +611,8 @@
 
 void StatsLogProcessor::SaveActiveConfigsToDisk(int64_t currentTimeNs) {
     std::lock_guard<std::mutex> lock(mMetricsMutex);
-
     const int64_t timeNs = getElapsedRealtimeNs();
     // Do not write to disk if we already have in the last few seconds.
-    // This is to avoid overwriting files that would have the same name if we
-    //   write twice in the same second.
     if (static_cast<unsigned long long> (timeNs) <
             mLastActiveMetricsWriteNs + WRITE_DATA_COOL_DOWN_SEC * NS_PER_SEC) {
         ALOGI("Statsd skipping writing active metrics to disk. Already wrote data in last %d seconds",
@@ -625,13 +622,7 @@
     mLastActiveMetricsWriteNs = timeNs;
 
     ProtoOutputStream proto;
-    for (const auto& pair : mMetricsManagers) {
-        const sp<MetricsManager>& metricsManager = pair.second;
-        uint64_t configToken = proto.start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED |
-                                                     FIELD_ID_ACTIVE_CONFIG_LIST_CONFIG);
-        metricsManager->writeActiveConfigToProtoOutputStream(currentTimeNs, &proto);
-        proto.end(configToken);
-    }
+    WriteActiveConfigsToProtoOutputStreamLocked(currentTimeNs, DEVICE_SHUTDOWN, &proto);
 
     string file_name = StringPrintf("%s/active_metrics", STATS_ACTIVE_METRIC_DIR);
     StorageManager::deleteFile(file_name.c_str());
@@ -644,9 +635,24 @@
     proto.flush(fd.get());
 }
 
+void StatsLogProcessor::WriteActiveConfigsToProtoOutputStream(
+        int64_t currentTimeNs, const DumpReportReason reason, ProtoOutputStream* proto) {
+    std::lock_guard<std::mutex> lock(mMetricsMutex);
+    WriteActiveConfigsToProtoOutputStreamLocked(currentTimeNs, reason, proto);
+}
+
+void StatsLogProcessor::WriteActiveConfigsToProtoOutputStreamLocked(
+        int64_t currentTimeNs,  const DumpReportReason reason, ProtoOutputStream* proto) {
+    for (const auto& pair : mMetricsManagers) {
+        const sp<MetricsManager>& metricsManager = pair.second;
+        uint64_t configToken = proto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED |
+                                                     FIELD_ID_ACTIVE_CONFIG_LIST_CONFIG);
+        metricsManager->writeActiveConfigToProtoOutputStream(currentTimeNs, reason, proto);
+        proto->end(configToken);
+    }
+}
 void StatsLogProcessor::LoadActiveConfigsFromDisk() {
     std::lock_guard<std::mutex> lock(mMetricsMutex);
-
     string file_name = StringPrintf("%s/active_metrics", STATS_ACTIVE_METRIC_DIR);
     int fd = open(file_name.c_str(), O_RDONLY | O_CLOEXEC);
     if (-1 == fd) {
@@ -670,6 +676,19 @@
         StorageManager::deleteFile(file_name.c_str());
         return;
     }
+    // Passing in mTimeBaseNs only works as long as we only load from disk is when statsd starts.
+    SetConfigsActiveStateLocked(activeConfigList, mTimeBaseNs);
+    StorageManager::deleteFile(file_name.c_str());
+}
+
+void StatsLogProcessor::SetConfigsActiveState(const ActiveConfigList& activeConfigList,
+                                                    int64_t currentTimeNs) {
+    std::lock_guard<std::mutex> lock(mMetricsMutex);
+    SetConfigsActiveStateLocked(activeConfigList, currentTimeNs);
+}
+
+void StatsLogProcessor::SetConfigsActiveStateLocked(const ActiveConfigList& activeConfigList,
+                                                    int64_t currentTimeNs) {
     for (int i = 0; i < activeConfigList.config_size(); i++) {
         const auto& config = activeConfigList.config(i);
         ConfigKey key(config.uid(), config.id());
@@ -679,11 +698,9 @@
             continue;
         }
         VLOG("Setting active config %s", key.ToString().c_str());
-        it->second->loadActiveConfig(config, mTimeBaseNs);
+        it->second->loadActiveConfig(config, currentTimeNs);
     }
     VLOG("Successfully loaded %d active configs.", activeConfigList.config_size());
-
-    StorageManager::deleteFile(file_name.c_str());
 }
 
 void StatsLogProcessor::WriteDataToDiskLocked(const DumpReportReason dumpReportReason,
diff --git a/cmds/statsd/src/StatsLogProcessor.h b/cmds/statsd/src/StatsLogProcessor.h
index 0dc597b..92aa425 100644
--- a/cmds/statsd/src/StatsLogProcessor.h
+++ b/cmds/statsd/src/StatsLogProcessor.h
@@ -31,17 +31,6 @@
 namespace os {
 namespace statsd {
 
-// Keep this in sync with DumpReportReason enum in stats_log.proto
-enum DumpReportReason {
-    DEVICE_SHUTDOWN = 1,
-    CONFIG_UPDATED = 2,
-    CONFIG_REMOVED = 3,
-    GET_DATA_CALLED = 4,
-    ADB_DUMP = 5,
-    CONFIG_RESET = 6,
-    STATSCOMPANION_DIED = 7,
-    TERMINATION_SIGNAL_RECEIVED = 8
-};
 
 class StatsLogProcessor : public ConfigListener {
 public:
@@ -92,9 +81,16 @@
     /* Persist configs containing metrics with active activations to disk. */
     void SaveActiveConfigsToDisk(int64_t currentTimeNs);
 
+    /* Writes the current active status/ttl for all configs and metrics to ProtoOutputStream. */
+    void WriteActiveConfigsToProtoOutputStream(
+            int64_t currentTimeNs, const DumpReportReason reason, ProtoOutputStream* proto);
+
     /* Load configs containing metrics with active activations from disk. */
     void LoadActiveConfigsFromDisk();
 
+    /* Sets the active status/ttl for all configs and metrics to the status in ActiveConfigList. */
+    void SetConfigsActiveState(const ActiveConfigList& activeConfigList, int64_t currentTimeNs);
+
     // Reset all configs.
     void resetConfigs();
 
@@ -158,6 +154,12 @@
 
     void GetActiveConfigsLocked(const int uid, vector<int64_t>& outActiveConfigs);
 
+    void WriteActiveConfigsToProtoOutputStreamLocked(
+            int64_t currentTimeNs, const DumpReportReason reason, ProtoOutputStream* proto);
+
+    void SetConfigsActiveStateLocked(const ActiveConfigList& activeConfigList,
+                                     int64_t currentTimeNs);
+
     void WriteDataToDiskLocked(const DumpReportReason dumpReportReason,
                                const DumpLatency dumpLatency);
     void WriteDataToDiskLocked(const ConfigKey& key, const int64_t timestampNs,
@@ -224,6 +226,7 @@
     FRIEND_TEST(StatsLogProcessorTest, TestActivationOnBootMultipleActivations);
     FRIEND_TEST(StatsLogProcessorTest,
             TestActivationOnBootMultipleActivationsDifferentActivationTypes);
+    FRIEND_TEST(StatsLogProcessorTest, TestActivationsPersistAcrossSystemServerRestart);
 
     FRIEND_TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForSumDuration1);
     FRIEND_TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForSumDuration2);
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index 623a1f2..8191d37 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -1478,8 +1478,21 @@
     StatsdStats::getInstance().noteSystemServerRestart(getWallClockSec());
     if (mProcessor != nullptr) {
         ALOGW("Reset statsd upon system server restarts.");
+        int64_t systemServerRestartNs = getElapsedRealtimeNs();
+        ProtoOutputStream proto;
+        mProcessor->WriteActiveConfigsToProtoOutputStream(systemServerRestartNs,
+                STATSCOMPANION_DIED, &proto);
+
         mProcessor->WriteDataToDisk(STATSCOMPANION_DIED, FAST);
         mProcessor->resetConfigs();
+
+        std::string serializedActiveConfigs;
+        if (proto.serializeToString(&serializedActiveConfigs)) {
+            ActiveConfigList activeConfigs;
+            if (activeConfigs.ParseFromString(serializedActiveConfigs)) {
+                mProcessor->SetConfigsActiveState(activeConfigs, systemServerRestartNs);
+            }
+        }
     }
     mAnomalyAlarmMonitor->setStatsCompanionService(nullptr);
     mPeriodicAlarmMonitor->setStatsCompanionService(nullptr);
diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h
index 936f7db..a4e6d7f 100644
--- a/cmds/statsd/src/StatsService.h
+++ b/cmds/statsd/src/StatsService.h
@@ -427,6 +427,7 @@
 
     std::shared_ptr<LogEventQueue> mEventQueue;
 
+    FRIEND_TEST(StatsLogProcessorTest, TestActivationsPersistAcrossSystemServerRestart);
     FRIEND_TEST(StatsServiceTest, TestAddConfig_simple);
     FRIEND_TEST(StatsServiceTest, TestAddConfig_empty);
     FRIEND_TEST(StatsServiceTest, TestAddConfig_invalid);
diff --git a/cmds/statsd/src/active_config_list.proto b/cmds/statsd/src/active_config_list.proto
index ef8e50b..9929833 100644
--- a/cmds/statsd/src/active_config_list.proto
+++ b/cmds/statsd/src/active_config_list.proto
@@ -26,7 +26,18 @@
 
     // Time left in activation. When this proto is loaded after device boot,
     // the activation should be set to active for this duration.
+    // This field will only be set when the state is ACTIVE
     optional int64 remaining_ttl_nanos = 2;
+
+    enum State {
+        UNNKNOWN = 0;
+        // This metric should activate for remaining_ttl_nanos when we load the activations.
+        ACTIVE = 1;
+        // When we load the activations, this metric should activate on next boot for the tll
+        // specified in the config.
+        ACTIVATE_ON_BOOT = 2;
+    }
+    optional State state = 3;
 }
 
 message ActiveMetric {
diff --git a/cmds/statsd/src/metrics/MetricProducer.cpp b/cmds/statsd/src/metrics/MetricProducer.cpp
index 9ad7f09..d913427 100644
--- a/cmds/statsd/src/metrics/MetricProducer.cpp
+++ b/cmds/statsd/src/metrics/MetricProducer.cpp
@@ -19,6 +19,7 @@
 #include "MetricProducer.h"
 
 using android::util::FIELD_COUNT_REPEATED;
+using android::util::FIELD_TYPE_ENUM;
 using android::util::FIELD_TYPE_INT32;
 using android::util::FIELD_TYPE_INT64;
 using android::util::FIELD_TYPE_MESSAGE;
@@ -37,6 +38,7 @@
 // for ActiveEventActivation
 const int FIELD_ID_ACTIVE_EVENT_ACTIVATION_ATOM_MATCHER_INDEX = 1;
 const int FIELD_ID_ACTIVE_EVENT_ACTIVATION_REMAINING_TTL_NANOS = 2;
+const int FIELD_ID_ACTIVE_EVENT_ACTIVATION_STATE = 3;
 
 void MetricProducer::onMatchedLogEventLocked(const size_t matcherIndex, const LogEvent& event) {
     if (!mIsActive) {
@@ -165,17 +167,21 @@
             continue;
         }
         auto& activation = it->second;
-        // We don't want to change the ttl for future activations, so we set the start_ns
-        // such that start_ns + ttl_ns == currentTimeNs + remaining_ttl_nanos
-        activation->start_ns =
-            currentTimeNs + activeEventActivation.remaining_ttl_nanos() - activation->ttl_ns;
-        activation->state = ActivationState::kActive;
-        mIsActive = true;
+        if (activeEventActivation.state() == ActiveEventActivation::ACTIVE) {
+            // We don't want to change the ttl for future activations, so we set the start_ns
+            // such that start_ns + ttl_ns == currentTimeNs + remaining_ttl_nanos
+            activation->start_ns =
+                currentTimeNs + activeEventActivation.remaining_ttl_nanos() - activation->ttl_ns;
+            activation->state = ActivationState::kActive;
+            mIsActive = true;
+        } else if (activeEventActivation.state() == ActiveEventActivation::ACTIVATE_ON_BOOT) {
+            activation->state = ActivationState::kActiveOnBoot;
+        }
     }
 }
 
 void MetricProducer::writeActiveMetricToProtoOutputStream(
-        int64_t currentTimeNs, ProtoOutputStream* proto) {
+        int64_t currentTimeNs, const DumpReportReason reason, ProtoOutputStream* proto) {
     proto->write(FIELD_TYPE_INT64 | FIELD_ID_ACTIVE_METRIC_ID, (long long)mMetricId);
     for (auto& it : mEventActivationMap) {
         const int atom_matcher_index = it.first;
@@ -196,9 +202,22 @@
                     activation->start_ns + activation->ttl_ns - currentTimeNs;
             proto->write(FIELD_TYPE_INT64 | FIELD_ID_ACTIVE_EVENT_ACTIVATION_REMAINING_TTL_NANOS,
                     (long long)remainingTtlNs);
+            proto->write(FIELD_TYPE_ENUM | FIELD_ID_ACTIVE_EVENT_ACTIVATION_STATE,
+                    ActiveEventActivation::ACTIVE);
+
         } else if (ActivationState::kActiveOnBoot == activation->state) {
-            proto->write(FIELD_TYPE_INT64 | FIELD_ID_ACTIVE_EVENT_ACTIVATION_REMAINING_TTL_NANOS,
-                    (long long)activation->ttl_ns);
+            if (reason == DEVICE_SHUTDOWN || reason == TERMINATION_SIGNAL_RECEIVED) {
+                proto->write(
+                        FIELD_TYPE_INT64 | FIELD_ID_ACTIVE_EVENT_ACTIVATION_REMAINING_TTL_NANOS,
+                        (long long)activation->ttl_ns);
+                proto->write(FIELD_TYPE_ENUM | FIELD_ID_ACTIVE_EVENT_ACTIVATION_STATE,
+                                    ActiveEventActivation::ACTIVE);
+            } else if (reason == STATSCOMPANION_DIED) {
+                // We are saving because of system server death, not due to a device shutdown.
+                // Next time we load, we do not want to activate metrics that activate on boot.
+                proto->write(FIELD_TYPE_ENUM | FIELD_ID_ACTIVE_EVENT_ACTIVATION_STATE,
+                                                    ActiveEventActivation::ACTIVATE_ON_BOOT);
+            }
         }
         proto->end(activationToken);
     }
diff --git a/cmds/statsd/src/metrics/MetricProducer.h b/cmds/statsd/src/metrics/MetricProducer.h
index ec3484c..3ddbef4 100644
--- a/cmds/statsd/src/metrics/MetricProducer.h
+++ b/cmds/statsd/src/metrics/MetricProducer.h
@@ -35,6 +35,18 @@
 namespace os {
 namespace statsd {
 
+// Keep this in sync with DumpReportReason enum in stats_log.proto
+enum DumpReportReason {
+    DEVICE_SHUTDOWN = 1,
+    CONFIG_UPDATED = 2,
+    CONFIG_REMOVED = 3,
+    GET_DATA_CALLED = 4,
+    ADB_DUMP = 5,
+    CONFIG_RESET = 6,
+    STATSCOMPANION_DIED = 7,
+    TERMINATION_SIGNAL_RECEIVED = 8
+};
+
 // If the metric has no activation requirement, it will be active once the metric producer is
 // created.
 // If the metric needs to be activated by atoms, the metric producer will start
@@ -244,7 +256,7 @@
     void flushIfExpire(int64_t elapsedTimestampNs);
 
     void writeActiveMetricToProtoOutputStream(
-            int64_t currentTimeNs, ProtoOutputStream* proto);
+            int64_t currentTimeNs, const DumpReportReason reason, ProtoOutputStream* proto);
 protected:
     virtual void onConditionChangedLocked(const bool condition, const int64_t eventTime) = 0;
     virtual void onSlicedConditionMayChangeLocked(bool overallCondition,
@@ -268,8 +280,6 @@
         return mIsActive;
     }
 
-    void prepActiveForBootIfNecessaryLocked(int64_t currentTimeNs);
-
     void loadActiveMetricLocked(const ActiveMetric& activeMetric, int64_t currentTimeNs);
 
     virtual void prepareFirstBucketLocked() {};
@@ -412,6 +422,7 @@
     FRIEND_TEST(StatsLogProcessorTest, TestActivationOnBootMultipleActivations);
     FRIEND_TEST(StatsLogProcessorTest,
             TestActivationOnBootMultipleActivationsDifferentActivationTypes);
+    FRIEND_TEST(StatsLogProcessorTest, TestActivationsPersistAcrossSystemServerRestart);
 };
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/metrics/MetricsManager.cpp b/cmds/statsd/src/metrics/MetricsManager.cpp
index 947f377..207a7dd 100644
--- a/cmds/statsd/src/metrics/MetricsManager.cpp
+++ b/cmds/statsd/src/metrics/MetricsManager.cpp
@@ -528,14 +528,14 @@
 }
 
 void MetricsManager::writeActiveConfigToProtoOutputStream(
-        int64_t currentTimeNs, ProtoOutputStream* proto) {
+        int64_t currentTimeNs, const DumpReportReason reason, ProtoOutputStream* proto) {
     proto->write(FIELD_TYPE_INT64 | FIELD_ID_ACTIVE_CONFIG_ID, (long long)mConfigKey.GetId());
     proto->write(FIELD_TYPE_INT32 | FIELD_ID_ACTIVE_CONFIG_UID, mConfigKey.GetUid());
     for (int metricIndex : mMetricIndexesWithActivation) {
         const auto& metric = mAllMetricProducers[metricIndex];
         const uint64_t metricToken = proto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED |
                 FIELD_ID_ACTIVE_CONFIG_METRIC);
-        metric->writeActiveMetricToProtoOutputStream(currentTimeNs, proto);
+        metric->writeActiveMetricToProtoOutputStream(currentTimeNs, reason, proto);
         proto->end(metricToken);
     }
 }
diff --git a/cmds/statsd/src/metrics/MetricsManager.h b/cmds/statsd/src/metrics/MetricsManager.h
index 818131e..da3be06 100644
--- a/cmds/statsd/src/metrics/MetricsManager.h
+++ b/cmds/statsd/src/metrics/MetricsManager.h
@@ -141,7 +141,7 @@
     void loadActiveConfig(const ActiveConfig& config, int64_t currentTimeNs);
 
     void writeActiveConfigToProtoOutputStream(
-            int64_t currentTimeNs, ProtoOutputStream* proto);
+            int64_t currentTimeNs, const DumpReportReason reason, ProtoOutputStream* proto);
 
 private:
     // For test only.
@@ -290,6 +290,7 @@
     FRIEND_TEST(StatsLogProcessorTest, TestActivationOnBootMultipleActivations);
     FRIEND_TEST(StatsLogProcessorTest,
             TestActivationOnBootMultipleActivationsDifferentActivationTypes);
+    FRIEND_TEST(StatsLogProcessorTest, TestActivationsPersistAcrossSystemServerRestart);
 };
 
 }  // namespace statsd