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
diff --git a/cmds/statsd/tests/StatsLogProcessor_test.cpp b/cmds/statsd/tests/StatsLogProcessor_test.cpp
index 49b4e90..fe25a25 100644
--- a/cmds/statsd/tests/StatsLogProcessor_test.cpp
+++ b/cmds/statsd/tests/StatsLogProcessor_test.cpp
@@ -13,12 +13,14 @@
 // limitations under the License.
 
 #include "StatsLogProcessor.h"
+#include "StatsService.h"
 #include "config/ConfigKey.h"
 #include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
 #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
 #include "guardrail/StatsdStats.h"
 #include "logd/LogEvent.h"
 #include "packages/UidMap.h"
+#include "storage/StorageManager.h"
 #include "statslog.h"
 
 #include <gmock/gmock.h>
@@ -97,7 +99,8 @@
     ConfigKey key(100, 12345);
     EXPECT_CALL(mockMetricsManager, byteSize())
             .Times(1)
-            .WillRepeatedly(Return(int(StatsdStats::kMaxMetricsBytesPerConfig * .95)));
+            .WillRepeatedly(::testing::Return(int(
+                    StatsdStats::kMaxMetricsBytesPerConfig * .95)));
 
     // Expect only one broadcast despite always returning a size that should trigger broadcast.
     p.flushIfNecessaryLocked(1, key, mockMetricsManager);
@@ -128,7 +131,7 @@
     ConfigKey key(100, 12345);
     EXPECT_CALL(mockMetricsManager, byteSize())
             .Times(1)
-            .WillRepeatedly(Return(int(StatsdStats::kMaxMetricsBytesPerConfig * 1.2)));
+            .WillRepeatedly(::testing::Return(int(StatsdStats::kMaxMetricsBytesPerConfig * 1.2)));
 
     EXPECT_CALL(mockMetricsManager, dropData(_)).Times(1);
 
@@ -1482,6 +1485,236 @@
     // }}}---------------------------------------------------------------------------
 }
 
+TEST(StatsLogProcessorTest, TestActivationsPersistAcrossSystemServerRestart) {
+    int uid = 9876;
+    long configId = 12341;
+
+    // Create config with 3 metrics:
+    // Metric 1: Activate on 2 activations, 1 on boot, 1 immediate.
+    // Metric 2: Activate on 2 activations, 1 on boot, 1 immediate.
+    // Metric 3: Always active
+    StatsdConfig config1;
+    config1.set_id(configId);
+    config1.add_allowed_log_source("AID_ROOT");  // LogEvent defaults to UID of root.
+    auto wakelockAcquireMatcher = CreateAcquireWakelockAtomMatcher();
+    auto screenOnMatcher = CreateScreenTurnedOnAtomMatcher();
+    auto jobStartMatcher = CreateStartScheduledJobAtomMatcher();
+    auto jobFinishMatcher = CreateFinishScheduledJobAtomMatcher();
+    *config1.add_atom_matcher() = wakelockAcquireMatcher;
+    *config1.add_atom_matcher() = screenOnMatcher;
+    *config1.add_atom_matcher() = jobStartMatcher;
+    *config1.add_atom_matcher() = jobFinishMatcher;
+
+    long metricId1 = 1234561;
+    long metricId2 = 1234562;
+    long metricId3 = 1234563;
+
+    auto countMetric1 = config1.add_count_metric();
+    countMetric1->set_id(metricId1);
+    countMetric1->set_what(wakelockAcquireMatcher.id());
+    countMetric1->set_bucket(FIVE_MINUTES);
+
+    auto countMetric2 = config1.add_count_metric();
+    countMetric2->set_id(metricId2);
+    countMetric2->set_what(wakelockAcquireMatcher.id());
+    countMetric2->set_bucket(FIVE_MINUTES);
+
+    auto countMetric3 = config1.add_count_metric();
+    countMetric3->set_id(metricId3);
+    countMetric3->set_what(wakelockAcquireMatcher.id());
+    countMetric3->set_bucket(FIVE_MINUTES);
+
+    // Metric 1 activates on boot for wakelock acquire, immediately for screen on.
+    auto metric1Activation = config1.add_metric_activation();
+    metric1Activation->set_metric_id(metricId1);
+    auto metric1ActivationTrigger1 = metric1Activation->add_event_activation();
+    metric1ActivationTrigger1->set_atom_matcher_id(wakelockAcquireMatcher.id());
+    metric1ActivationTrigger1->set_ttl_seconds(100);
+    metric1ActivationTrigger1->set_activation_type(ACTIVATE_ON_BOOT);
+    auto metric1ActivationTrigger2 = metric1Activation->add_event_activation();
+    metric1ActivationTrigger2->set_atom_matcher_id(screenOnMatcher.id());
+    metric1ActivationTrigger2->set_ttl_seconds(200);
+    metric1ActivationTrigger2->set_activation_type(ACTIVATE_IMMEDIATELY);
+
+    // Metric 2 activates on boot for scheduled job start, immediately for scheduled job finish.
+    auto metric2Activation = config1.add_metric_activation();
+    metric2Activation->set_metric_id(metricId2);
+    auto metric2ActivationTrigger1 = metric2Activation->add_event_activation();
+    metric2ActivationTrigger1->set_atom_matcher_id(jobStartMatcher.id());
+    metric2ActivationTrigger1->set_ttl_seconds(100);
+    metric2ActivationTrigger1->set_activation_type(ACTIVATE_ON_BOOT);
+    auto metric2ActivationTrigger2 = metric2Activation->add_event_activation();
+    metric2ActivationTrigger2->set_atom_matcher_id(jobFinishMatcher.id());
+    metric2ActivationTrigger2->set_ttl_seconds(200);
+    metric2ActivationTrigger2->set_activation_type(ACTIVATE_IMMEDIATELY);
+
+    // Send the config.
+    StatsService service(nullptr, nullptr);
+    string serialized = config1.SerializeAsString();
+    service.addConfigurationChecked(uid, configId, {serialized.begin(), serialized.end()});
+
+    // Make sure the config is stored on disk. Otherwise, we will not reset on system server death.
+    StatsdConfig tmpConfig;
+    ConfigKey cfgKey1(uid, configId);
+    EXPECT_TRUE(StorageManager::readConfigFromDisk(cfgKey1, &tmpConfig));
+
+    // Metric 1 is not active.
+    // Metric 2 is not active.
+    // Metric 3 is active.
+    // {{{---------------------------------------------------------------------------
+    sp<StatsLogProcessor> processor = service.mProcessor;
+    EXPECT_EQ(1, processor->mMetricsManagers.size());
+    auto it = processor->mMetricsManagers.find(cfgKey1);
+    EXPECT_TRUE(it != processor->mMetricsManagers.end());
+    auto& metricsManager1 = it->second;
+    EXPECT_TRUE(metricsManager1->isActive());
+    EXPECT_EQ(3, metricsManager1->mAllMetricProducers.size());
+
+    auto& metricProducer1 = metricsManager1->mAllMetricProducers[0];
+    EXPECT_EQ(metricId1, metricProducer1->getMetricId());
+    EXPECT_FALSE(metricProducer1->isActive());
+
+    auto& metricProducer2 = metricsManager1->mAllMetricProducers[1];
+    EXPECT_EQ(metricId2, metricProducer2->getMetricId());
+    EXPECT_FALSE(metricProducer2->isActive());
+
+    auto& metricProducer3 = metricsManager1->mAllMetricProducers[2];
+    EXPECT_EQ(metricId3, metricProducer3->getMetricId());
+    EXPECT_TRUE(metricProducer3->isActive());
+
+    // Check event activations.
+    EXPECT_EQ(metricsManager1->mAllAtomMatchers.size(), 4);
+    EXPECT_EQ(metricsManager1->mAllAtomMatchers[0]->getId(),
+              metric1ActivationTrigger1->atom_matcher_id());
+    const auto& activation1 = metricProducer1->mEventActivationMap.at(0);
+    EXPECT_EQ(100 * NS_PER_SEC, activation1->ttl_ns);
+    EXPECT_EQ(0, activation1->start_ns);
+    EXPECT_EQ(kNotActive, activation1->state);
+    EXPECT_EQ(ACTIVATE_ON_BOOT, activation1->activationType);
+
+    EXPECT_EQ(metricsManager1->mAllAtomMatchers[1]->getId(),
+              metric1ActivationTrigger2->atom_matcher_id());
+    const auto& activation2 = metricProducer1->mEventActivationMap.at(1);
+    EXPECT_EQ(200 * NS_PER_SEC, activation2->ttl_ns);
+    EXPECT_EQ(0, activation2->start_ns);
+    EXPECT_EQ(kNotActive, activation2->state);
+    EXPECT_EQ(ACTIVATE_IMMEDIATELY, activation2->activationType);
+
+    EXPECT_EQ(metricsManager1->mAllAtomMatchers[2]->getId(),
+              metric2ActivationTrigger1->atom_matcher_id());
+    const auto& activation3 = metricProducer2->mEventActivationMap.at(2);
+    EXPECT_EQ(100 * NS_PER_SEC, activation3->ttl_ns);
+    EXPECT_EQ(0, activation3->start_ns);
+    EXPECT_EQ(kNotActive, activation3->state);
+    EXPECT_EQ(ACTIVATE_ON_BOOT, activation3->activationType);
+
+    EXPECT_EQ(metricsManager1->mAllAtomMatchers[3]->getId(),
+              metric2ActivationTrigger2->atom_matcher_id());
+    const auto& activation4 = metricProducer2->mEventActivationMap.at(3);
+    EXPECT_EQ(200 * NS_PER_SEC, activation4->ttl_ns);
+    EXPECT_EQ(0, activation4->start_ns);
+    EXPECT_EQ(kNotActive, activation4->state);
+    EXPECT_EQ(ACTIVATE_IMMEDIATELY, activation4->activationType);
+    // }}}------------------------------------------------------------------------------
+
+    // Trigger Activation 1 for Metric 1. Should activate on boot.
+    // Trigger Activation 4 for Metric 2. Should activate immediately.
+    long configAddedTimeNs = metricsManager1->mLastReportTimeNs;
+    std::vector<AttributionNodeInternal> attributions1 = {CreateAttribution(111, "App1")};
+    auto event = CreateAcquireWakelockEvent(attributions1, "wl1", 1 + configAddedTimeNs);
+    processor->OnLogEvent(event.get());
+
+    event = CreateFinishScheduledJobEvent(attributions1, "finish1", 2 + configAddedTimeNs);
+    processor->OnLogEvent(event.get());
+
+    // Metric 1 is not active; Activation 1 set to kActiveOnBoot
+    // Metric 2 is active. Activation 4 set to kActive
+    // Metric 3 is active.
+    // {{{---------------------------------------------------------------------------
+    EXPECT_FALSE(metricProducer1->isActive());
+    EXPECT_EQ(0, activation1->start_ns);
+    EXPECT_EQ(kActiveOnBoot, activation1->state);
+    EXPECT_EQ(0, activation2->start_ns);
+    EXPECT_EQ(kNotActive, activation2->state);
+
+    EXPECT_TRUE(metricProducer2->isActive());
+    EXPECT_EQ(0, activation3->start_ns);
+    EXPECT_EQ(kNotActive, activation3->state);
+    EXPECT_EQ(2 + configAddedTimeNs, activation4->start_ns);
+    EXPECT_EQ(kActive, activation4->state);
+
+    EXPECT_TRUE(metricProducer3->isActive());
+    // }}}-----------------------------------------------------------------------------
+
+    // Can't fake time with StatsService.
+    // Lets get a time close to the system server death time and make sure it's sane.
+    int64_t approximateSystemServerDeath = getElapsedRealtimeNs();
+    EXPECT_TRUE(approximateSystemServerDeath > 2 + configAddedTimeNs);
+    EXPECT_TRUE(approximateSystemServerDeath < NS_PER_SEC + configAddedTimeNs);
+
+    // System server dies.
+    service.binderDied(nullptr);
+
+    // We should have a new metrics manager. Lets get it and ensure activation status is restored.
+    // {{{---------------------------------------------------------------------------
+    EXPECT_EQ(1, processor->mMetricsManagers.size());
+    it = processor->mMetricsManagers.find(cfgKey1);
+    EXPECT_TRUE(it != processor->mMetricsManagers.end());
+    auto& metricsManager2 = it->second;
+    EXPECT_TRUE(metricsManager2->isActive());
+    EXPECT_EQ(3, metricsManager2->mAllMetricProducers.size());
+
+    auto& metricProducer1001 = metricsManager2->mAllMetricProducers[0];
+    EXPECT_EQ(metricId1, metricProducer1001->getMetricId());
+    EXPECT_FALSE(metricProducer1001->isActive());
+
+    auto& metricProducer1002 = metricsManager2->mAllMetricProducers[1];
+    EXPECT_EQ(metricId2, metricProducer1002->getMetricId());
+    EXPECT_TRUE(metricProducer1002->isActive());
+
+    auto& metricProducer1003 = metricsManager2->mAllMetricProducers[2];
+    EXPECT_EQ(metricId3, metricProducer1003->getMetricId());
+    EXPECT_TRUE(metricProducer1003->isActive());
+
+    // Check event activations.
+    // Activation 1 is kActiveOnBoot.
+    // Activation 2 and 3 are not active.
+    // Activation 4 is active.
+    EXPECT_EQ(metricsManager2->mAllAtomMatchers.size(), 4);
+    EXPECT_EQ(metricsManager2->mAllAtomMatchers[0]->getId(),
+              metric1ActivationTrigger1->atom_matcher_id());
+    const auto& activation1001 = metricProducer1001->mEventActivationMap.at(0);
+    EXPECT_EQ(100 * NS_PER_SEC, activation1001->ttl_ns);
+    EXPECT_EQ(0, activation1001->start_ns);
+    EXPECT_EQ(kActiveOnBoot, activation1001->state);
+    EXPECT_EQ(ACTIVATE_ON_BOOT, activation1001->activationType);
+
+    EXPECT_EQ(metricsManager2->mAllAtomMatchers[1]->getId(),
+              metric1ActivationTrigger2->atom_matcher_id());
+    const auto& activation1002 = metricProducer1001->mEventActivationMap.at(1);
+    EXPECT_EQ(200 * NS_PER_SEC, activation1002->ttl_ns);
+    EXPECT_EQ(0, activation1002->start_ns);
+    EXPECT_EQ(kNotActive, activation1002->state);
+    EXPECT_EQ(ACTIVATE_IMMEDIATELY, activation1002->activationType);
+
+    EXPECT_EQ(metricsManager2->mAllAtomMatchers[2]->getId(),
+              metric2ActivationTrigger1->atom_matcher_id());
+    const auto& activation1003 = metricProducer1002->mEventActivationMap.at(2);
+    EXPECT_EQ(100 * NS_PER_SEC, activation1003->ttl_ns);
+    EXPECT_EQ(0, activation1003->start_ns);
+    EXPECT_EQ(kNotActive, activation1003->state);
+    EXPECT_EQ(ACTIVATE_ON_BOOT, activation1003->activationType);
+
+    EXPECT_EQ(metricsManager2->mAllAtomMatchers[3]->getId(),
+              metric2ActivationTrigger2->atom_matcher_id());
+    const auto& activation1004 = metricProducer1002->mEventActivationMap.at(3);
+    EXPECT_EQ(200 * NS_PER_SEC, activation1004->ttl_ns);
+    EXPECT_EQ(2 + configAddedTimeNs, activation1004->start_ns);
+    EXPECT_EQ(kActive, activation1004->state);
+    EXPECT_EQ(ACTIVATE_IMMEDIATELY, activation1004->activationType);
+    // }}}------------------------------------------------------------------------------
+}
+
 #else
 GTEST_LOG_(INFO) << "This test does nothing.\n";
 #endif
diff --git a/libs/protoutil/include/android/util/ProtoOutputStream.h b/libs/protoutil/include/android/util/ProtoOutputStream.h
index 360e8d3..60d0318 100644
--- a/libs/protoutil/include/android/util/ProtoOutputStream.h
+++ b/libs/protoutil/include/android/util/ProtoOutputStream.h
@@ -123,6 +123,7 @@
     size_t size(); // Get the size of the serialized protobuf.
     sp<ProtoReader> data(); // Get the reader apis of the data.
     bool flush(int fd); // Flush data directly to a file descriptor.
+    bool serializeToString(std::string* out); // Serializes the proto to a string.
 
     /**
      * Clears the ProtoOutputStream so the buffer can be reused instead of deallocation/allocation again.
diff --git a/libs/protoutil/src/ProtoOutputStream.cpp b/libs/protoutil/src/ProtoOutputStream.cpp
index ccbb83b..98a68c6 100644
--- a/libs/protoutil/src/ProtoOutputStream.cpp
+++ b/libs/protoutil/src/ProtoOutputStream.cpp
@@ -448,6 +448,23 @@
     return true;
 }
 
+bool
+ProtoOutputStream::serializeToString(std::string* out)
+{
+    if (out == nullptr) return false;
+    if (!compact()) return false;
+
+
+    sp<ProtoReader> reader = mBuffer->read();
+    out->reserve(reader->size());
+    while (reader->hasNext()) {
+        out->append(static_cast<const char*>(static_cast<const void*>(reader->readBuffer())),
+                    reader->currentToRead());
+        reader->move(reader->currentToRead());
+    }
+    return true;
+}
+
 sp<ProtoReader>
 ProtoOutputStream::data()
 {
diff --git a/libs/protoutil/tests/ProtoOutputStream_test.cpp b/libs/protoutil/tests/ProtoOutputStream_test.cpp
index 9d357f3..6282fd5 100644
--- a/libs/protoutil/tests/ProtoOutputStream_test.cpp
+++ b/libs/protoutil/tests/ProtoOutputStream_test.cpp
@@ -88,6 +88,50 @@
     EXPECT_EQ(primitives.val_enum(), PrimitiveProto_Count_TWO);
 }
 
+TEST(ProtoOutputStreamTest, SerializeToStringPrimitives) {
+    std::string s = "hello";
+    const char b[5] = { 'a', 'p', 'p', 'l', 'e' };
+
+    ProtoOutputStream proto;
+    EXPECT_TRUE(proto.write(FIELD_TYPE_INT32 | PrimitiveProto::kValInt32FieldNumber, 123));
+    EXPECT_TRUE(proto.write(FIELD_TYPE_INT64 | PrimitiveProto::kValInt64FieldNumber, -1LL));
+    EXPECT_TRUE(proto.write(FIELD_TYPE_FLOAT | PrimitiveProto::kValFloatFieldNumber, -23.5f));
+    EXPECT_TRUE(proto.write(FIELD_TYPE_DOUBLE | PrimitiveProto::kValDoubleFieldNumber, 324.5));
+    EXPECT_TRUE(proto.write(FIELD_TYPE_UINT32 | PrimitiveProto::kValUint32FieldNumber, 3424));
+    EXPECT_TRUE(proto.write(FIELD_TYPE_UINT64 | PrimitiveProto::kValUint64FieldNumber, 57LL));
+    EXPECT_TRUE(proto.write(FIELD_TYPE_FIXED32 | PrimitiveProto::kValFixed32FieldNumber, -20));
+    EXPECT_TRUE(proto.write(FIELD_TYPE_FIXED64 | PrimitiveProto::kValFixed64FieldNumber, -37LL));
+    EXPECT_TRUE(proto.write(FIELD_TYPE_BOOL | PrimitiveProto::kValBoolFieldNumber, true));
+    EXPECT_TRUE(proto.write(FIELD_TYPE_STRING | PrimitiveProto::kValStringFieldNumber, s));
+    EXPECT_TRUE(proto.write(FIELD_TYPE_BYTES | PrimitiveProto::kValBytesFieldNumber, b, 5));
+    EXPECT_TRUE(proto.write(FIELD_TYPE_SFIXED32 | PrimitiveProto::kValSfixed32FieldNumber, 63));
+    EXPECT_TRUE(proto.write(FIELD_TYPE_SFIXED64 | PrimitiveProto::kValSfixed64FieldNumber, -54));
+    EXPECT_TRUE(proto.write(FIELD_TYPE_SINT32 | PrimitiveProto::kValSint32FieldNumber, -533));
+    EXPECT_TRUE(proto.write(FIELD_TYPE_SINT64 | PrimitiveProto::kValSint64FieldNumber, -61224762453LL));
+    EXPECT_TRUE(proto.write(FIELD_TYPE_ENUM | PrimitiveProto::kValEnumFieldNumber, 2));
+
+    PrimitiveProto primitives;
+    std::string serialized;
+    ASSERT_TRUE(proto.serializeToString(&serialized));
+    ASSERT_TRUE(primitives.ParseFromString(serialized));
+    EXPECT_EQ(primitives.val_int32(), 123);
+    EXPECT_EQ(primitives.val_int64(), -1);
+    EXPECT_EQ(primitives.val_float(), -23.5f);
+    EXPECT_EQ(primitives.val_double(), 324.5f);
+    EXPECT_EQ(primitives.val_uint32(), 3424);
+    EXPECT_EQ(primitives.val_uint64(), 57);
+    EXPECT_EQ(primitives.val_fixed32(), -20);
+    EXPECT_EQ(primitives.val_fixed64(), -37);
+    EXPECT_EQ(primitives.val_bool(), true);
+    EXPECT_THAT(primitives.val_string(), StrEq(s.c_str()));
+    EXPECT_THAT(primitives.val_bytes(), StrEq("apple"));
+    EXPECT_EQ(primitives.val_sfixed32(), 63);
+    EXPECT_EQ(primitives.val_sfixed64(), -54);
+    EXPECT_EQ(primitives.val_sint32(), -533);
+    EXPECT_EQ(primitives.val_sint64(), -61224762453LL);
+    EXPECT_EQ(primitives.val_enum(), PrimitiveProto_Count_TWO);
+}
+
 TEST(ProtoOutputStreamTest, Complex) {
     std::string name1 = "cat";
     std::string name2 = "dog";
@@ -127,6 +171,47 @@
     EXPECT_THAT(log2.data(), StrEq("food"));
 }
 
+TEST(ProtoOutputStreamTest, SerializeToStringComplex) {
+    std::string name1 = "cat";
+    std::string name2 = "dog";
+    const char data1[6] = { 'f', 'u', 'n', 'n', 'y', '!' };
+    const char data2[4] = { 'f', 'o', 'o', 'd' };
+
+    ProtoOutputStream proto;
+    EXPECT_TRUE(proto.write(FIELD_TYPE_INT32 | ComplexProto::kIntsFieldNumber, 23));
+    EXPECT_TRUE(proto.write(FIELD_TYPE_INT32 | ComplexProto::kIntsFieldNumber, 101));
+    EXPECT_TRUE(proto.write(FIELD_TYPE_INT32 | ComplexProto::kIntsFieldNumber, -72));
+    uint64_t token1 = proto.start(FIELD_TYPE_MESSAGE | ComplexProto::kLogsFieldNumber);
+    EXPECT_TRUE(proto.write(FIELD_TYPE_INT32 | ComplexProto::Log::kIdFieldNumber, 12));
+    EXPECT_TRUE(proto.write(FIELD_TYPE_STRING | ComplexProto::Log::kNameFieldNumber, name1));
+    // specify the length to test the write(id, bytes, length) function.
+    EXPECT_TRUE(proto.write(FIELD_TYPE_BYTES | ComplexProto::Log::kDataFieldNumber, data1, 5));
+    proto.end(token1);
+    uint64_t token2 = proto.start(FIELD_TYPE_MESSAGE | ComplexProto::kLogsFieldNumber);
+    EXPECT_TRUE(proto.write(FIELD_TYPE_INT32 | ComplexProto::Log::kIdFieldNumber, 98));
+    EXPECT_TRUE(proto.write(FIELD_TYPE_STRING | ComplexProto::Log::kNameFieldNumber, name2));
+    EXPECT_TRUE(proto.write(FIELD_TYPE_BYTES | ComplexProto::Log::kDataFieldNumber, data2, 4));
+    proto.end(token2);
+
+    ComplexProto complex;
+    std::string serialized;
+    ASSERT_TRUE(proto.serializeToString(&serialized));
+    ASSERT_TRUE(complex.ParseFromString(serialized));
+    EXPECT_EQ(complex.ints_size(), 3);
+    EXPECT_EQ(complex.ints(0), 23);
+    EXPECT_EQ(complex.ints(1), 101);
+    EXPECT_EQ(complex.ints(2), -72);
+    EXPECT_EQ(complex.logs_size(), 2);
+    ComplexProto::Log log1 = complex.logs(0);
+    EXPECT_EQ(log1.id(), 12);
+    EXPECT_THAT(log1.name(), StrEq(name1.c_str()));
+    EXPECT_THAT(log1.data(), StrEq("funny")); // should not contain '!'
+    ComplexProto::Log log2 = complex.logs(1);
+    EXPECT_EQ(log2.id(), 98);
+    EXPECT_THAT(log2.name(), StrEq(name2.c_str()));
+    EXPECT_THAT(log2.data(), StrEq("food"));
+}
+
 TEST(ProtoOutputStreamTest, Reusability) {
     ProtoOutputStream proto;
     EXPECT_TRUE(proto.write(FIELD_TYPE_INT32 | ComplexProto::kIntsFieldNumber, 32));